有关Android Handler内存泄漏分析及解决办法
2017-07-14 15:40
435 查看
1、Android的开发工具是java,这能帮助我们解决很底层的问题 包括:内存管理,平台依赖。然而,有时候项目依然会报OOM错误,so垃圾收集器在哪?
2、我主要研究一种情况:内存中较大对象很长一段时间内不能被释放。这方面并不完全算作内存溢出,对象会在某一时间点上被收集,so我们不屌它。虽然有时候他也会导致oom,所以不建议这么干滴。(这话咋说的这么矛盾,作者精分了?)
3、简单例子:
4、这是一个非常基本的Activity,注意到匿名类Runnable被handler延迟执行了好多次,我们运行并且旋转屏幕多次,然后dump memory 分析它
5、现在内存里有很多Activity,这tm很不好啊,为啥gc没收了他们。
6、获取所有内存的Activity查询语句是在OQL(Object Query Language)创建的,非常简单粗暴。
7、能够看出,其中一个Activity是被this$0引用了,这是一个来自内部匿名类指向其主类的间接引用(OMG) this$0是被callback方法引用的,callback又被一堆next()方法引用到主线程。
8、任何时候你创建一个非静态内部类在你自己的类中,java会自动为其创建一个间接引用指向其主类。
9、只要你的handler调用了Runnable或Message,它将被存储在LoopThread所引用的Message消息队列中,直到Message被执行。发送delayed消息是一个明显的内存泄漏,泄漏的时间大于等于延迟的时间。如果消息队列很长的话发送非延迟消息也可能引发一个临时泄漏。
10、解决方案1:Static Runnable
让我们试着克服这个把我们弄疯了的this$0,将匿名类转化为静态类
11、运行、旋转屏幕、释放内存堆:
12、啥?还tm这样,看看谁持有了Activities的引用
13、看这颗树的最底部,activity被DoneRunnable中的mTextView类的mContext引用,用静态内部类没办法解决内存泄漏,需要来点更狠的
14、解决方案2:弱引用的Static Runnable
我们继续用之前的修复办法并且弱引组织Activity被释放的TextView
15、重新运行,旋转,释放内存:
16、woops!只有一个Activity实例,解决了我们的内存泄漏问题
17、使用弱引用时要小心谨慎,他们能在任何时候被置为空,解决办法就是先赋值给一个局部变量(强引用),然后在使用前检查是否为空
18、解决handler内存泄漏我们需要做的:
1、用静态内部类或外部类
2、操纵Handler/Runnable时用弱引用
19、如果和一开始的代码进行对比,会发现在可读性和清晰度方面有很大的不同。一开始的代码很短和清晰,这么写太麻烦
20、解决方案3:在onDestroy中清空所有Message
Handler类中有个牛逼方法removeCallbacksAndMessages(),它可以接受空字段作为参数,它将移除所有特定Handler的Runnable和Message:
21、完美!这比上一个解决办法要好很多。唯一难搞的是你需要在所有的Activity/Fragment中调用onDestory()方法来清除message,听着都恶心
22、解决方案4:用WeakHandler
隆重介绍一下Badoo团队整的WeakHeadler,Handler的替代方案,安全多了。
23、代码很像吧,经实测内存中也只有一个实例,简单吧,代码和内存都一样简洁。使用方法gradle一下即可
24、总结:
本文一共介绍了3种方案:
1、声明一个继承Runnable类的静态内部类,并弱引用要操作的控件
2、在onDestroy()中调用removeCallbacksAndMessages()
3、调用Badoo团队的WeakHandler来替代os.Handler
666、WeakHandler只能替代postDelay(New Runnable())方法造成的内存泄漏,没办法替代os.Handler中接收消息的操作,所以正常情况下需要调用方法(2)
原文地址:https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks/
2、我主要研究一种情况:内存中较大对象很长一段时间内不能被释放。这方面并不完全算作内存溢出,对象会在某一时间点上被收集,so我们不屌它。虽然有时候他也会导致oom,所以不建议这么干滴。(这话咋说的这么矛盾,作者精分了?)
3、简单例子:
public class NewActivity extends Activity { private Handler mHandler = new Handler(); private TextView mTextview; @Override protected void onCreate(android.os.Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.inflate_textview); mTextview = (TextView) findViewById(R.id.xxwj_newtv4); mHandler.postDelayed(new Runnable() { @Override public void run() { mTextview.setText("Done"); } }, 80000); }; }
4、这是一个非常基本的Activity,注意到匿名类Runnable被handler延迟执行了好多次,我们运行并且旋转屏幕多次,然后dump memory 分析它
5、现在内存里有很多Activity,这tm很不好啊,为啥gc没收了他们。
6、获取所有内存的Activity查询语句是在OQL(Object Query Language)创建的,非常简单粗暴。
7、能够看出,其中一个Activity是被this$0引用了,这是一个来自内部匿名类指向其主类的间接引用(OMG) this$0是被callback方法引用的,callback又被一堆next()方法引用到主线程。
8、任何时候你创建一个非静态内部类在你自己的类中,java会自动为其创建一个间接引用指向其主类。
9、只要你的handler调用了Runnable或Message,它将被存储在LoopThread所引用的Message消息队列中,直到Message被执行。发送delayed消息是一个明显的内存泄漏,泄漏的时间大于等于延迟的时间。如果消息队列很长的话发送非延迟消息也可能引发一个临时泄漏。
10、解决方案1:Static Runnable
让我们试着克服这个把我们弄疯了的this$0,将匿名类转化为静态类
@Override protected void onCreate(android.os.Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.inflate_textview); mTextview = (TextView) findViewById(R.id.xxwj_newtv4); mHandler.postDelayed(new DoneRunnable(mTextview), 80000); }; private static final class DoneRunnable implements Runnable{ private final TextView mTV; public DoneRunnable(TextView tv) { this.mTV = tv; } @Override public void run() { mTV.setText("Done"); } }
11、运行、旋转屏幕、释放内存堆:
12、啥?还tm这样,看看谁持有了Activities的引用
13、看这颗树的最底部,activity被DoneRunnable中的mTextView类的mContext引用,用静态内部类没办法解决内存泄漏,需要来点更狠的
14、解决方案2:弱引用的Static Runnable
我们继续用之前的修复办法并且弱引组织Activity被释放的TextView
@Override protected void onCreate(android.os.Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.inflate_textview); mTextview = (TextView) findViewById(R.id.xxwj_newtv4); mHandler.postDelayed(new DoneRunnable(mTextview), 80000); }; private static final class DoneRunnable implements Runnable{ private final WeakReference<TextView> mTV; public DoneRunnable(TextView tv) { this.mTV = new WeakReference<TextView>(tv); } @Override public void run() { final TextView tt = mTV.get(); if(tt != null){ tt.setText("Done"); } } }
15、重新运行,旋转,释放内存:
16、woops!只有一个Activity实例,解决了我们的内存泄漏问题
17、使用弱引用时要小心谨慎,他们能在任何时候被置为空,解决办法就是先赋值给一个局部变量(强引用),然后在使用前检查是否为空
18、解决handler内存泄漏我们需要做的:
1、用静态内部类或外部类
2、操纵Handler/Runnable时用弱引用
19、如果和一开始的代码进行对比,会发现在可读性和清晰度方面有很大的不同。一开始的代码很短和清晰,这么写太麻烦
20、解决方案3:在onDestroy中清空所有Message
Handler类中有个牛逼方法removeCallbacksAndMessages(),它可以接受空字段作为参数,它将移除所有特定Handler的Runnable和Message:
@Override protected void onDestroy() { mHandler.removeCallbacksAndMessages(null); super.onDestroy(); }
21、完美!这比上一个解决办法要好很多。唯一难搞的是你需要在所有的Activity/Fragment中调用onDestory()方法来清除message,听着都恶心
22、解决方案4:用WeakHandler
隆重介绍一下Badoo团队整的WeakHeadler,Handler的替代方案,安全多了。
23、代码很像吧,经实测内存中也只有一个实例,简单吧,代码和内存都一样简洁。使用方法gradle一下即可
24、总结:
本文一共介绍了3种方案:
1、声明一个继承Runnable类的静态内部类,并弱引用要操作的控件
2、在onDestroy()中调用removeCallbacksAndMessages()
3、调用Badoo团队的WeakHandler来替代os.Handler
666、WeakHandler只能替代postDelay(New Runnable())方法造成的内存泄漏,没办法替代os.Handler中接收消息的操作,所以正常情况下需要调用方法(2)
原文地址:https://techblog.badoo.com/blog/2014/08/28/android-handler-memory-leaks/
相关文章推荐
- Android Handler leak 分析及解决办法,使用WeakReference
- Android Handler leak分析及解决办法详解
- Android Handler leak 分析及解决办法
- Android之Handler内存泄漏分析及解决
- Android之Handler内存泄漏分析及解决
- Android之Handler内存泄漏分析及解决
- Android重写Handler的内存泄漏及解决办法
- Android之Handler内存泄漏分析及解决
- Android Handler leak 分析及解决办法
- Android 面试总结 Handler内存泄漏分析及解决
- Android中使用Handler造成内存泄露的分析和解决
- 【android】Handler引起的内存泄露及解决办法
- 有关编译嵌入式android的swap空间不够导致的编译错误和解决办法
- Android内存泄漏监测(MAT)及解决办法
- Android中Handler引起的内存泄露问题解决办法
- android anr本质分析及解决办法
- Android内存泄漏监测(MAT)及解决办法
- Android Handler leak 分析及解决办法
- Android中使用Handler造成内存泄露的分析和解决
- Android中使用Handler造成内存泄露的分析和解决