Android内存泄漏问题

2019-01-08  本文已影响10人  不会敲代码的好代码

一、垃圾回收

一般来说,程序使用内存遵循先向操作系统申请一块内存,使用内存,使用完毕之后释放内存归还给操作系统。然而在传统的C/C++等要求显式释放内存的编程语言中,在合适的时候释放内存是一个很有难度的工作,因此Java等编程语言都提供了基于垃圾回收算法的内存管理机制。

常见的垃圾回收算法有引用计数算法(Reference Counting)、标注—清除算法(Mark and Sweep GC)、复制算法(Copying GC)和逐代回收(Generational GC)等算法,其中Android虚拟机采用的是标注—清除算法,并不是大多数JVM实现里采用的逐代回收算法。

二、几个基本概念

三、Android内存泄漏存在的原因

1.静态变量导致的内存泄漏
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    
    private static View mView;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView (R.layout.activity_main);
        mView = new View (this);
    }
}

上面这段代码就会产生内存泄漏,mView是静态变量,它的内部持有当前的activity,因此Activity仍然无法释放。

2.单例模式导致的内存泄漏

不合理的单例模式也会引起内存泄漏,


public class LeakTest {

    private static LeakTest mInstance;

    private LeakTest(Context context) {
    }

    /**
     * 单例模式
     */
    public static LeakTest getInstance(Context context) {
        if(mInstance == null) {
            mInstance = new LeakTest (context);
        }
        return mInstance;
    }

}

比如以上代码是一个提供单例的测试类,需要传入一个Contex进行获取,在Activity中使用的时候如果传入了改Activity的Context,当Activity不再使用的时候,如果在onDestroy时不进行操作,就会造成静态的单例变量一直引用这个Context,导致本该释放内存的变量一直占用内存造成内存泄漏。要解决这种类型的内存泄漏可以在引用Activity的Context时变成引用Application的Context。

3.属性动画导致内存泄漏

属性动画中有一类无限循环的动画,如果在Activity中播放此类动画但是没有在onDestroy中去停止动画,那么动画就会一直播放,尽管在当前界面以及看不到动画了。这个时候Activity的View会被动画持有,View又持有这个Activity,最终导致Activity无法释放,造成内存泄漏。解决方法是在onDestroy的时候调用animator.cancle()方法来停止动画。

4.其他原因导致的内存泄漏
android:largeHeap="true"

此时heapsize会增大2-3倍,缓解OOM的发生
其他内存泄漏的原因参考:https://blog.csdn.net/cyq1028/article/details/19980369

四、内存泄漏的排查

1.LeakCanary

A memory leak detection library for Android and Java.

LeakCanary的工作机制:

  1. RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。

  2. 然后在后台线程检查引用是否被清除,如果没有,调用GC。

  3. 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。

  4. 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。

  5. 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。

  6. HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。

  7. 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

LeakCanary的使用

1.添加依赖

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
  // Optional, if you use support library fragments:
  debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
}

2.新建一个Application类,用于使用LeakCanary

public class ExampleApplication extends Application {

 public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (ExampleApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

LeakCanary.install() 会返回一个预定义的 RefWatcher,同时也会启用一个 ActivityRefWatcher,用于自动监控调用 Activity.onDestroy() 之后泄露的 activity,LeakCanary自动监控Activity,如果要在Fragment中使用LeakCanary需要在onDestroy方法中进行监控。

public abstract class BaseFragment extends Fragment {

  @Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
  }
}

只要继承自基类的Fragment都会被监控内存泄漏的情况。

3.LeakCanary的自定义和其他操作参考
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/

内存泄漏 泄漏详情及原因

2. Android Profiler

上一篇 下一篇

猜你喜欢

热点阅读