内存泄漏
1、内存泄露的本质
指当前对象在实际运行中超出了其本身意义上生命周期范围的,从而导致本该处于内存可回收状态的但实际上却一直处于不可回收状态的内存占用非正常现象。
2、常见的两种现象:
(1)异步回调中持有M,异步回调生命周期不可控,或者本身的生命周期长于Activity,导致内存泄漏。常见于Activity中handler、异步线程
(2)静态属性持有了M的强引用。常见于单例模式强引用外部的非静态对象。
3、具体导致内存泄漏的几个方面
(1)Context
单例模式中,引用的context尽量用非activity类型的context,优先使用Application的Context
(2)Handler
泄漏一:activity内部类handler,发送延迟消息。消息未发送,主线程持有message,message持有handler,handler是activity内部类。
private Handler handler = new Handler(Looper.getMainLooper()); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); handler.postDelayed(new Runnable() { @Override public void run() { } },20000); finish(); }
主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity
泄漏二:子线程不回收。子线程生命周期长于外部类
handler.post(new Runnable() { @Override public void run() { try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } test = "sss"; } }); finish();
Handler导致的一般是临时性泄漏,当任务执行完后,会被gc
(3)非静态内部类
非静态内部类默认持有外部类的引用,会导致内存泄漏。前提是内部类的生命周期要比外部类的生命周期长。
如下不会发生内存泄漏,doSomething生命周期短于外部类
public class MainActivity2 extends AppCompatActivity { private MessageCall messageCall; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); messageCall = new MessageCall(); findViewById(R.id.doSomething).setOnClickListener(view -> { messageCall.doSomething(); }); } class MessageCall { public void doSomething(){ System.out.println("doSomething"); } } }
改造messagecall,加入耗时方法。当runable未执行完,关闭activity,则会导致内存泄漏
class MessageCall { public void doSomething(){ Runnable work = new Runnable() { @Override public void run() { System.out.println("doSomething start"); SystemClock.sleep(20000); System.out.println("doSomething end"); } }; new Thread(work).start(); } }
(4)Activity动态广播
finish、destory时取消广播。因为activity中动态注册的broadcastreceive,通过AMS强应用。
(5)Activity
Activity中尽量不要使用非静态内部类,可以使用静态内部类和WeakReference代替
4、内存分析工具
(1)LeakCanary
app的build.gradle中加入:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' }
Application中加入:
LeakCanary.install(this);
操作要检测的页面,查看结果。如果有内存泄漏
(2)Profile Memory 常用工具
从Heap Dump中可以看到leaks的数量和具体发生泄漏的类。使用方便,快捷
(3)adb
adb shell dumpsys meminfo com.example.testclient 查看当前activity使用情况
小结,几个处理内存泄漏的方法:
(1)Context:单例模式引用长生命周期context
(2)Handler:handler.removeCallbacksAndMessages(null);
(3)广播、各种监听:适时注销
(4)非静态内部类:
- 提取为外部类,单独定义类
- 构造方法持有外部类的弱引用
- 转化为静态类 + 外部类的弱引用
private static class MyRunnable implements Runnable { private WeakReference<MainActivity> weakReference; public MyRunnable(MainActivity activity){ weakReference = new WeakReference<>(activity); } @Override public void run() { MainActivity activity = weakReference.get(); activity.initView(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】