本文已在公众号郭霖原创发布。版权归微信公众号郭霖全部,未经许可,不得以任何形式转载,微信文章地址:java
1.Activity onDestroy以后将它放在一个WeakReference。git
2.这个WeakReference关联到一个ReferenceQueue。github
3.查看ReferenceQueue是否存在Activity的引用。算法
4.若是该Activity泄露了,Dump出heap信息,而后再去分析泄露路径。微信
软引用&弱引用并发
软引用(SoftReference)和弱引用(WeakReference)都继承Reference。app
软引用:当一个对象只有软引用存在时,系统内存不足时会被gc回收。dom
弱引用:当一个对象只有弱引用存在时,随时被gc回收。异步
对象被回收后,Java虚拟机就会把这个引用加入到与之关联的引用队列中。
//java.lang.ref.Reference.java
public abstract class Reference<T> {
...
volatile T referent;
final ReferenceQueue<? super T> queue;
...
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = queue;
}
...
/** * <p> This method is invoked only by Java code; when the garbage collector * enqueues references it does so directly, without invoking this method. *(这个方法仅会被java代码调用,当GC时会直接把referent添加到queue引用队列) * */
public boolean enqueue() {
return queue != null && queue.enqueue(this);
}
}
复制代码
简单实例:
public class RefTest {
public static void main(String[] args) {
//user为强引用
User user = new User("张三", 18);
//建立弱引用并关联引用队列
ReferenceQueue<User> queue = new ReferenceQueue<>();
WeakReference<User> weakReference = new WeakReference<User>(user,queue);
//置空强引用,触发GC
user=null;
Runtime.getRuntime().gc();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//强制系统回收已经没有强引用的对象
System.runFinalization();
WeakReference pollRef=null;
//弹出对列的引弱用
while ((pollRef = (WeakReference) queue.poll()) != null) {
System.out.println("pollRef的内存地址:"+pollRef.toString());
System.out.println("pollRef等于weakReference?:"+weakReference.equals(pollRef));
}
}
}
复制代码
运行结果(注意内存地址是由JVM分配的,故可能有所差别):
pollRef的内存地址:java.lang.ref.WeakReference@610455d6 pollRef等于weakReference?:true
Java垃圾回收(GC) 在Java中垃圾判断方法是 可达性分析算法,这个算法的基本思路是经过一系列的"GC Root"的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Root没有任何引用链相连时,则证实此对象是不可用的。 GC Root的对象包括如下几种:
一、虚拟机栈中引用的对象。
二、方法区中类静态属性引用的对象。
三、方法区中常量引用的对象。
四、本地方法栈中JNI引用的对象。
就算一个对象,经过可达性分析算法分析后,发现其是『不可达』的,也并非非回收不可的。 通常状况下,要宣告一个对象死亡,至少要通过两次标记过程:
一、通过可达性分析后,一个对象并无与GC Root关联的引用链,将会被第一次标记和筛选。筛选条件是此对象有没有必要执行finalize()方法。若是对象没有覆盖finalize()方法,或者已经执行过了。那就认为他能够回收了。若是有必要执行finalize()方法,那么将会把这个对象放置到F-Queue的队列中,等待执行。
二、虚拟机会创建一个低优先级的Finalizer线程执行F-Queue里面的对象的finalize()方法。若是对象在finalize()方法中能够『拯救』本身,那么将不会被回收,不然,他将被移入一个即将被回收的ReferenceQueue。
首先在gradle引入依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
LeakCanary在Application初始化,代码以下:
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//检查当前进程是否在HeapAnalyzerService所属进程
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
//安装泄露检测
LeakCanary.install(this);
}
}
复制代码
首先调用isInAnalyzerProcess()来判断当前进程是否为HeapAnalyzerService运行的进程。这个方法回调用LeakCanaryInternals.isInServiceProcess()经过PackageManager、ActivityManager以及android.os.Process来判断当前进程是否为HeapAnalyzerService运行的进程,这样子作的目的是不影响主进程的使用。下面是debug生成的AndroidManifest.xml,能够在run应用以后再app/build/intermediates/instant_app_manifest/debug/查看。
<!-- 这个是LeakCanary分析泄露的Service -->
<service android:name="com.squareup.leakcanary.internal.HeapAnalyzerService" android:enabled="false" android:process=":leakcanary" />
<!-- 这个是LeakCanary展现泄露的Service -->
<service android:name="com.squareup.leakcanary.DisplayLeakService" android:enabled="false" android:process=":leakcanary" />
<!-- 这个是LeakCanary显示泄露信息的Activity,由于被设置为Launcher,并设置了金丝雀的icon,因此使用LeakCanar才会在桌面上生成icon入口的缘由。 -->
<activity android:name="com.squareup.leakcanary.internal.DisplayLeakActivity" android:enabled="false" android:icon="@mipmap/leak_canary_icon" android:label="@string/leak_canary_display_activity_label" android:process=":leakcanary" android:taskAffinity="com.squareup.leakcanary.com.pengguanming.studydemo" android:theme="@style/leak_canary_LeakCanary.Base" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
复制代码
注意上面关于LeakCanary的组件的android:enabled=false,android:enabled表示是否可以实例化该应用程序的组件,若是为true,每一个组件的enabled属性决定那个组件是否能够被 enabled。若是为false,它覆盖组件指定的值;全部组件都是disabled。
这里回过来看install()方法,它调用返回RefWatcher对象,这个对象经过Application注册了Activity的生命周期监听、经过Activity注册监听Fragment的生命周期,且用到了leakcanary-support-fragment包,兼容了v4的fragment。
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())//默认过滤一些系统bug形成的内存泄露
.buildAndInstall();
}
复制代码
值得注意的是LeakCanary.refWatcher(application)返回的是一个AndroidRefWatcherBuilder对象,下面看看它的buildAndInstall():
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
//判断是否开启内存泄露提示
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
//判断是否开启Activity内存泄露检测
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
//判断是否开启Fragment内存泄露检测
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
//复制给全局静态变量,防止二次调用
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
复制代码
首先判断refWatcher是否已被初始,build()建立RefWatcher对象,而后判断这对象是否是RefWatch是否可用,若可用则执行下面操做:
1.检测DisplayLeakActivity是否可用,若不可用则调用LeakCanaryInternals.setEnabledAsync()调用AsyncTask.THREAD_POOL_EXECUTOR这个静态异步线程池执行PackageManager.setComponentEnabledSetting()将这个Activity设置为可用。PackageManager.setComponentEnabledSetting()是IPC的阻塞操做,故做异步处理。
2.判断是否开启Activity内存泄露检测,若没则调用ActivityRefWatcher.install()会建立ActivityRefWatcher对象而后经过Application注册Activity的生命周期监听。
3.判断是否开启Fragment内存泄露检测,若没则调用FragmentRefWatcher.Helper.install(),经过Activity注册监听Fragment的生命周期,且用到了leakcanary-support-fragment包,兼容了v4的fragment。
上面提到Activity和Fragment的生命周期监听,这里首先看看监听Activity的代码:
//ActivityRefWatcher.java
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
复制代码
上面经过Application注册了ActivityRefWatcher成员lifecycleCallbacks监听Activity生命周期回调,lifecycleCallbacks是继承Application.ActivityLifecycleCallbacks的抽想类,这样就完成了Activity销毁时监听监听回调,并执行Activity内存泄露检测操做。下面看看它的代码实现:
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
复制代码
如今再来看看监听Fragment的代码:
//FragmentRefWatcher.Helper.install()
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
if (SDK_INT >= O) {
//添加兼容android O的Fragment泄露检测
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
//添加经过反射构造兼容android O如下的Fragment泄露检测
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
//注册监听Activity生命周期回调
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
复制代码
上面代码实现和监听Activity生命周期有所差别,首先建立FragmentRefWatcher的容器,判断SDK版本是否大于等于Android O,若大于等于则建立AndroidOFragmentRefWatche加入容器,而后在经过反射建立SupportFragmentRefWatcher也加入到容器中,以后建立Helper并经过Application注册了Helper成员activityLifecycleCallbacks监听Activity的生命周期,但它仅监听Activity的建立,下面来看看它的代码:
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
复制代码
经过上面的代码能够知道,这个activityLifecycleCallbacks在Activity建立时,遍历以前 FragmentRefWatcher的list并调用实例中的watchFragments(),list只有两个对象:SupportFragmentRefWatcher(兼容android O如下)和AndroidOFragmentRefWatcher(兼容android O+引入了fragment的生命周期,用户不须要在onDestroy中自行调用),它们两实现差很少,这里只看SupportFragmentRefWatcher代码:
class SupportFragmentRefWatcher implements FragmentRefWatcher {
...
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
//在Fragment的View销毁时检测泄露
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
//在Fragment实例销毁时检测泄露
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
if (activity instanceof FragmentActivity) {
//注册supportFragment生命周期监听
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
}
复制代码
到watchFragments()这里发现这才和监听Activity生命周期类似,经过Activity获取FragmentManager注册成员FragmentLifecycleCallbacks监听Fragment销毁生命周期,而后进行内存泄露检测操做。
在【2.2】小节里得知在Activity和Fragment销毁时都会拿调用RefWatch.watch()方法,在此以前先了解一下RefWatch对象:
/
*@param WatchExecutor 用于执行检测内存的线程控制器
*@param DebuggerControl 查询是否正在debug中,若正在debug会不执行内存泄露的检测判断
*@param GcTrigger 用来触发垃圾回收的,上面的线程控制器5s后观察有泄露,不算泄露,必须垃圾回收后,再去观察一次。因此最多会观察两次。第一次是5s后观察,第二次是5s后在垃圾回收后观察
*@param HeapDump dump出内存泄露的heap堆文件
*@param HeapDump.Listener 产生heap文件的回调
*@param ExcludeRefs 过滤掉的内存泄露
/
RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
HeapDumper heapDumper, HeapDump.Listener heapdumpListener, ExcludedRefs excludedRefs) {
this.watchExecutor = checkNotNull(watchExecutor, "watchExecutor");
this.debuggerControl = checkNotNull(debuggerControl, "debuggerControl");
this.gcTrigger = checkNotNull(gcTrigger, "gcTrigger");
this.heapDumper = checkNotNull(heapDumper, "heapDumper");
this.heapdumpListener = checkNotNull(heapdumpListener, "heapdumpListener");
this.excludedRefs = checkNotNull(excludedRefs, "excludedRefs");
//持有待检测内存泄露引用的key,这里使用CopyOnWriteArraySet解决并发读写问题
retainedKeys = new CopyOnWriteArraySet<>();
//引用队列,弱引用或软引用被gc回收后会达到此队列
queue = new ReferenceQueue<>();
}
复制代码
上面代码让咱们有个大概认识,这小节只关注retainedKeys和queue成员变量就够了,其余成员变量会在以后分析到,咱们先看看RefWatch.watch():
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
//检查泄露开始时间
final long watchStartNanoTime = System.nanoTime();
//为引用生成惟一的key
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
//建立一个弱引用
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
//开启异步线程分析弱引用
ensureGoneAsync(watchStartNanoTime, reference);
}
复制代码
首先会判断当前RefWatch是否可用,而后对检测对象和对应的引用标识字符串判空,生成检查泄露开始时间,接下来为引用生成惟一的key并添加到retainedKeys,而后建立KeyedWeakReference对象,开启异步线程分析弱引用。这里看看KeyedWeakReference代码:
final class KeyedWeakReference extends WeakReference<Object> {
public final String key;
public final String name;
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue<Object> referenceQueue) {
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
复制代码
从上面能够看出KeyedWeakReference封装了引用惟一的key和引用标识字符串,并将检测对象的弱引用关联到RefWatch的引用队列。
从上一小节看到RefWatch.ensureGoneAsync(),下面看看代码实现:
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
复制代码
有上面代码看出将泄露检测转移到WatchExecutor,而它的实现是AndroidWatchExecutor,接下来看看它的代码:
public final class AndroidWatchExecutor implements WatchExecutor {
static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";//子线程名称
private final Handler mainHandler;//绑定主线程的Handler
private final Handler backgroundHandler;//绑定子线程的Handler
private final long initialDelayMillis;//初始化延迟的毫秒数
private final long maxBackoffFactor;//最大失败重试数
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
//主线程操做
waitForIdle(retryable, 0);
} else {
//子线程切换到主线程操做
postWaitForIdle(retryable, 0);
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
//调用mainHandler将操做切换到主线程
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.(这里必定要在主线程调用)
//添加主线程空闲回调监听执行postToBackgroundWithDelay()
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
//指数2增加的重试数,最大值不超过maxBackoffFactor
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
//延迟执行的时间
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
//切换到后台子线程执行
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
//若是返回值为RETRY时,会再次延时再次尝试执行。延迟初始时间为5s,之后每次重试时间x2
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
}
复制代码
1.AndroidWatchExecutor的构造方法会建立主线程和和后台子线的Handler,并初始化切换到子线程Handler的最大延迟时间maxBackoffFactor和初始化时间initialDelayMillis,maxBackoffFactor值为Long.MAX_VALUE / initialDelayMillis,initialDelayMillis值为5(由于AndroidWatchExecutor是被AndroidRefWatcherBuilder.defaultWatchExecutor()建立)。
2.execute()里的不管waitForIdle()仍是postWaitForIdle(),都是须要切换到主线程执行,并且postWaitForIdle()最终切换到waitForIdle()。
3.waitForIdle()监听主线程Handler消息队列空闲,只要主线程空闲就会执行postToBackgroundWithDelay()操做。
4.postToBackgroundWithDelay()会就首先根据重试次数计算延迟执行的时间,而后延迟切换到子线程的Handler操做,若是Retryable.run()返回Result.RETRY时,会执行postWaitForIdle()继续等待主线程再次空闲。
5.Retryable.run()在以前RefWatcher.ensureGoneAsync()被调用,而Retryable.run()的返回值由RefWatcher.ensureGoneAsync()返回,ensureGoneAsync()在如下的状况会返回Result.RETRY:
A.debug模式启动时。
B.建立dumpHeap文件失败时(见【2.5.6泄漏判断】)。
C.5s后UI线程未空闲时(见【2.5.6泄漏判断】)。
RefWatch.ensureGone()主要是判断内存泄露,下面看看它的实现:
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
//计算watch方法到gc垃圾回收的时长
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//尝试移除已经到达引用队列的弱引用
removeWeaklyReachableReferences();
//判断是否在debug
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks. (debug能够创造错误的内存泄露)
return RETRY;
}
if (gone(reference)) {//若当前对象已经可达了,即不会形成你内存泄露
return DONE;
}
//手动gc,确保引用对象是否真的被回收了。由于在dump内存信息以前提示内存泄露的时候,但愿系统通过充分gc垃圾回收,而不存在任何的误判,对leakcanary容错性的考虑
gcTrigger.runGc();
//清除已经到达引用队列的弱引用
removeWeaklyReachableReferences();
if (!gone(reference)) {//此时对象还没到达对列,表明已经内存泄露了
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//dump出内存泄露的heap文件,这里可能触发GC
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.(不能dump heap堆文件)
return RETRY;
}
//dump heap文件的时间计算
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
//真正分析内存泄露以及路径
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
复制代码
上面代码执行以下:
1.在以前的RefWatch.watch()里生成了检测引用对象的UUID的key并关联了弱引用KeyedWeakReference对象,弱引用与ReferenceQueue联合使用,若是弱引用关联的对象被回收,则会把这个弱引用加入到ReferenceQueue中。
2.removeWeaklyReachableReferences()尝试移除已经到达引用队列的弱引用,会对应删除KeyedWeakReference的数据。若是这个引用继续存在,那么就说明没有被回收。
3.gone()查看retainedKeys是否包含KeyedWeakReference的惟一key。
4.手动触发GC操做,gcTrigger中封装了gc操做的代码,首先会调用Runtime.getRuntime().gc()以触发系统gc操做,而后当先后台子线程sleep 100毫秒,最后调用System.runFinalization()强制系统回收没有引用的队形,这样子确保引用对象是否真的被回收了。由于在dump内存信息以前提示内存泄露的时候,但愿系统通过充分gc垃圾回收,而不存在任何的误判,这是对leakcanary容错性的考虑。
5.再次移除不可达引用,若是引用存在了,都没有被回收则断定内存泄露。
6.断定泄露后调用AndroidHeapDumper.dump(),首先经过LeakDirectoryProvider的实现类DefaultLeakDirectoryProvider为.prof文件建立File,若文件建立失败也会返回RETRY_LATER让以前的AndroidWatchExecutor.execute()等待下次主线程空闲执行,它最多建立7个文件,数目超事后,删除最先建立的文件,全部文件默认保存在Download文件夹下;而后利用CountDownLatch阻塞当先后台子线程5秒并监听主线程是否空闲,若不空闲则返回RETRY_LATER,若空闲则调用android.os.Debug.dumpHprofData()生成.prof文件。
7.调用HeapDump.Listener分析刚生成的.prof文件。
既然判断了内存泄露,那么接下来泄露信息分析,找出泄露的对象的引用路径。 ServiceHeapDumpListener是HeapDump.Listener的实现类,在RefWatch.ensureGone()中调用了它的analyze():
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
复制代码
HeapAnalyzerService继承抽象类ForegroundService,而ForegroundService继承IntentService,它的runAnalysis()会回调onHandleIntent():
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
...
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
//开启Service实例化
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
//启动前台服务
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent);
}
...
@Override
protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
//建立过滤掉特定内存泄露的heapAnalyzer
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//检查内存泄露
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
//回调结果显示
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
}
复制代码
HeapAnalyzer的checkForLeak():是leakcannary最核心的方法
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,@NonNull String referenceKey,boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
//封装成MemoryMappedFileBuffer对象
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
//传进HprofParser解析器
HprofParser parser = new HprofParser(buffer);
//装换成Snapshot内存快照对象
listener.onProgressUpdate(PARSING_HEAP_DUMP);
Snapshot snapshot = parser.parse();
//去重复路径结果
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
deduplicateGcRoots(snapshot);
//根据要检测的key查询解析结果中是否有须要的对象
listener.onProgressUpdate(FINDING_LEAKING_REF);
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.(误报,弱引用在对应的KeyedWeakReference检查和heap堆文件期间被清除)
if (leakingRef == null) {//引用对象不存在,说明gc时被清除
String className = leakingRef.getClassObj().getClassName();
return noLeak(className, since(analysisStartNanoTime));
}
//找出内存泄露的路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
复制代码
1.引入HAHA库(一个heap prof堆文件分析库),将hprof文件解析成内存快照Snapshot对象进行分析。
2.deduplicateGcRoots()使用jetBrains的THashMap(THashMap的内存占用量比HashMap小)作中转,去掉snapshot中GCRoot的重复路径,以减小内存压力。
3.找出泄露对象并找出泄露对象的最短路劲。
HeapAnalyzer.findLeakingReference()主要做用是找出泄露对象:
private Instance findLeakingReference(String key, Snapshot snapshot) {
//经过查找的弱引用建立ClassOb对象
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
List<String> keysFound = new ArrayList<>();
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
String keyCandidate = asString(fieldValue(values, "key"));
if (keyCandidate.equals(key)) {//key值相等则找到内存泄露对象
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}
复制代码
1.在snpashot内存快照中找到泄露对象的弱引用。
2.遍历这个对象全部实例。
3.若这个key值和最开始定义封装KeyedWeakReference的key值相同,那么返回这个泄露对象。
HeapAnalyer.findLeakTract()主要做用是找到最短泄露路径,计算泄露大小做为结果反馈:
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {
//分析snpashot内存快照找出内存泄露的点,判断依据是GCRoot
listener.onProgressUpdate(FINDING_SHORTEST_PATH);
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
//查找泄露的最短引用链,这里只关注GCRoot的两种类型:1.静态;2.被其余线程使用而且其余线程正在运行没有结束
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
String className = leakingRef.getClassObj().getClassName();
// False alarm, no strong reference path to GC Roots.(误报,没有强引用的GCRoots)
if (result.leakingNode == null) {
return noLeak(className, since(analysisStartNanoTime));
}
listener.onProgressUpdate(BUILDING_LEAK_TRACE);
//生成内存泄露调用栈
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
//计算内存泄露空间大小
long retainedSize;
if (computeRetainedSize) {
listener.onProgressUpdate(COMPUTING_DOMINATORS);
// Side effect: computes retained size.
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
retainedSize = leakingInstance.getTotalRetainedSize();
// TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
复制代码
在【2.6泄露信息分析】里HeapAnalyzerService.onHandleIntentInForeground():
...
//HeapAnalyzerService.java
//回调结果显示
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
复制代码
AbstractAnalysisResultService.sendResultToListener()经过调用AnalyzedHeap.save()将以前的.prof文件保存为.result文件,而后将.result文件路径经过intent传到DisplayLeakService(继承AbstractAnalysisResultService,而AbstractAnalysisResultService是继承ForegroundService),这里首先调用父类AbstractAnalysisResultService.onHandleIntentInForeground()经过AnalyzedHeap.load()将.result文件生成AnalyzedHeap对象,以后调用onHeapAnalyzed()将AnalyzedHeap的信息经过Notification展现。
DisplayLeakActivity是平时用到的经过桌面入口进入的泄漏信息查看Activity在【见2.1】AndroidRefWatcherBuilder.buildAndInstall()被开启实例化
//DisplayLeakActivity.java
@Override protected void onResume() {
super.onResume();
LoadLeaks.load(this, getLeakDirectoryProvider(this));
}
复制代码
在onResume的时候使用了LoadLeaks(实现了Runnable接口),并传入一个Provider,这个Provider就是上面建立.result文件时所用到的DefaultLeakDirectoryProvider,而在load方法主要在线程池执行是读取.result文件,而后经过UI Handler将读取的信息更新ui中。
A.使用DownCountLatch同步主线程和子线程,见【2.5 泄漏判断】。
B.这里使用CopyOnWriteArraySet解决并发读写问题。
retainedKeys = new CopyOnWriteArraySet<>();
C.构建者模式,代码简洁、清新,链式调用建立对象,参考RefWatcherBuilder对象。
D.MessageQueue.addIdleHandler(IdleHandler handler),监听线程空闲。
E.手动GC,参考GCTrigger.runGc()。
F.Reference.watch()本质上是能够监控任意对象类型的,关键在于监控的时机,像activity、service、fragmen是有生命周期的,能够在ondestroy时开始监控,其余的对象类型用户能够选择合适的时机调用该方法进行监控。注意若是首页的Activity一直不销毁(onDestroy)那么将一直没法检测到首页的调用栈的内存泄漏。
G.借助AsyncTask.THREAD_POOL_EXECUTOR静态线程池执行异步任务。
LeakCanary的源码设计很是精妙,因为本人水平有限仅给各位提供参考,但愿可以抛砖引玉,若是有什么能够讨论的问题能够在评论区留言或联系本人。
参考: