LeakCanary和常见内存泄漏场景


LeakCanary和常见内存泄漏场景

一. LeakCanary介绍

1. 介绍

2. 用法

二. 常见泄漏方式

1. 不合理的单例模式、静态Activity、Context等

2. 持有Activity内的静态View

3.较长生命周期的匿名内部类

4.Handler中有生命周期较长的匿名内部类

5. 资源未关闭造成的内存泄漏

一. LeakCanary介绍

1. 介绍

LeakCanary是一个检测内存泄露的开源类库,以可视化的方式 轻松检测内存泄露,并且在出现内存泄漏时及时通知开发者,省去手工分析hprof的过程。

Github:LeakCanary

2. 用法

Step1:在app的build.gradle的dependencies节点中添加以下两行

1.debugCompile'com.squareup.leakcanary:leakcanary-android:1.3'

2.releaseCompile'com.squareup.leakcanary:leakcanary-android-no-op:1.3'

Step2:在Application中的onCreate方法中注册

1.publicclassLeakApplicationextendsApplication{

2.

3.@Override

4.publicvoidonCreate(){

5.super.onCreate();

6.LeakCanary.install(this);

7.}

8.

9.}

Step3:启动应用,等到memory leak发生,当内存泄漏发生时,launcher上生成一个图标(名称为Leaks),点击进去,即可看到完整的内存泄漏的引用路径。实际测试发现,点击Leaks后,页面上可能出现一段时间的空白,等待一段时间才出现内存泄漏的引用路径。

LeakCanary和常见内存泄漏场景_第1张图片

内存泄漏后桌面上产生的图标

二. 常见泄漏方式

1. 不合理的单例模式、静态Activity、Context等

示例代码:

1.publicclassSingleInstance{

2.privatestaticSingleInstance sInstance;

3.privateContext mContext;

4.privateSingleInstance(Context context){

5.mContext = context;

6.}

7.publicstaticSingleInstancegetInstance(Context context){

8.if(sInstance ==null){

9.sInstance =newSingleInstance(context);

10.}

11.returnsInstance;

12.}

13.}

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.@Override

4.protectedvoidonCreate(Bundle savedInstanceState){

5.super.onCreate(savedInstanceState);

6.setContentView(R.layout.activity_second);

7.SingleInstance.getInstance(this);

8.}

9.

10.}

LeakCanary提示:

LeakCanary和常见内存泄漏场景_第2张图片

不合理的SingleInstance导致的内存泄漏

原因分析:

这种场景非常常见!当SecondActivity销毁时,SingleInstance仍持有SecondActivity的对象的引用(mContext),导致SecondActivity对象不能释放。

解决方法:

可使用Application的Context代替Activity。

2. 持有Activity内的静态View

示例代码:

1.publicclassTestDataModel{

2.privatestaticTestDataModel sInstance;

3.privateTextView mRetainedTextView;

4.publicstaticTestDataModelgetInstance(){

5.if(sInstance ==null) {

6.sInstance =newTestDataModel();

7.}

8.returnsInstance;

9.}

10.publicvoidsetRetainedTextView(TextView textView){

11.mRetainedTextView = textView;

12.}

13.}

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.@Override

4.protectedvoidonCreate(Bundle savedInstanceState){

5.super.onCreate(savedInstanceState);

6.setContentView(R.layout.activity_second);

7.TextView textView = (TextView) findViewById(R.id.test_text_view);

8.TestDataModel.getInstance().setRetainedTextView(textView);

9.}

10.

11.}

LeakCanary提示:

LeakCanary和常见内存泄漏场景_第3张图片

持有静态View导致的内存泄漏

原因分析:

TestDataModel 持有Activity中的TextView的静态引用,而TextView又持有SecondActivity的引用,从而导致SecondActity在onDestory之后不能释放

解决方法:

尽量避免这种用法

3.较长生命周期的匿名内部类

示例代码:

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.@Override

4.protectedvoidonCreate(Bundle savedInstanceState){

5.super.onCreate(savedInstanceState);

6.setContentView(R.layout.activity_second);

7.testLeakMemory();

8.}

9.

10.privatevoidtestLeakMemory(){

11.newThread(newRunnable() {

12.@Override

13.publicvoidrun(){

14.SystemClock.sleep(200000);

15.}

16.}).start();

17.}

18.

19.}

LeakCanary提示:

LeakCanary和常见内存泄漏场景_第4张图片

原因分析:

SecondActivity中的匿名内部类持有SecondActivity的引用,并且该匿名内部类的生命周期比Activity要长

解决方法:

SecondActivity在销毁时,应取消内部类对其的引用

4.Handler中有生命周期较长的匿名内部类

示例代码:

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.privateHandler mHandler;

4.

5.@Override

6.protectedvoidonCreate(Bundle savedInstanceState){

7.super.onCreate(savedInstanceState);

8.setContentView(R.layout.activity_second);

9.testLeakMemory();

10.}

11.

12.privatevoidtestLeakMemory(){

13.HandlerThread thread =newHandlerThread("test");

14.thread.start();

15.mHandler =newHandler(thread.getLooper());

16.mHandler.post(newRunnable() {

17.@Override

18.publicvoidrun(){

19.SystemClock.sleep(200000);

20.}

21.});

22.}

23.}

LeakCanary提示:

LeakCanary和常见内存泄漏场景_第5张图片

Handler中有匿名内部类Runnable

原因分析:

mHandler在post时,其参数为匿名内部类,持有SecondActivity的引用,并且该Runnable的生命周期比SecondActivity长

解决方法:

SecondActivity在onDestory时,应移除mHandler中未完成的任务

5. 资源未关闭造成的内存泄漏

Cursor、BroadcastReceiver、 TypedArray、File 、Stream 、Bitmap等使用完毕后应及时释放或关闭或反注册。

示例代码(Cursor):

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.@Override

4.protectedvoidonCreate(Bundle savedInstanceState){

5.super.onCreate(savedInstanceState);

6.setContentView(R.layout.activity_second);

7.testLeakMemory();

8.}

9.

10.privatevoidtestLeakMemory(){

11.Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null);

12.if(cursor !=null){

13.while(cursor.moveToNext()){

14.Log.d("test",cursor.getString(0));

15.}

16.}

17.}

18.

19.}

示例代码(BroadcastReceiver):

1.publicclassSecondActivityextendsAppCompatActivity{

2.

3.@Override

4.protectedvoidonCreate(Bundle savedInstanceState){

5.super.onCreate(savedInstanceState);

6.setContentView(R.layout.activity_second);

7.testLeakMemory();

8.}

9.

10.privatevoidtestLeakMemory(){

11.IntentFilter filter =newIntentFilter(Intent.ACTION_MEDIA_BUTTON);

12.TestReceiver receiver =newTestReceiver();

13.registerReceiver(receiver,filter);

14.}

15.

16.privateclassTestReceiverextendsBroadcastReceiver{

17.@Override

18.publicvoidonReceive(Context context, Intent intent){

19.

20.}

21.}

22.}

解决方法:

Cursor、BroadcastReceiver、 TypedArray、File 、Stream 、Bitmap等使用完毕后应及时关闭或释放

你可能感兴趣的:(LeakCanary和常见内存泄漏场景)