这篇文章中介绍了内存泄露检测的原理:Reference、ReferenceQueue
三个核心知识点
1、Reference与ReferenceQueue
@Test
public void testQueue()
{
A a=new A();
ReferenceQueue queue=new ReferenceQueue();
WeakReference reference=new WeakReference(a,queue);
a=null;
Runtime.getRuntime().gc();
System.runFinalization();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Reference poll=null;
while ((poll=queue.poll())!=null)
{
System.out.println(poll.toString());
}
}
输出结果:
修改代码:
//a=null;
测试结果:
结论:
如果一个对象只是弱可达的(就是说没有强引用指向它,只有弱引用指向它,那么在gc时这个对象就是可回收的),那么在gc时,这个对象就会被回收,并且会将这个对象的弱引用放到queue中
leakcanary核心原理
以Activity为例,在Activity的onDestroy生命周期方法中,首先为这个Activity生成一个唯一的uuid,然后将这个uuid添加到一个set集合中,然后为这个Activity新建一个含有queue的WeakReference,那么如果说发生gc时,这个Activity被回收了,那么这个queue中就有数据,如果没有被回收,那么这个queue中就没有数据,这个Activity可能就发生了内存泄露;
下一步就是判断有没有发生内存泄露,具体做法是遍历这个queue然后根据对应关系移除set中的元素
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
遍历完成之后,set中存在的元素就是可能发生内存泄露的对象,为了判断的准确性;第三部再次调用gc;第四步再次重复上面的循环;最后如果说这个set集合中还存在这个reference的可以那么就可以确定这个对象发生了内存泄露
2、如何统一监测Activity、Fragment、普通对象的生命周期
Activity
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
Fragment
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
图片地址:https://juejin.im/post/5c72af2af265da2de165a624
根据源码leakcanary只能 检测Activity、Fragment的泄露情况,无法检测其他的对象
3、Runtime.getRuntime().gc()与System.gc()
public static void gc() {
boolean shouldRunGC;
synchronized (LOCK) {
shouldRunGC = justRanFinalization;
if (shouldRunGC) {
justRanFinalization = false;
} else {
runGC = true;
}
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
}
justRanFinalization这个值是调用runFinalization方法来设置的
public static void runFinalization() {
boolean shouldRunGC;
synchronized (LOCK) {
shouldRunGC = runGC;
runGC = false;
}
if (shouldRunGC) {
Runtime.getRuntime().gc();
}
Runtime.getRuntime().runFinalization();
synchronized (LOCK) {
justRanFinalization = true;
}
}
所以单纯调用System.gc()是不会触发Runtime.getRuntime().gc()的。但是会把这次尝试纪录下来,等到下次调用System.runFinalization()时,会先执行这个Runtime.getRuntime().gc()。