LeakCanary:检测Android中的内存泄漏

Square开源了一个内存泄露自动探测神器——LeakCanary,它是一个Android和Java的内存泄露检测库,可以大幅度减少了开发中遇到的OOM问题,对于开发者来说,无疑是个福音,下面对该库的使用进行简单的介绍。

一 什么是内存泄漏:

有些对象只有有限的生命周期。当它们的任务完成之后,它们将被垃圾回收。如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏。随着泄漏的累积,app将消耗完内存。

比如,在Activity.onDestroy()被调用之后,view树以及相关的bitmap都应该被垃圾回收。如果一个正在运行的后台线程继续持有这个Activity的引用,那么相关的内存将不会被回收,这最终将导致OutOfMemoryError崩溃。

二 LeakCanary介绍:

一个用于检测Android&Java的内存泄漏检测库

A memory leak detection library for Android and Java.

“A small leak will sink a great ship.” - Benjamin Franklin
小漏不补沉大船。——本杰明 富兰克林

三 DEMO使用:

(1)在build.gradle中添加依赖:

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
 }

(2)在Application中初始化Leak:

public class MyApp extends Application {
    private static MyApp instance;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        LeakCanary.install(this);//初始化LeakCanary
    }

    public static final MyApp getInstance() {
        return instance;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
    }
}

或者这样初始化:

public class MyApplication extends Application {
//在自己的Application中添加如下代码
  public static RefWatcher getRefWatcher(Context context) {
    ExampleApplication application = (MyApplication) context.getApplicationContext();
    return application.refWatcher;
  }

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    refWatcher = LeakCanary.install(this);
  }
}

(3)在Activity中制造一个内存泄漏的场景:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    public static MainActivity instance = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        instance = this;//创建一个静态对象instance,保持其对当前activity的引用;
        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, SecondActivity.class);
                MainActivity.this.startActivity(intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //instance=null;   //当要销毁activity的时候,instance并未置空,仍然保持对MainActivity的引用,因此MainActivity不会销毁;
    }
}

SecondActivity.java:

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MainActivity.instance.finish();//通过instance关闭MainActivity;
            }
        });
    }
}

在finish的时候会出现内存泄漏,instance仍然保持对activity的引用。

(4)运行结果如下:

这里写图片描述

然后点击桌面的Leaks图标可以看到:

这里写图片描述

Leaks指出了内存泄漏的位置是instance。

(5)LeakCanary的检测工作机制:

LeakCanary检测内存泄漏的原理大概分为7个步骤,从创建被关联对象的弱引用、检查该应用是否被清除等。

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, 并以通知的形式展示出来。

图形路程图如下所示:

这里写图片描述

参考致谢:

1 利用 LeakCanary 来检查 Android 内存泄漏

2 LeakCanary Github介绍

3 LeakCanary开源项目

发布了102 篇原创文章 · 获赞 76 · 访问量 27万+
展开阅读全文

EMUI5.0 Android7.0中开发遇到内存泄漏

02-10

