笔沫

有梦为马,随处可栖

  • 主页
  • 技术
  • 随想
  • 音乐
所有文章 其他

笔沫

有梦为马,随处可栖

  • 主页
  • 技术
  • 随想
  • 音乐

LeakCanary框架源码解析

2017-11-16

如果我们开发的程序,出现内存泄漏,导致程序奔溃,造成用户卸载APP。这样的结果,是我们不想见到的。作为一名向上的程序员,如何避免内存泄漏,这就成为必须要解决的问题。良心企业Square,开源了LeakCanary框架,可以轻松集成,让检测内存泄漏变得十分容易。

什么是内存泄漏?
内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

一、什么是LeakCanary?

LeakCanary 是一个检测内存泄露的开源类库。你可以在 debug 包种轻松检测内存泄露。

LeakCanary源码地址:https://github.com/square/leakcanary

二、LeakCanary源码解析

1.LeakCanary入口

1
2
3
4
5
6
7
8
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);//1.核心方法
}
}

进入LeakCanary类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Creates a {@link RefWatcher} that works out of the box, and starts watching activity
* references (on ICS+).
*/
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
/** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}

refWatcher()返回AndroidRefWatcherBuilder对象,listenerServiceClass、excludedRefs和buildAndInstall皆为
AndroidRefWatcherBuilder的方法。这里我们先看AndroidRefWatcherBuilder中的buildAndInstall的方法

1
2
3
4
5
6
7
8
9
10
11
/**
* Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
*/
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);//2.核心方法
}
return refWatcher;
}

这里创建了RefWatcher,并把其传给了ActivityRefWatcher。进入ActivityRefWatcher类

1
2
3
4
5
6
7
8
9
10
11
12
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
/**
* Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
* after they have been destroyed.
*/
public ActivityRefWatcher(Application application, RefWatcher refWatcher) {
this.application = checkNotNull(application, "application");
this.refWatcher = checkNotNull(refWatcher, "refWatcher");
}

创建ActivityRefWatcher类的对象,并且调用了watchActivities()方法,我们继续看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);//3.核心方法
}
};
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);//4.核心方法,设置监听所有Activity的生命周期
}

application.registerActivityLifecycleCallbacks(lifecycleCallbacks),设置监听了应用Activity的生命周期,可以监听所有Activity。当Activity调onDestory方法时,都会调 ActivityRefWatcher.this.onActivityDestroyed(activity),我们继续看

1
2
3
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);//5.核心方法
}

2.核心类RefWatcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Identical to {@link #watch(Object, String)} with an empty string reference name.
*
* @see #watch(Object, String)
*/
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);//6.核心方法
ensureGoneAsync(watchStartNanoTime, reference);//7.核心方法
}

这里对watchedReference即Activity对象建立了一个弱应用KeyedWeakReference,并且对KeyedWeakReference加了一个引用队列queue(ReferenceQueue)。当KeyedWeakReference对象可以回收时,会添加到ReferenceQueue中,我们继续

1
2
3
4
5
6
7
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);//8.核心方法
}
});
}

通过watchExecutor开启了一个线程,执行ensureGone。ensureGone方法可以说是LeakCanary框架最最核心的方法,核心原理都在这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();//i.回收弱引用和删除key
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {//ii.判断弱引用是否回收,若回收true,不存在泄漏
return DONE;
}
gcTrigger.runGc();//iii.手动GC
removeWeaklyReachableReferences();//iv.再次回收弱引用和删除key
if (!gone(reference)) {//v.再判断弱引用是否回收,若回收true,不存在泄漏,false则存在泄漏
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(//vi.分析Hprof文件,找出泄漏的最短
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}

其中,方法removeWeaklyReachableReferences()是回收弱引用及删除key,方法gone(reference)判断弱引用对象是否被回收。

如代码中的注释,通过多次判断Activity的弱应用是否被回收,判断Activity是否内存泄漏。如泄漏,生成Hprof文件,通过Square的haha开源库分析泄漏引用链,从而将其应用链传给界面展示出来,让开发者知道。

到此,LeakCanary原理分析就告一段落了。如果你还想知道LeakCanary是怎么找到泄漏引用链,并传给界面展示的,你还可以继续分析 heapdumpListener.analyze();入口地方AndroidRefWatcherBuilder中的listenerServiceClass的方法与DisplayLeakService类。

三、总结

通过阅读LeakCanary框架的源码,知LeakCanary框架原理还是比较简单的,主要就是通过Activity弱引用(KeyedWeakReference)是否被回收,来判断是否内存是否泄漏。

四、相关及参考文档

LeakCanary 中文使用说明

LeakCanary核心原理源码浅析

LeakCanary 原理浅析

leakcanary原理分析与AppsFly内存泄漏

十分钟理解Java中的弱引用

赏

感谢认可,么么哒

支付宝
微信
  • 技术
  • Android
  • 开源框架源码解析
  • LeakCanary框架源码分析

扫一扫,分享到微信

微信分享二维码
Butter Knife框架源码解析
Android Fragment使用小结及介绍
© 2019 笔沫
Hexo Theme Yilia by Litten
  • 所有文章
  • 其他

tag:

  • 技术
  • Android
  • Activity的启动模式
  • Android注解
  • Java反射机制
  • Java动态代理
  • Android基础
  • MVP
  • NDK
  • JNI C/C++
  • 开源框架
  • AS Gradle优化
  • 博客搭建
  • 设计模式
  • Fragment
  • Rxjava
  • Rxandroid
  • 响应式编程
  • 随想
  • 你本是一个肉体,是什么驱使你前行
  • 原创
  • 笔沫拾光
  • Java
  • Java基础
  • 王阳明心学
  • 中国历史
  • 人生的意义
  • 演讲
  • 执着的人是幸福的
  • 郭川
  • Android框架源码解析
  • 加解密算法
  • Binder通信机制
  • 开源框架源码解析
  • LeakCanary框架源码分析
  • Java集合类
  • 转载
  • Http文件断点续传
  • Logger框架源码解析
  • Android应用程序入口源码解析
  • DecorView绘制流程
  • Android消息机制源码解析
  • Activity启动流程
  • Butterknife框架源码解析
  • 我之存在,因为有你
  • 霍华德*舒尔茨
  • Android相机
  • Camera
  • 悬浮窗
  • WindowManager

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 风光摄影
  • 星空摄影
  • 人像摄影
  • 学习站点