Android handle memory leak

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/qq_15345551/article/details/100185513

First, what is a memory leak

First, we have the concept of heap memory, heap memory is used to store objects, the concept of heap memory and stack memory can refer to this article https://www.cnblogs.com/liyonghua/p/8805017.html

jvm only a heap (heap) is shared by all threads, the heap does not store the basic types and object references, only store the object itself, almost all object instances and arrays are allocated on the heap. When an object is not referenced, it will be recycled gc, release the memory occupied by the heap. If the object is no longer used, was misquoted, and can not be recovered, which had "memory leak"

Two, handle memory leak causes

Inner class will hold a non-default references the outer class, so when the Activity is destroyed, can not be held due to release anonymous handler objects, Activity occupied by the memory will leak.

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");
    }
}

For non-static inner class, create inner class instance object depend on outside class, in the absence of external class instance can not be created inside the class. Therefore, the non-default class will hold internal references to external class, you can view the class file after the build, the file path app \ build \ intermediates \ javac \

Expand the following document, $ 1 represents HandleTestActivity HandleTestActivity external class, represents anonymous inner classes, instances HandleTestActivity constructor of pass. Non-static inner classes is similar!

 

 

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();
    }
}

When you start HandleTestActivity after the finish, you can view references by Android studio of profiler tools are to be released

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分析

 

源码地址 https://dev.tencent.com/u/kuangxuefeng/p/AndroidTestDemo/git

Guess you like

Origin blog.csdn.net/qq_15345551/article/details/100185513