LeakCanary github地址 https://github.com/square/leakcanary
LeakCanary分析实例
1. Activity内存泄漏示例
LeakApplication.java
public class LeakApplication extends Application {
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
// 1. 判断是否是LeakCanary所在进程
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
// 2. 核心代码
LeakCanary.install(this);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 该线程为成员内部类,其包含MainActivity实例
new MyThread().start();
}
class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
// 2. 延时5分钟
TimeUnit.MINUTES.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
启动界面后返回退出即出现内存泄漏,点击LeakCanary发出的通知,如下图
Activity内存泄漏
图中标红色波浪线的为可能导致内存泄漏对象,此题通过this$0即可判断是子线程持有MainActivity引用,Activity退出后理应被GC回收,但由于子线程持有其引用导致无法释放,从而内存泄漏。
2. Service内存泄漏示例
LeakCanary默认会获取Activity的内存泄漏,对于检测Service需要手动在Service的onDestory()中进行监测,代码如下:
LeakApplication
public class LeakApplication extends Application {
// 1. RefWatcher用来监测目标对象
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = setupLeakCanary();
}
// 2. 返回RefWatcher对象
private RefWatcher setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
}
return LeakCanary.install(this);
}
// 3. 给整个应用提供获取接口
public static RefWatcher getRefWatcher(Context context) {
LeakApplication leakApplication = (LeakApplication) context.getApplicationContext();
return leakApplication.refWatcher;
}
}
MyService
public class MyService extends Service {
public static final String TAG = "wangyannan";
// 1. 静态变量
private static Context sContext;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
// 2. 当前Service对象赋值给静态变量context
sContext = this;
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
// 3. 得到RefWatcher对象
RefWatcher refWatcher = LeakApplication.getRefWatcher(this);
// 4. 监听Service对象是否内存泄漏
refWatcher.watch(this);
Log.i(TAG, "onDestroy: ");
}
}
在MainActivity中启动Service,然后停止Service,LeakCanary监测到内存泄漏,如下图
Service内存泄漏
红色波浪线中MyService.sContext比较明显,可见静态引用出现的问题,稍加判断就可以知道是包含了Service对象而导致其没有释放,故出现内存泄漏。
LeakCanary源码分析 绝对简单易懂
能检测Activity内存泄漏主要就是自定义Application中的该方法调用
MyApplication.java
LeakCanary.install(this);
LeakCanary.java
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall(); // RefWatcher通过buildAndInstall()返回
}
AndroidRefWatcherBuilder.java
public @NonNull RefWatcher buildAndInstall() {
...
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (watchActivities) {
// 1. 用于Activity的onDestory检测
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
// 2. 用于Fragment的onDestory检测
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
这里看Activity的onDestory是如何被检测的
ActivityRefWatcher.java
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
// 1.
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
// 2.
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
// 3. RefWatcher监控activity是否内存泄漏
refWatcher.watch(activity);
}
};
注释1处会通过Application的registerActivityLifecycleCallbacks方法注册监听Activity的生命周期,参数会传入一个callback,当Activity对应生命周期执行时,Callback中与Activity生命周期对应的方法也会走。例如Activity执行onDestory()方法时,就会回调Callback中对应方法,从而执行了上面注释2的onActivityDestroyed方法.
RefWatcher.java
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
// 如果为NULL就抛出异常
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
// 生成唯一的key值
String key = UUID.randomUUID().toString();
// 加入到retainedKeys集合
retainedKeys.add(key);
// 创建弱引用与activity关联,queue为ReferenceQueue
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 异步执行,最终用来判断
ensureGoneAsync(watchStartNanoTime, reference);
}
ensureGoneAsync最终会执行ensureGone方法
RefWatcher.java
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
// 1. 尝试移除key(如果Activity被回收了,该key会从retainedKeys集合中被删除)
removeWeaklyReachableReferences();
// 2. 判断key是否已经被移除,移除返回true
if (gone(reference)) {
return DONE;
}
// 3. 没有移除则主动触发一次GC
gcTrigger.runGc();
// 4. 效果和注释1相同
removeWeaklyReachableReferences();
// 5. 如果依然key依然没有从retainedKeys集合中移除,则说明出现内存泄漏
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
// 6. dump 堆hprof文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
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();
// 7. 分析出泄漏的地方
heapdumpListener.analyze(heapDump);
}
return DONE;
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
// queue.poll()有值则说明WeakReference中的Activity被回收了,如果Activity的强引用还存在,那么WeakReference包含的activity不会被回收,queue.poll还是null
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
// 如果被回收,则移除
retainedKeys.remove(ref.key);
}
}
整体工作也就清除了。总结下过程如下。
1. 监听Activity或者Fragment在onDestory方法中
application.registerActivityLifecycleCallbacks 该方法可以监听Activity的onDestory方法
2. 确定内存是否泄漏
- Activity onDestory时,将Activity通过WeakReference引用并绑定一个通过
UUID.randomUUID().toString()
生成唯一的Key,该key交由一个Set集合维护,该WeakReference会传入一个ReferenceQueue。 - Activity执行onDestory时默认会触发一次GC,那么WeakRerence中activity就会被回收,并放入ReferenceQueue。LeakCanary就是通过判断ReferenceQueue是否包含activity对象,有则强转为WeakRerference,再从Set集合中移除WeakRerfernce的key。
- 如果key在Set集合中已不存在,表明该Activity被回收,如果两次GC,该key在Set中还没有被移除,则说明存在内存泄漏。
3. dump hprof
实质通过Debug.dumpHprofData()
dump堆信息
4. 分析内存泄漏位置
LeakCanary面试题
1. Activity检测机制是什么?
通过application.registerActivityLifecycleCallbacks来绑定Activity生命周期的监听,从而监控所有Activity; 在Activity执行onDestroy时,开始检测当前页面是否存在内存泄漏,并分析结果。因此,如果想要在不同的地方都需要检测是否存在内存泄漏,需要手动添加。
2. 内存泄漏检测机制是什么?
KeyedWeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收, 手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。
网友评论