网站制作工具 织梦,安康服务好的网络公司,泉州seo网站建设费用,怎么做网站赚钱吗一、背景
一般意义上而言#xff0c;Java/Android中的引用类型包括强引用、软引用、弱引用、虚引用。不同的引用类型具有各自适用的应用场景#xff0c;并与JVM的GC直接相关。
作为Java/Android中的引用类型之一#xff0c;WeakReference被大量的使用到系统源码、基础工具…一、背景
一般意义上而言Java/Android中的引用类型包括强引用、软引用、弱引用、虚引用。不同的引用类型具有各自适用的应用场景并与JVM的GC直接相关。
作为Java/Android中的引用类型之一WeakReference被大量的使用到系统源码、基础工具甚至具体的业务逻辑中。在解决需要异步使用目标对象实体、且又不影响目标对象实体的生命周期的场景中具有天然优势。同时还能进一步判断目标对象实体当前所处的GC阶段如当前是否GC roots可达亦或者已经被GC回收。 二、四种引用类型
2.1 强引用与GC可达
默认情况下我们直接指向对象的引用类型为强引用也是我们天天写代码必定会用到的。
在Java/Android应用层面上强引用更多的只是单纯的概念层次上的引用变量定义时对应的类型即为实际指向对象的类型或其父类型。如
Person person new Person();
复制代码
其中person就是一个强引用变量指向的是Person类型的对象实体。
从GC的视角来看new Person()对应的对象实体是储存在堆中的逃逸先不必考虑。person这个引用变量依据实际的变量定义的位置有可能分配在栈中存储如在方法中定义也有可能分配在堆中存储如作为类的字段。
引用关系画一个简单的图大概如下所示 现实中对同一个对象实体往往会具有复杂的多个引用指向如最常见的将对象的引用变量作为实参传递形参接收后会指向同一对象实体等等。因此现实中的对象引用与实体关系比较复杂可能如下 GC时通过可达性去分析如果没有强引用指向对象实体或者即使有强引用指向但强引用的所处的对象自身已经不能从GC Roots可达了这时GC此对象实体会被垃圾回收。
从对象实体的生命周期视角来看new Person()时开始给对象分配内存空间并调用构造器等进行初始化此时对象生成。一旦在GC Roots中没有强引用直达对象实体变成“孤魂野鬼”对象生命周期走向完结对应内存空间可以被回收。
只要对象实体存在强应用可达就不会被垃圾回收直至发生OOM进程终止。 2.2 Reference 与 ReferenceQueue
Java源码中的java.lang.ref包对应的是应用类型和引用队列的类定义。在Android中对应部分具体源码上有稍许更改但整体上类职责与实现逻辑是类似的不妨碍整体上的对引用类型的分析。
为了陈述方便同时不引起歧义先界定几个基本概念以及对应的具体解释。
1目标对象实体。表示通常意义上创建出来的对象例如上述强引用示例中的new Person()即表示一个Person类型的对象实体。此对象可以被引用对象中的referent属性去指向。
2引用对象。由具体的引用类型类如WeakReference、SoftReference、PhantomReference所创建出来的对象。引用对象在创建时外部会将目标对象实体传入进来从而使得引用对象中的referent属性去指向目标对象实体。
3referent属性。引用对象中的referent属性指向的是实际的目标对象实体。
4引用队列。引用对象创建时由外部传入ReferenceQueue类型的对象引用队列中存储的是引用对象并且是会在特定情况下由虚拟机将引用对象入队。存在于引用队列中的引用对象表明此引用对象中referent属性所指向的目标对象实体已经被垃圾回收。
Reference类本身是一个抽象类作为具体引用类型的基类定义了基本的类属性与行为。主体类结构如下所示 从类的注释上可以看出Reference类对所有子类提供了一致的操作行为并在运行时是会与虚拟机中的垃圾收集器紧密协作的实际使用中我们只能使用现有的Reference类的子类或者自定义类去继承现有的Reference类的子类。
/**
* Abstract base class for reference objects. This class defines the
* operations common to all reference objects. Because reference objects are
* implemented in close cooperation with the garbage collector, this class may
* not be subclassed directly.
*
* author Mark Reinhold
* since 1.2
*/public abstract class ReferenceT {....}
复制代码
Reference类比较关键的部分摘录如下
public abstract class ReferenceT {....private T referent; /* Treated specially by GC */volatile ReferenceQueue? super T queue;/*** Returns this reference objects referent. If this reference object has* been cleared, either by the program or by the garbage collector, then* this method returns codenull/code.** return The object to which this reference refers, or* codenull/code if this reference object has been cleared*/public T get() {return this.referent;}/*** Clears this reference object. Invoking this method will not cause this* object to be enqueued.** p This method is invoked only by Java code; when the garbage collector* clears references it does so directly, without invoking this method.*/public void clear() {this.referent null;}/* -- Constructors -- */Reference(T referent) {this(referent, null);}Reference(T referent, ReferenceQueue? super T queue) {this.referent referent;this.queue (queue null) ? ReferenceQueue.NULL : queue;}....
}
复制代码
可以看出Reference类有两个构造器其中T referent是一个泛型形式表示的形参指向的是目标对象实体。ReferenceQueue? super T queue表示的是一个引用队列队列内存储的元素是引用对象外部调用方通过get()方法获取目标对象实体。如果引用对象中的referent属性为nullget()方法将返回null。
referent属性为null存在如下两个触发场景 1虚拟机进行垃圾回收时 2人为的调用引用对象的clear()方法。
其中区别在于人为的调用clear()方法并不会使得此引用对象进入引用队列。
ReferenceQueue表示引用队列类的职责可以从类注释中看出来。
/*** Reference queues, to which registered reference objects are appended by the* * garbage collector after the appropriate reachability changes are detected.* ** author Mark Reinhold* since 1.2*/
public class ReferenceQueueT {....}
复制代码
引用队列中存储的元素是引用对象垃圾回收器会在引用对象中的目标对象实体不再可达时对目标对象实体进行垃圾回收并将对应的引用对象放入引用队列中。因此我们可以通过引用对象中是否存在引用对象去判断对应的目标对象实体是否已经被垃圾回收。
Reference是一个抽象类实际使用时外部用的是其具体的子类依据实际的需求场景对应选择使用WeakReference、SoftReference和PhantomReference。 2.3 软引用
首先要说明一下一般意义上的软引用、弱引用和虚引用实际上指的都是引用对象中的指向目标对象实体的referent属性。而非指此引用对象本身。因为此referent属性才是真正指向的目标对象实体且存在于具体的引用对象中具有具体的引用类型的特性。当然这个特性更多是虚拟机赋予的。
例如众所周知的当目标对象实体没有强引用可达但有软引用指向时在内存不够用时才会回收目标对象实体。
因此我们发现只要内存够用是否够用由虚拟机判断即使目标对象实体只是软引用可达的目标对象实体也不会被GC会一直存活。
可以通过实际的例子看一下软引用的效果。
public class SoftReferenceTest {public static void main(String[] args) {A a new A();ReferenceQueueA rq new ReferenceQueueA();SoftReferenceA srA new SoftReferenceA(a, rq);a null;if (srA.get() null) {System.out.println(a对象进入垃圾回收流程);} else {System.out.println(a对象尚未进入垃圾回收流程 srA.get());}// 通知系统进行垃圾回收System.gc();try {Thread.currentThread().sleep(1);} catch (Exception e) {e.printStackTrace();}if (srA.get() null) {System.out.println(a对象进入垃圾回收流程);} else {System.out.println(a对象尚未进入垃圾回收流程 srA.get());}System.out.println(引用对象: rq.poll());}
}class A {Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println(in A finalize);}}
复制代码
运行结果为
a对象尚未进入垃圾回收流程com.corn.javalib.A60e53b93
a对象尚未进入垃圾回收流程com.corn.javalib.A60e53b93
引用对象:null
复制代码
当a对象没有强引用可达时只有软引用可达此时无论系统是否发生GCa对象的生命周期依然是存活的不会被垃圾回收。也正因为如下引用队列中是不存在对应的srA这个引用对象的。
上述过程对A这个类型的目标对象实体的引用关系起始是这样的 当执行a null时此时引用关系如下 强引用断裂但不影响引用对象中的对A对象这个目标对象实体的引用关系。
因此只要内存足够通过引用对象的get()方法都可以获取到A对象实体。
如果恰巧此时内存不够了呢虚拟机在GC流程中会将引用对象的referent强制置为null此时A对象实体彻底变成“孤魂野鬼”可以被垃圾回收。
当然这里需要说明一点的是示例中只是一个demo。当方法执行完毕后方法中所占用的栈内存空间的引用A a、SoftReference srA会自动出栈A对象实体也会自动变成“孤魂野鬼”直至等待被垃圾回收。
实际使用中SoftReference不一定被经常用到虽然SoftReference可以适当应用到如缓存等场景但一般更通用的建议是使用如LruCache等缓存方案。 2.4 弱引用
与弱引用直接关联的引用对象类型为WeakReference。弱引用的特性如下
当目标对象实体没有强引用可达但有弱引用可达此时在发生GC之前此目标对象实体都是存活的一旦发生GCGC过程中会将弱引用对象中的referent属性置为null并直接将此目标对象实体进行回收并将此引用对象入队到引用队列中。
继续看一个具体的示例
public class WeakReferenceTest {public static void main(String[] args) {A a new A();ReferenceQueueA rq new ReferenceQueueA();WeakReferenceA wrA new WeakReferenceA(a, rq);System.out.println(引用对象: wrA);a null;if (wrA.get() null) {System.out.println(a对象进入垃圾回收流程);} else {System.out.println(a对象尚未进入垃圾回收流程 wrA.get());}// 通知系统进行垃圾回收System.gc();try {Thread.currentThread().sleep(1);} catch (Exception e) {e.printStackTrace();}if (wrA.get() null) {System.out.println(a对象进入垃圾回收流程);} else {System.out.println(a对象尚未进入垃圾回收流程 wrA.get());}System.out.println(引用对象: rq.poll());}static class A {Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println(in A finalize);}}
}
复制代码
输出结果为
引用对象:java.lang.ref.WeakReference60e53b93
a对象尚未进入垃圾回收流程com.corn.javalib.WeakReferenceTest$A5e2de80c
in A finalize
a对象进入垃圾回收流程
引用对象:java.lang.ref.WeakReference60e53b93
复制代码
示例代码中System.gc();执行后之所以让当前线程sleep(1)是基于进一步确保GC线程能被调度执行考虑的。最终的输运行结果对应的弱引用对象被入队到引用队列中表明A对象实体已经被垃圾回收。
引用关系起初是这样的 执行a null时此时引用关系如下 当虚拟机GC时首先会将referent置为null引用关系变为如下 此时A对象实体已经变成“孤魂野鬼”可以被垃圾回收。GC过程中弱引用对象入队引用队列。 由此我们发现弱引用一个强大的地方在于弱引用本质上是不改变目标对象实体的生命周期的也不影响目标对象实体被GC的时机并且还提供了一种机制即基于引用队列下的可以直接去监测目标对象实体是否已经被GC。
这无疑是相当强大的相当于提供了一种可以监测到对象是否被GC的方法且不影响到对象生命周期本身。 2.5 虚引用
无论是SoftReference、WeakReference还是PhantomReference作为Reference类的子类自身更多只是作为引用类型的对象去标记用的类中没有过多的自身的逻辑。与引用类型的逻辑处理过程绝大部分都是在虚拟机中实现的。
当然有一大不同的是PhantomReference类中重写了T get()方法直接返回了null。
public class PhantomReferenceT extends ReferenceT {/*** Returns this reference objects referent. Because the referent of a* phantom reference is always inaccessible, this method always returns* codenull/code.** return codenull/code*/public T get() {return null;}/*** Creates a new phantom reference that refers to the given object and* is registered with the given queue.** p It is possible to create a phantom reference with a ttnull/tt* queue, but such a reference is completely useless: Its ttget/tt* method will always return null and, since it does not have a queue, it* will never be enqueued.** param referent the object the new phantom reference will refer to* param q the queue with which the reference is to be registered,* or ttnull/tt if registration is not required*/public PhantomReference(T referent, ReferenceQueue? super T q) {super(referent, q);}
}
复制代码
也就是说通过虚引用对象的get()方法是无法获取到目标对象实体的。但实际上虚引用对象中的referent还是指向目标对象实体的。也正因为如此使用到虚引用对象时往往都需要传一个引用队列否则构建的虚引用就没有任何意义了。
虚拟机在GC时接下来的处理流程与弱引用类似。目标对象实体被GC后会被入队到引用队列中。 三、WeakReference应用实践
3.1 WeakReference特性总结
相比SoftReference和PhantomReferenceWeakReference应用更加普遍。主要得益于WeakReference提供的特性 1提供了一种监测目标对象实体是否已经被垃圾回收的方法 2同时不改变目标对象实体本身的生命周期 3对外提供了T get()方法去尝试获取到目标对象。
下面具体看一下WeakReference在Java/Android中的使用场景。 3.2 通过WeakReference处理Handler内存泄漏
不少人第一次接触到WeakReference这个概念是在Activity中的Handler可能引起的内存泄露中。
Activity中的Handler内存泄露都比较熟。Activity中的Handler如果以非静态内部类的方式存在默认会持有外部类即Activity的引用在Activity对象中通过Handler发出去的消息是会被加入到消息队列中的待Looper不断轮循在MQ中取到此消息时才会进行消息的处理如handleMessage。也就是说Handler默认持有Activity的引用同时消息处理过程整体上是异步的。此时在消息被处理前如果按下了如back键等Activity是会出栈的一旦GC发生理论上此Activity对象也应该被GC但由于被Handler持有导致强引用可达内存无法回收且handleMessage依然可以执行。
因此往往都建议将Handler定义成静态的内存类或者外部类形式此时不再默认持有Activity引用但如果handleMessage中又需要使用到Activity中的属性时这种情况下通过WeakReference实现就是一个极佳的使用场景。
重新梳理下上述的流程本质上就是Activity对象中需要做一件事情这个事情是一个未来发生的异步的事情。最佳的期望应该是当Acitivity对象生命周期走向完结这件事情与Acitivity直接相关的部分应当自然终止。因为期望上此时Activity对象已经被销毁甚至被垃圾回收。那与Acitivity直接相关的这部分自然也就没有意义了。
我们发现这其实完全符合WeakReference的特性通过WeakReference对象中的T referent属性弱引用到Activity对象实体当T get()为null时直接将与Activity对象有关的事情终止即可。这也是经典的Handler内存泄露的处理方式。 3.3 WeakHashMap
WeakHashMap与HashMap基本实现过程是一样的根本的区别在于其内部的Entry继承的是WeakReferenceEntry中的key具有弱引用特性。具体定义如下
/*** The entries in this hash table extend WeakReference, using its main ref* field as the key.*/
private static class EntryK,V extends WeakReferenceObject implements Map.EntryK,V {V value;final int hash;EntryK,V next;/*** Creates new entry.*/Entry(Object key, V value,ReferenceQueueObject queue,int hash, EntryK,V next) {super(key, queue);this.value value;this.hash hash;this.next next;}SuppressWarnings(unchecked)public K getKey() {return (K) WeakHashMap.unmaskNull(get());}public V getValue() {return value;}public V setValue(V newValue) {V oldValue value;value newValue;return oldValue;}public boolean equals(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry?,? e (Map.Entry?,?)o;K k1 getKey();Object k2 e.getKey();if (k1 k2 || (k1 ! null k1.equals(k2))) {V v1 getValue();Object v2 e.getValue();if (v1 v2 || (v1 ! null v1.equals(v2)))return true;}return false;}public int hashCode() {K k getKey();V v getValue();return Objects.hashCode(k) ^ Objects.hashCode(v);}public String toString() {return getKey() getValue();}
}
复制代码
因此当Entry中key指向的目标对象实体本身没有其他强引用或软引用可达时GC发生时此目标对象实体会被收回Entry中key会被置为null并且此Entry对象将被入队到引用队列中。但直到此时对于WeakHashMap而言这些key为null的Entry还是作为一个个item项存在的依然处于之前的位置。
实际上这些Entry已经没有必要存在了因为key已经从起初的指向目标对象实体变成了null作为key-value这种映射关系已经发生了破坏且key原本指向的目标对象实体生命周期也已经走向了完结。
于是WeakHashMap提供了一种机制去清除对应的这种情况下的Entry。并在主要方法调用路径中会执行expungeStaleEntries方法。
/*** Expunges stale entries from the table.*/
private void expungeStaleEntries() {for (Object x; (x queue.poll()) ! null; ) {synchronized (queue) {SuppressWarnings(unchecked)EntryK,V e (EntryK,V) x;int i indexFor(e.hash, table.length);EntryK,V prev table[i];EntryK,V p prev;while (p ! null) {EntryK,V next p.next;if (p e) {if (prev e)table[i] next;elseprev.next next;// Must not null out e.next;// stale entries may be in use by a HashIteratore.value null; // Help GCsize--;break;}prev p;p next;}}}
}
复制代码
expungeStaleEntries首先从引用队列中去一个取出对应的引用对象实际类型即为Entry。然后找map中找到对应的Entry并从map中移除。
为了将上述情况中的key为null与直接向map中put一个key本身就为null区分开WeakHashMap在put时会将key为null转成成一个new Object()对象。并以此为keyput到map中。
/*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for this key, the old* value is replaced.** param key key with which the specified value is to be associated.* param value value to be associated with the specified key.* return the previous value associated with ttkey/tt, or* ttnull/tt if there was no mapping for ttkey/tt.* (A ttnull/tt return can also indicate that the map* previously associated ttnull/tt with ttkey/tt.)*/
public V put(K key, V value) {Object k maskNull(key);int h hash(k);EntryK,V[] tab getTable();int i indexFor(h, tab.length);for (EntryK,V e tab[i]; e ! null; e e.next) {if (h e.hash eq(k, e.get())) {V oldValue e.value;if (value ! oldValue)e.value value;return oldValue;}}modCount;EntryK,V e tab[i];tab[i] new Entry(k, value, queue, h, e);if (size threshold)resize(tab.length * 2);return null;
}
复制代码
关键语句maskNull(key);实现如下
/*** Value representing null keys inside tables.*/
private static final Object NULL_KEY new Object();/*** Use NULL_KEY for key if it is null.*/
private static Object maskNull(Object key) {return (key null) ? NULL_KEY : key;
}
复制代码
当然了取一个指定的key为null的Entry也会相应转化。
总结一下WeakHashMap中的Entry实际上是一个弱引用对象使得key成为了事实上的referent具备了弱引用特性。实际使用中WeakHashMap中元素项的key往往是指向具有一定生命周期的目标对象实体。如Activity作为key等等这需要实际考虑具体的业务场景。 3.4 ThreadLocal
ThreadLocal为多线程场景下的共享变量的线程安全提供了一种方案。具体思路是将共享变量分别放到各自线程内部的ThreadLocalMap属性中。ThreadLocalMap以ThreadLocal对象为key对应需要存入的对象为value对外统一封装在ThreadLocal类内部并提供接口。也就是说外界对ThreadLocalMap是无感知的。
ThreadLocal对外主要提供了T get()、set(T value)和remove()方法。
/*** Returns the value in the current threads copy of this* thread-local variable. If the variable has no value for the* current thread, it is first initialized to the value returned* by an invocation of the {link #initialValue} method.** return the current threads value of this thread-local*/
public T get() {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {ThreadLocalMap.Entry e map.getEntry(this);if (e ! null) {SuppressWarnings(unchecked)T result (T)e.value;return result;}}return setInitialValue();
}/*** Sets the current threads copy of this thread-local variable* to the specified value. Most subclasses will have no need to* override this method, relying solely on the {link #initialValue}* method to set the values of thread-locals.** param value the value to be stored in the current threads copy of* this thread-local.*/
public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null)map.set(this, value);elsecreateMap(t, value);
}/*** Removes the current threads value for this thread-local* variable. If this thread-local variable is subsequently* {linkplain #get read} by the current thread, its value will be* reinitialized by invoking its {link #initialValue} method,* unless its value is {linkplain #set set} by the current thread* in the interim. This may result in multiple invocations of the* {code initialValue} method in the current thread.** since 1.5*/public void remove() {ThreadLocalMap m getMap(Thread.currentThread());if (m ! null)m.remove(this);}
复制代码
上述方法最终都转到了ThreadLocalMap中。ThreadLocalMap内部是数组存储的数据结构在必要时候进行扩容。重点看一下元素项Entry的定义
/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal oject). Note that null keys (i.e. entry.get()* null) mean that the key is no longer referenced, so the* entry can be expunged from table. Such entries are referred to* as stale entries in the code that follows.*/
static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}
}
复制代码
我们发现与WeakHashMap类似ThreadLocalMap中的Entry继承的也是WeakReferenceEntry中的key即为referent指向的目标对象实体为ThreadLocal对象。因此Entry中的key具备了弱引用特性。
当所指向的ThreadLocal对象生命周期完结时Entry中的key会自动被置为null同时与WeakHashMap类似ThreadLocalMap中也提供了expungeStaleEntry去清除对应的Entry。
其中具体的引用关系如下图所示。 如此对于线程池等线程复用的场景即使线程对象依然存活ThreadLocal对象也不会发生内存泄露会随着其本身生命周期的终结而终结。 3.5 LifeCycle
最新的Android jetpack套件中LifeCycle是其中重要的一个组成部分。LifeCycle提供了一种对象可以观察组件的声明周期的机制并在源码层面开始支持。整体设计上采用的是观察者模式具有生命周期的被观察的组件是被观察者观察组件生命周期的对象是观察者。组件针对不同的生命周期的变化会发出对应的事件并对应回调观察者对象的中相应的方法。
以ComponentActivity为例源码中直接实现了LifecycleOwner接口并初始化了mLifecycleRegistry对象。LifecycleRegistry作为观察者与被观察者的桥梁主要完成对观察者的注册并接收到被观察者发出的事件后分发给观察者。
public class ComponentActivity extends androidx.core.app.ComponentActivity implementsLifecycleOwner,ViewModelStoreOwner,SavedStateRegistryOwner,OnBackPressedDispatcherOwner {....private final LifecycleRegistry mLifecycleRegistry new LifecycleRegistry(this);
....复制代码
LifecycleRegistry中存在mLifecycleOwner属性此对象是一个弱引用对象其referent指向的目标对象实体是LifecycleOwner即被观察者。对应注释部分如下
/*** The provider that owns this Lifecycle.* Only WeakReference on LifecycleOwner is kept, so if somebody leaks Lifecycle, they wont leak* the whole Fragment / Activity. However, to leak Lifecycle object isnt great idea neither,* because it keeps strong references on all other listeners, so youll leak all of them as* well.*/
private final WeakReferenceLifecycleOwner mLifecycleOwner;....
复制代码
也就是说LifecycleRegistry对象中对被观察者即拥有声明周期的组件如Activity、Fragment不是直接强引用的而是通过mLifecycleOwner去弱引用防止LifecycleRegistry在被泄露的情况下导致组件被进一步泄露。 3.6 LeakCanary实现原理
LeakCanary作为Android中知名的内存泄露检测工具能够检测使用过程中泄露的对象并提供详细的路径等信息。
LeakCanary主要实现原理是通过WeakReference去弱引用到目标对象并结合ReferenceQueue以实现检测到目标对象生命周期的目的。下面以检测Activity为例分析LeakCanary监测过程。
执行LeakCanary.install(context);后会执行RefWatcher的构建。
public final class LeakCanary {/*** 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();}....
}
复制代码
其中buildAndInstall()方法中会自动加上对Activity以及Fragment的监测。Activity对应的监测类是ActivityRefWatcher。
/**
* Creates a {link RefWatcher} instance and makes it available through {link
* LeakCanary#installedRefWatcher()}.
*
* Also starts watching activity references if {link #watchActivities(boolean)} was set to true.
*
* throws UnsupportedOperationException if called more than once per Android process.
*/
public RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher ! null) {throw new UnsupportedOperationException(buildAndInstall() should only be called once.);
}
RefWatcher refWatcher build();
if (refWatcher ! DISABLED) {if (watchActivities) {ActivityRefWatcher.install(context, refWatcher);}if (watchFragments) {FragmentRefWatcher.Helper.install(context, refWatcher);}
}
LeakCanaryInternals.installedRefWatcher refWatcher;
return refWatcher;
}
复制代码
ActivityRefWatcher中通过向Application中注册Activity的生命周期回调接口并在Activity onActivityDestroyed方法回调中开始观察。
public final class ActivityRefWatcher {private final ActivityLifecycleCallbacks lifecycleCallbacks new ActivityLifecycleCallbacksAdapter() {public void onActivityDestroyed(Activity activity) {ActivityRefWatcher.this.refWatcher.watch(activity);}};....}
复制代码
watch方法中开始对Activity对象增加上弱引用。
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);ensureGoneAsync(watchStartNanoTime, reference);
}
复制代码
KeyedWeakReference继承WeakReference。构造器中形成referent对Activity对象的弱引用特性并传入了引用队列。
final class KeyedWeakReference extends WeakReferenceObject {public final String key;public final String name;KeyedWeakReference(Object referent, String key, String name,ReferenceQueueObject referenceQueue) {super(checkNotNull(referent, referent), checkNotNull(referenceQueue, referenceQueue));this.key checkNotNull(key, key);this.name checkNotNull(name, name);}
}
复制代码
ensureGoneAsync方法中会调用ensureGone触发GC。
public interface GcTrigger {GcTrigger DEFAULT new GcTrigger() {Override public void runGc() {// Code taken from AOSP FinalizationTest:// https://android.googlesource.com/platform/libcore//master/support/src/test/java/libcore/// java/lang/ref/FinalizationTester.java// System.gc() does not garbage collect every time. Runtime.gc() is// more likely to perfom a gc.Runtime.getRuntime().gc();enqueueReferences();System.runFinalization();}private void enqueueReferences() {// Hack. We dont have a programmatic way to wait for the reference queue daemon to move// references to the appropriate queues.try {Thread.sleep(100);} catch (InterruptedException e) {throw new AssertionError();}}};void runGc();
}
复制代码
随后通过判断引用队列中是否有此引用对象去判断Activity对象是否被回收。并针对未回收情况通过HeapDump去分析内存及堆栈详情。
对其他对象如Fragment等的内存泄露监测基本过程也是相似的。 四、结语
WeakReference自身的特性决定了可以被广泛的应用到实际的需求场景中厘清WeakReference中对应的各概念尤其是其内部的T referent可以进一步加深对WeakReference的理解。并在对应的场景中选择对应现有的或自实现相应的类结构以完成目标功能的同时减少不必要的内存泄露等问题。