内存优化二

内存泄漏

Handler内存泄漏分析

public class MainActivity extends AppCompatActivity {

    private final int MESSAGE_WHAT = 10000;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);
    }

}

在启动时,用Handler发空消息,然后在处理消息里面又发空消息。我们启动MainActivity 之后,1s之内点返回,然后在Profile里面点多次回收,按理说MainActivity 应该被GC回收掉,没有MainActivity 的实例。 但是实际上,如下图所示,还有一个MainActivity 实例。
在这里插入图片描述
可以看到,我截取的部分是点了很多次GC的,很多个垃圾桶的标志。但是这个MainActivity仍然有一个实例,说明MainActivity已经泄露了。

泄漏分析

在这里插入图片描述
这个按钮(dump the heap)能自动截取一段内存信息,生成一个文件。
在这里插入图片描述
生成完点这里保存到磁盘中,后缀名是hprof

然后使用sdk的工具hprof-conv,转一下,转成mat工具可以使用的。

.\hprof-conv.exe .\test1.hprof .\test2.hprof

在这里插入图片描述
prof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof为原始文件,yyyyy.hprof为转换过后的文件。

使用mat工具打开test2.hprof文件。Mat:下载地址
打开之后,点一下下图位置
在这里插入图片描述
在ClassName的地方输入MainActivity,然后回车,找到MainActivity的所有实例。
在这里插入图片描述
然后排除软、若、虚引用,也就是只留强引用。
在这里插入图片描述
然后mat会打开新的标签页,用来展示这个MainActivity的引用信息
在这里插入图片描述
从图中我们可以看到MainActivity被MainActivity$1引用,然后一层一层到上面。哦了,分析到这里,我们也能从证据上看出来MainActivity因为MainActivity$1的强引用导致内存泄漏了。

理论分析

首先非静态内部类持有外部类引用,其次Handler发送Message,会把本身交给Message,Message放到Looper的Queue中。倒过来推就是,Looper中引用Queue,Queue引用Message,Message引用Handler,Handler引用MainActivity。所以MainActivity内存泄漏了。

解决办法

static修饰

 private static final int MESSAGE_WHAT = 10000;
    static Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);
        }
    };

在这里插入图片描述
经过垃圾回收,MainActivity已经被回收掉了,没有MainActivity的实例了。

缺点:static声明过多, 会造成类加载速度变慢,因为static是跟着类一起加载的。

软引用(这个在非静态内部类无法控制)

在Handler这个例子中没法测试,但是其它的可以试试的。

缺点:何时被回收不确定,如果在回收之后还需要用,就比较麻烦了。

销毁Activity时移除消息

public class MainActivity extends AppCompatActivity {

    private final int MESSAGE_WHAT = 10000;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 3 * 1000);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeMessages(MESSAGE_WHAT);
    }

在这里插入图片描述
在onDestroy回调方法中,移除Message,这样引用链就会在Queue–Message这里断开,下面的对象就可以回收了。从截图中可以看到不论MainActivity还是Handler都被正确回收了。

缺点:如果Handler写的比较多,这样的做法导致代码量比较多。

总结

内存泄漏的原因就是 短生命周期的实例,被长生命周期的实例引用,导致短生命周期的实例无法被回收。具体如何解决内存泄漏问题,不可一概而论,具体问题,具体分析,如果上面的例子中,Handler处理的事情很简单,那么即使MainActivity销毁,只要Handler的事情做完了,还是可以正常回收的。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    private int MESSAGE_WHAT = 10000;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Log.d(TAG, "handleMessage: 我收到消息了");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler.sendEmptyMessageDelayed(MESSAGE_WHAT, 5 * 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

在这里插入图片描述
如上所示,Handler处理的事情很简单, 最终也是没有内存泄漏的。
所以没有绝对的解决方法,具体问题,具体分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值