一,什么是内存泄漏
首先我们要有堆内存的概念,堆内存是用来存放对象的,堆内存和栈内存的概念可以参考这篇文章https://www.cnblogs.com/liyonghua/p/8805017.html
jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。当对象不被引用时,会被gc回收,释放占用的堆内存。如果不再用到的对象,被错误引用,而无法被回收,这就发生了“内存泄漏”
二,handle内存泄漏的原因
非内部类默认会持有外部类的引用,这样当Activity被销毁时,由于被匿名handler对象所持有而不能被释放,Activity所占用的内存就会泄露。
public class HandleTestActivity extends AppCompatActivity {
private TextView tv;
private int id = 0;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("HandleTestActivity", "handleMessage");
tv.setText(msg.arg1 + "");
Toast.makeText(HandleTestActivity.this, msg.arg1 + " handleMessage", Toast.LENGTH_LONG).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("HandleTestActivity", "onCreate");
setContentView(R.layout.activity_handle_test);
tv = findViewById(R.id.tv);
Message msg = handler.obtainMessage();
++id;
msg.arg1 = id;
handler.sendMessageDelayed(msg, 2000);
}
@Override
protected void onPause() {
super.onPause();
Log.e("HandleTestActivity", "onPause");
Message msg = handler.obtainMessage();
++id;
msg.arg1 = id;
handler.sendMessageDelayed(msg, 100000);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("HandleTestActivity", "onDestroy");
//我们可以在Activity onDestroy()时调用handler.removeCallbacksAndMessages(null),
// 这样就把queue里所有的message都remove掉了,之前说过message被message pool回收掉会reset,
// 因此不会再引用handler,这条引用链就断掉了。
// handler.removeCallbacksAndMessages(null);
}
/**
* 当垃圾回收确定不再有对对象的引用时,由垃圾回收器对对象调用。
* 子类重写@code finalize方法以释放系统资源或执行其他清理。
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
Log.e("HandleTestActivity", "finalize");
}
}
对于非静态的内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。所以非内部类默认会持有外部类的引用,可以查看build后的class文件,文件路径 app\build\intermediates\javac\
文件展开如下,HandleTestActivity$1 表示HandleTestActivity为外部类,1代表匿名内部类,构造方法传了HandleTestActivity的实例。非静态的内部类也是类似的!
class HandleTestActivity$1 extends Handler {
HandleTestActivity$1(HandleTestActivity this$0) {
this.this$0 = this$0;
}
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("HandleTestActivity", "handleMessage");
HandleTestActivity.access$000(this.this$0).setText(msg.arg1 + "");
Toast.makeText(this.this$0, msg.arg1 + " handleMessage", 1).show();
}
}
当启动HandleTestActivity后再finish,可以通过Android studio的profiler工具查看引用是否被释放
handler实例引用了Activity,handler又被其messsage.target所引用,message放入message queue中,message queue生命周期结束前,message queue都间接引用了activity,导致内存泄漏!
也有些文章提到,可以在Activity onDestroy()时调用handler.removeCallbacksAndMessages(null),这样确实message queue对activity的引用链没有了,如下图,但是handle对activity的引用链仍然存在
可以看到handle仍然引用着当前的this
三。解决方法
最好的解决方式就是使用静态内部类解决
public class HandleTest2Activity extends AppCompatActivity {
private TextView tv;
private int id = 0;
private Handler handler = new Myhandle(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("HandleTest1Activity", "onCreate");
setContentView(R.layout.activity_handle_test2);
tv = findViewById(R.id.tv);
Message msg = handler.obtainMessage();
++id;
msg.arg1 = id;
handler.sendMessageDelayed(msg, 2000);
}
@Override
protected void onPause() {
super.onPause();
Log.e("HandleTest1Activity", "onPause");
Message msg = handler.obtainMessage();
++id;
msg.arg1 = id;
handler.sendMessageDelayed(msg, 40000);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("HandleTest1Activity", "onDestroy");
// handler.removeCallbacksAndMessages(null);
}
/**
* 当垃圾回收确定不再有对对象的引用时,由垃圾回收器对对象调用。
* 子类重写@code finalize方法以释放系统资源或执行其他清理。
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
Log.e("HandleTest1Activity", "finalize");
}
private static class Myhandle extends Handler {
private WeakReference<Context> reference;//弱引用
public Myhandle(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Context context = reference.get();
Log.e("Myhandle", "handleMessage context=" + context);
if (null != context){
Toast.makeText(context, msg.arg1 + " handleMessage", Toast.LENGTH_LONG).show();
}
}
}
}
静态内部类编译好的class文件,对照HandleTestActivity$1,构造方法没有传入外部类的对象
class HandleTest2Activity$Myhandle extends Handler {
private WeakReference<Context> reference;
public HandleTest2Activity$Myhandle(Context context) {
this.reference = new WeakReference(context);
}
public void handleMessage(Message msg) {
super.handleMessage(msg);
Context context = (Context)this.reference.get();
Log.e("Myhandle", "handleMessage context=" + context);
if (null != context) {
Toast.makeText(context, msg.arg1 + " handleMessage", 1).show();
}
}
}
此时看运行完的结果,此时已经没有handle对activity的引用了
四,leakcanary
内存泄漏也可以使用leakcanary分析