package com.xinghui.java.weakreference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** * https://github.com/square/leakcanary 中判断是否有内存泄漏的原理。 * * 原理:利用Android的Application的android.app.Application. * ActivityLifecycleCallbacks回调跟踪Activity的生命周期, * 在有android.app.Application.ActivityLifecycleCallbacks * .onActivityDestroyed(Activity)的时候把Activity对象添加到java.lang.ref.WeakReference中, * 当添加的对象变为弱引用就会添加到'java.lang.ref.WeakReference.WeakReference(T, * ReferenceQueue)'的第二个参数,弱引用队列 中。 利用 * 'WeakReference'的这一特性,在有onActivityDestroyed回调时,通过Runtime.getRuntime().gc()去通知系统gc, * 等待100ms * ,然后通过java.lang.System.runFinalization()方法,强制调用已经没有被引用的对象的java.lang.Object * .finalize()方法。 * 检查WeakReference的第二个参数ReferenceQueue队列,取出这个要检查的Activity是否已经变成弱引用。 * 如果存在于队列中,则说明:当前对象已变成弱引用,内存可以成功释放,也就不会有内存泄漏的问题 * 如果没有存在于队列中,则说明:当前对象目前还被其他对象保持持有关系(有其他对象指向要释放的对象),没有按预期释放当前对象,代表这个对象被泄漏了 * 这种情况积累下来,就会带来OutOfMemoryError。 * * @author xinghui * */ public class WeakReferenceTest { /** * 用于看验证效果。 * true: 最后有不能回收的内存 * false: 内存全部回收 */ private static final boolean CONFIG_LEAK = true; /** * 被保留的对象列表 */ static Set retainedObject; static ReferenceQueue queue = new ReferenceQueue(); public static void main(String[] args) { retainedObject = new CopyOnWriteArraySet(); ClassA mClassA = new ClassA(); ClassB mClassB = new ClassB(); mClassB.mClassA = mClassA; if (CONFIG_LEAK) { ClassC.container = mClassB; } /** * 当mClassA变成弱引用时,会添加到queue队列 * A WeakReference may be cleared and enqueued as soon as is known to be weakly-referenced. */ WeakReference re = new WeakReference(mClassA, queue); retainedObject.add(re); mClassA = null; mClassB = null; removeWeaklyReachableReferences("NONE"); /** * 1、Java中的System.gc()和Android中的System.gc()是有区别的; * @see 2、libcore.java.lang.ref.FinalizationTester.induceFinalization() * */ // System.gc() does not garbage collect every time. Runtime.gc() is // more likely to perfom a gc. Runtime.getRuntime().gc();// 通知系统gc try { /* * Hack. We don't have a programmatic way to wait for the reference queue * daemon to move references to the appropriate queues. */ Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError(); } System.runFinalization(); // 强制调用*已经没有被引用的对象*的finalize方法 removeWeaklyReachableReferences("runFinalization"); if (retainedObject.size() > 0) { for (Object o : retainedObject) { System.out.println("++++++++++ retained Object " + o); } } else { System.out.println("------------NO retained Object"); } } private static void removeWeaklyReachableReferences(String TAG) { Reference ref; System.out.println(TAG + " start "); while ((ref = queue.poll()) != null) { retainedObject.remove(ref); System.out.println("removed " + ref + " after " + TAG); } } static class ClassA { } static class ClassB { ClassA mClassA; } static class ClassC { static ClassB container; } }