com.zxsd.shoppingguide.activity.AggregationEntryActivity has leaked: GC ROOT static android.gestureboost.GestureBoostManager.sGestureBoostManager references android.gestureboost.GestureBoostManager.mContext leaks com.zxsd.shoppingguide.activity.AggregationEntryActivity instance Retaining: 4.9 KB. Reference Key: def072d8-5909-4709-9365-9a244a488e02 Device: HUAWEI honor FRD-AL10 FRD-AL10 Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5 Durations: watch=5011ms, gc=173ms, heap dump=1862ms, analysis=103468ms Details: Class android.gestureboost.GestureBoostManager static sGestureBoostManager = android.gestureboost.GestureBoostManager@3172306 (0x12e88e50) static SWITCH_GESTURE_BOOST = 4 static $classOverhead = byte[652]@318563329 (0x12fce401) static TAG = java.lang.String@1896298 (0x71073638) static GESTURE_BOOST_CODE = 10001 static RMS_EXT_CODE_BASE = 10000 Instance of android.gestureboost.GestureBoostManager static sGestureBoostManager = android.gestureboost.GestureBoostManager@3172306 (0x12e88e50) static SWITCH_GESTURE_BOOST = 4 static $classOverhead = byte[652]@318563329 (0x12fce401) static TAG = java.lang.String@1896298 (0x71073638) static GESTURE_BOOST_CODE = 10001 static RMS_EXT_CODE_BASE = 10000 mAwareService = null mContext = com.zxsd.shoppingguide.activity.AggregationEntryActivity@3151232 (0x12c86600) mGestureBoostflag = false shadow$klass = android.gestureboost.GestureBoostManager shadow$monitor = 0 Instance of com.zxsd.shoppingguide.activity.AggregationEntryActivity static $change = null static $classOverhead = byte[4544]@316317697 (0x12daa001) static serialVersionUID = 0 appBarLayout = android.support.design.widget.AppBarLayout@3208130 (0x131f3800) btnLeft = android.support.v7.widget.AppCompatTextView@3211796 (0x1324d000) btnRight = android.support.v7.widget.AppCompatTextView@3212390 (0x1325b800) btnRightTwo = android.support.v7.widget.AppCompatTextView@3212267 (0x13258800) bundle = android.os.Bundle@3205945 (0x131be280) container = android.widget.FrameLayout@3214438 (0x1328d800) fragment = com.zxsd.shoppingguide.fragment.AggregationEntryFragment@3151228 (0x12c864c0) imgBack = android.support.v7.widget.AppCompatImageButton@3211827 (0x1324dc00) imgMore = android.support.v7.widget.AppCompatImageButton@3212369 (0x1325b000) layoutId = 2130968603 toolbar = android.support.v7.widget.Toolbar@3211560 (0x13247400) toolbarTitle = android.support.v7.widget.AppCompatTextView@3214479 (0x1328e800) mDelegate = android.support.v7.app.AppCompatDelegateImplN@3148888 (0x12c4d280) mEatKeyUpEvent = false mResources = null mThemeId = 2131361857 mCreated = true mFragments = android.support.v4.app.FragmentController@3164159 (0x12dc1fc0) mHandler = android.support.v4.app.FragmentActivity$1@319517120 (0x130b71c0) mNextCandidateRequestIndex = 0 mOptionsMenuInvalidated = false mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@3205947 (0x131be340) mReallyStopped = true mRequestedPermissionsFromFragment = false mResumed = false mRetaining = false mStopped = true mStartedActivityFromFragment = false mStartedIntentSenderFromFragment = false mExtraDataMap = android.support.v4.util.SimpleArrayMap@3204282 (0x131958c8) mActionBar = null mActionModeTypeStarting = 0 mActivityInfo = android.content.pm.ActivityInfo@3148902 (0x12c4d820) mActivityTransitionState = android.app.ActivityTransitionState@3191331 (0x130595b8) mApplication = com.zxsd.shoppingguide.base.App@3146104 (0x12c09308) mCalled = true mChangeCanvasToTranslucent = false mChangingConfigurations = false mComponent = android.content.ComponentName@3162928 (0x12da3f00) mConfigChangeFlags = 0 mCurrentConfig = android.content.res.Configuration@3158405 (0x12d35848) mCustActivity = android.app.HwCustActivityImpl@3204282 (0x131958f8) mDecor = null mDefaultKeyMode = 0 mDefaultKeySsb = null mDestroyed = true mDoReportFullyDrawn = false mEatKeyUpEvent = false mEmbeddedID = null mEnableDefaultActionBarUp = false mEnterTransitionListener = android.app.SharedElementCallback$1@1903589792 (0x717679a0) mExitTransitionListener = android.app.SharedElementCallback$1@1903589792 (0x717679a0) mFinished = true mFragments = android.app.FragmentController@3153430 (0x12cbc0d0) mHandler = android.os.Handler@3195173 (0x130b72c0) mHasCurrentPermissionsRequest = false mIdent = 132658027 mInstanceTracker = android.os.StrictMode$InstanceTracker@315343040 (0x12cbc0c0) mInstrumentation = android.app.Instrumentation@3148126 (0x12c3a8f8) mIntent = android.content.Intent@3168869 (0x12e34fc0) mIsFullFlag = false mLastNonConfigurationInstances = null mMainThread = android.app.ActivityThread@3147206 (0x12c24160) mManagedCursors = java.util.ArrayList@3204284 (0x131959a0) mManagedDialogs = null mMenuInflater = null mParent = null mReferrer = java.lang.String@3168862 (0x12e34cc0) mResultCode = 0 mResultData = null mResumed = false mSearchEvent = null mSearchManager = null mStartedActivity = false mStopped = true mTaskDescription = android.app.ActivityManager$TaskDescription@319517344 (0x130b72a0) mTemporaryPause = false mTitle = java.lang.String@1894097 (0x70e5a400) mTitleColor = 0 mTitleReady = true mToken = android.os.BinderProxy@3197227 (0x130e9500) mTranslucentCallback = null mUiThread = java.lang.Thread@2021072 (0x78771dd0) mVisibleBehind = false mVisibleFromClient = true mVisibleFromServer = true mVoiceInteractor = null mWindow = com.android.internal.policy.HwPhoneWindow@3167633 (0x12e16ce0) mWindowAdded = true mWindowManager = android.view.WindowManagerImpl@3198605 (0x1310af60) mInflater = com.android.internal.policy.HwPhoneLayoutInflater@3170012 (0x12e50e20) mOverrideConfiguration = null mResources = android.content.res.HwResources@3164745 (0x12dd04c0) mTheme = android.content.res.Resources$Theme@316415648 (0x12dc1ea0) mThemeResource = 2131361857 mBase = android.app.ContextImpl@3148388 (0x12c40f20) shadow$klass = com.zxsd.shoppingguide.activity.AggregationEntryActivity shadow$monitor = 1073743756 Excluded Refs: Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always) Thread:FinalizerWatchdogDaemon (always) Thread:main (always) Thread:LeakCanary-Heap-Dump (always) Class:java.lang.ref.WeakReference (always) Class:java.lang.ref.SoftReference (always) Class:java.lang.ref.PhantomReference (always) Class:java.lang.ref.Finalizer (always) Class:java.lang.ref.FinalizerReference (always) ![图片说明](https://img-ask.csdn.net/upload/201702/10/1486711588_136805.png) 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览