转载请注明出处:http://blog.csdn.net/llew2011/article/details/52089842
提到内存泄露有的小伙伴们可能遇见过,处理起来是非常棘手的。记得刚从事Android开发那会手机主流版本还是2.2的,手机内存普遍都很小,开发的时候在处理耗用内存过大的对象(比如Bitmap等)上,稍有不慎就会出现OOM,这很让人头疼,更为头疼的是发版后还是会收到有关OOM的反馈。为了解决OOM的问题就反复查代码捋逻辑,然后借助MAT工具来分析可能引发的内存泄露点,但是MAT使用起来很麻烦……直到去年著名的开源组织square在github上开源了著名的内存泄露检测库LeakCanary,它是专门用来检测内存泄露的,有了这个库后告别了MAT工具,更给力的是在程序中用一行代码就搞定了,该库从开源至今是最受欢迎的开源库之一。用了该库那么久总该有点贡献吧?因此我打算写篇文章来帮助小伙伴们从源码的角度深入理解一下LeakCanary的内存泄露检测机制,如果你对该库十分熟悉,请跳过本文(*^__^*) ……
在分析LeakCanary源码之前,我们有必要先来了解一下Java的内存分配策略以及Android中常见的内存泄漏。
二、堆与栈的区别
public class MainActivity extends Activity { private static Drawable mBackgroundDrawable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView label = new TextView(this); label.setText("Leaks are bad !"); if (null == mBackgroundDrawable) { mBackgroundDrawable = getResources().getDrawable(R.id.bg); } label.setBackgroundDrawable(mBackgroundDrawable); setContentView(label); } }MainActivity中静态成员变量mBackgroudDrawable缓存了drawable对象,这种写法在Android 3.0之前会导致MainActivity销毁后无法被系统回收,因为在Android3.0之前View的setBackgroundDrawable()方法的源码如下所示:
public void setBackgroundDrawable(Drawable background) { // ...... 此处省略部分代码 ...... background.setCallback(this); // ...... 此处省略部分代码 ...... }根据源码可知background对当前View保持了一个引用,而View又对当前的Activity保持了一个引用,当退出当前Activity时该Activity本该释放,但是因为mBackgroundDrawable是静态成员变量,它的生命周期是伴随着整个应用程序的,它间接持有了Activity的应用从而导致Activity对象不能被释放,进而导致了内存泄露。在Android3.0之前Drawable的setCallback()方法是这样的:
public final void setCallback(Callback cb) { mCallback = cb; }但是在Android3.0之后写法是这样的:
public final void setCallback(Callback cb) { mCallback = new WeakReference<Callback>(cb); } // 下边是Android7.0版本写法 public final void setCallback(@Nullable Callback cb) { mCallback = cb != null ? new WeakReference<>(cb) : null; }由此可知在Android3.0之后Drawable在setCallback()方法中使用了软引用,把内存泄露的问题修复了。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread() { @Override public void run() { while(true) { try { sleep(10000); // 执行长时间的耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } }在上述例子中当MainActivity执行destroy()后因为new出来的Thread还在执行,同时Thread为MainActivity的匿名内部类,会持有MainActivity对象的引用(详细可以通过查看字节码求证)造成MainActivity不会被垃圾回收器回收,从而造成内存泄露。解决办法可通过静态内部类的方式:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new InnerTask().start(); } private static class InnerTask extends Thread { @Override public void run() { while(true) { try { sleep(10000); // 执行长时间的耗时任务 } catch (InterruptedException e) { e.printStackTrace(); } } } } }由于静态内部类不持有外部类的引用,与独立类文件形式的类没有本质区别。上面的例子需要注意的是在Activity退出时如果没有必要应该将线程stop掉。
public class MainActivity extends AppCompatActivity { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // do work } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }以上例子咋一看并没有什么问题,但是Android Lint时会提示我们一个这样的warning:In Android,Handler classes should be static or leaks might occur。这个意思就是说在Android中,Handler类应该是静态的否则可能会发生内存泄露。
public class MainActivity extends AppCompatActivity { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // do work } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.postDelayed(new Runnable() { @Override public void run() { // do work } }, 600000); finish(); } }在上面的代码中当MainActivity结束后,在Message Queue处理这个Message之前,MainActivity会持续存活。由于Message持有Handler的引用而Handler又持有MainActivity的引用,所以就导致MainActivity的资源在Message被处理之前都不能被回收,所以会引发内存泄露。解决方法有以下几种:
public class MainActivity extends AppCompatActivity { private final InnerHandler mHandler = new InnerHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.postDelayed(new Runnable() { @Override public void run() { // do work } }, 600000); finish(); } private static class InnerHandler extends Handler { private final WeakReference<MainActivity> mainActivityWeakReference; public InnerHandler(MainActivity activity) { this.mainActivityWeakReference = null == activity ? null : new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { if (null != mainActivityWeakReference) { MainActivity mainActivity = mainActivityWeakReference.get(); if (null != mainActivity) { // do work } } } } }解决方法二:
public class MainActivity extends AppCompatActivity { private static final Runnable TASK = new Runnable() { @Override public void run() { // do work } }; private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { // do work } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.postDelayed(TASK, 600000); finish(); } }解决方式三:
public class MainActivity extends AppCompatActivity { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // do work } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.postDelayed(new Runnable() { @Override public void run() { // do work } }, 600000); finish(); } @Override protected void onDestroy() { if (null != mHandler) { mHandler.removeCallbacksAndMessages(null); } super.onDestroy(); } }方法一和方法二同样采用了内部静态类的方式,方法二虽然避免了MainActivity的内存泄露,但是它静态化一个Runnable静态对象,对于这个静态对象在MainAcitvity销毁后并没有什么用处,也属于一种小范围的内存泄露,所以建议采用第一种方式解决。解决方式三在MainActivity的onDestroy()方法中调用mHandler的removeCallbacksAndMessage()方法移除消息队列中那些还没有被执行的Message对象防止间接持有MainActivity的引用而造成内存泄露。综上所述建议采用方法一和方法三结合的方式,从根本上解决Handler造成的内存泄露。
registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // ... 省略 ... } }, filter);上边注册了一个广播接收器,这种通过匿名内部类注册了广播接收器是个很严重的错误,它会导致注入的广播接收器不会被unregister而造成内存泄露。
public class MainActivity extends AppCompatActivity { private ValueAnimator mValueAnimator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); mValueAnimator = ValueAnimator.ofInt(0, 1); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // do some work } }); mValueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { // do some work } @Override public void onAnimationEnd(Animator animation) { // do some work } @Override public void onAnimationCancel(Animator animation) { // do some work } @Override public void onAnimationRepeat(Animator animation) { // do some work } }); mValueAnimator.setInterpolator(new LinearInterpolator()); mValueAnimator.setDuration(3000); mValueAnimator.start(); } }上边的例子看起来也没啥问题,可能我们也都这样写过,但是这确实存在内存泄露的风险,mValueAnimator中设置了两个内部匿名监听类,这俩内部匿名类默认持有MainActivity的引用,但是在mValueAnimator中设置监听器是这样处理的:
public void addUpdateListener(AnimatorUpdateListener listener) { if (mUpdateListeners == null) { mUpdateListeners = new ArrayList<AnimatorUpdateListener>(); } mUpdateListeners.add(listener); } public void addListener(AnimatorListener listener) { if (mListeners == null) { mListeners = new ArrayList<AnimatorListener>(); } mListeners.add(listener); }以上是Animator设置监听器的源码,根据源码我们知道监听器的设置最终是存储在集合中的,那也就是说在MainActivity中的mValueAnimator间接持有了MainActivity的引用,会造成内存泄露的风险,应当在MainActivity的onDestroy()方法中清除监听器,如下所示:
@Override protected void onDestroy() { if (null != mValueAnimator) { mValueAnimator.removeAllUpdateListeners(); mValueAnimator.removeAllListeners(); mValueAnimator = null; } super.onDestroy(); }这样就避免了内存泄露的风险。
10-24 23:19:08.360: E/BufferQueue(228): [com.**.**] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count 10-24 23:19:08.360: E/Surface(14162): dequeueBuffer failed (Invalid argument) 10-24 23:19:08.360: E/ViewRootImpl(14162): Could not lock surface 10-24 23:19:08.360: E/ViewRootImpl(14162): java.lang.IllegalArgumentException 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Surface.nativeLockCanvas(Native Method) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Surface.lockCanvas(Surface.java:246) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2513) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl.draw(ViewRootImpl.java:2487) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2331) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1961) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1060) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5782) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Choreographer.doCallbacks(Choreographer.java:574) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Choreographer.doFrame(Choreographer.java:544) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.os.Handler.handleCallback(Handler.java:733) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.os.Handler.dispatchMessage(Handler.java:95) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.os.Looper.loop(Looper.java:136) 10-24 23:19:08.360: E/ViewRootImpl(14162): at android.app.ActivityThread.main(ActivityThread.java:5086) 10-24 23:19:08.360: E/ViewRootImpl(14162): at java.lang.reflect.Method.invokeNative(Native Method) 10-24 23:19:08.360: E/ViewRootImpl(14162): at java.lang.reflect.Method.invoke(Method.java:515) 10-24 23:19:08.360: E/ViewRootImpl(14162): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893) 10-24 23:19:08.360: E/ViewRootImpl(14162): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:702) 10-24 23:19:08.360: E/ViewRootImpl(14162): at dalvik.system.NativeStart.main(Native Method)有关WebView造成内存泄露的问题请查看https://code.google.com/p/android/issues/detail?id=63738或者https://code.google.com/p/android/issues/detail?id=71742,所以要在Activity或者Fragment中合理的销毁WebView,如下所示:
@Override protected void onDestroy() { if (null != webView) { ViewParent viewParent = webView.getParent(); if (viewParent instanceof ViewGroup) { ViewGroup parent = (ViewGroup) viewParent; parent.removeView(webView); webView.destroy(); } } super.onDestroy(); }测试一下看看
public void recycleBitmap(Bitmap bitmap) { if (null != bitmap && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } }②、构造Adapter时没有使用缓存的ConvertView
http://blog.csdn.net/gemmem/article/details/8920039