首页 > 学院 > 开发设计 > 正文

内存泄漏的检测、几种常见场景及解决方法

2019-11-06 09:36:59
字体:
来源:转载
供稿:网友

文章参考:http://blog.nimbledroid.com/2016/05/23/memory-leaks.html

使用AndroidStudio检测内存泄漏:

http://wetest.QQ.com/lab/view/99.html

一.内存泄漏的原因

一般内存泄漏(traditional memory leak)的原因是:由忘记释放分配的内存导致的。逻辑内存泄漏(logical memory leak)的原因是:当应用不再需要这个对象,当仍未释放该对象的所有引用。

二.几种常见的内存泄漏场景

实例代码一: MainActivity:

public class MainActivity extends AppCompatActivity { TextView tv_test; PRivate Handler handler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_test = (TextView) findViewById(R.id.tv_test); Message message = Message.obtain(); message.what = 0; message.obj = "000"; handler.sendMessageDelayed(message,60000); new Util(this,handler); } @Override protected void onDestroy() { super.onDestroy(); //handler.removeCallbacksAndMessages(null); } class MyHandler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv_test.setText((CharSequence) msg.obj); } }}

Util代码:

public class Util { private static Context context; private static Handler handler; Util(Context context,Handler handler){ this.context = context; this.handler = handler; }}

场景一:静态变量持有了Activity

由于Util中的context是static的,即其生命周期是和应用一样长的,在MainActivity方法中创建Util对象的时候,持有了MainActivity的引用(即this),导致该MainActivity无法被垃圾回收器回收,这样就造成了内存泄漏。

场景二:匿名内部类或非静态内部类引起的内存泄漏

内部类会隐式地持有了外部类。在该例子中,MyHandler会持有MainActivity的引用,而Util中的静态变量又持有了MyHandler的引用,这样还是静态变量间接地持有了Activity,导致Activity无法被垃圾回收器回收。 另外还有其它几种常见的实例 实例一:TimerTask

private void scheduleTimer() { new Timer().schedule(new TimerTask() { @Override public void run() { while(true); } }, Long.MAX_VALUE >> 1); }实例二:void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start(); }

该方法若在Activity中,由于TimerTask使用的是匿名内部类,会持有Activity的引用,因此会造成内存泄漏。其实这个是匿名内部类导致内存泄漏的一个实例。

场景三:Handler

因为Handler是基于消息的。每次new出Handler,都会创建一个消息队列用于处理你使用handler发送的消息,形如:handler.send***Message。由于消息的发送总是会有先来后到的区别(如果只是这样都还好,毕竟再慢也不会太久,总归可以跑完,可能会延迟个几秒),但是如果你使用的是sendMessageDelayed(Message msg, long delayMillis)或postDelayed(Runnable r, long delayMillis)等发送延迟消息的时候,那基本内存泄漏发生的概率已经在90%以上了。因为handler会持有MainActivity的引用,会导致MainActivity无法销毁。   

场景四:Static Views

private static View view;void setStaticView() { view = findViewById(R.id.sv_button);}

由于View持有其宿主Activity的引用,故和原因一其实是一样的。

场景五:无限循环的属性动画

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv_test,"rotation",0,360).setDuration(2000); objectAnimator.setRepeatCount(ValueAnimator.INFINITE); objectAnimator.start();

若在OnDestory()中没有停止动画,则动画即时不可见,仍会一直执行下去,tv_test持有Activity的引用,故会导致Activity无法被回收

三.解决方案

针对场景一: 静态变量不要持有Activity,若无特殊需求,将其改为一般的变量针对原因二: 将非静态内部类/匿名内部类替换为静态内部类,这样就不会持有外部类的引用了针对场景三: 在onDestory()中调用handler.removeCallbacksAndMessages(null);,就是移除所有的消息和回调,简单一句话就是清空了消息队列。注意,不要以为你post的是个Runnable或者只是sendEmptyMessage。你可以看一下源码,在handler里面都是会把这些转成正统的Message,放入消息队列里面,所以清空队列就意味着这个Handler直接被打成原型了,当然也就可以回收了。针对场景四: 尽量不要用静态的view,若必须使用,需在onDestory的时候将view置为null针对场景五: 在onDestory()中,停止动画 objectAnimator.cancel();

四.总结:

定义变量的时候,慎用static,例如若使用静态的集合,集合中的数据都不会被回收及时回收需要回收的资源,如bitmap,cursor等使用非静态内部类或匿名内部类的时候要注意

如有问题,欢迎加群交流:579853893


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表