通常意義上而言,Java/Android中的引用類型包括強引用、軟引用、弱引用、虛引用。不一樣的引用類型具備各自適用的應用場景,並與JVM的GC直接相關。java
做爲Java/Android中的引用類型之一,WeakReference被大量的使用到系統源碼、基礎工具甚至具體的業務邏輯中。在解決須要異步使用目標對象實體、且又不影響目標對象實體的生命週期的場景中,具備自然優點。同時,還能進一步判斷目標對象實體當前所處的GC階段,如當前是否GC roots可達,亦或者已經被GC回收。android
默認狀況下,咱們直接指向對象的引用類型爲強引用,也是咱們每天寫代碼一定會用到的。數組
在Java/Android應用層面上,強引用更多的只是單純的概念層次上的,引用變量定義時對應的類型即爲實際指向對象的類型或其父類型。如:緩存
Person person = new Person();
複製代碼
其中,person
就是一個強引用變量,指向的是Person
類型的對象實體。安全
從GC的視角來看,new Person()
對應的對象實體,是儲存在堆中的(逃逸先沒必要考慮)。person
這個引用變量,依據實際的變量定義的位置,有可能分配在棧中存儲(如在方法中定義),也有可能分配在堆中存儲(如做爲類的字段)。bash
引用關係畫一個簡單的圖,大概以下所示:markdown
現實中,對同一個對象實體,每每會具備複雜的多個引用指向,如最多見的將對象的引用變量做爲實參傳遞,形參接收後會指向同一對象實體等等。所以,現實中的對象引用與實體關係比較複雜,可能以下:數據結構
GC時,經過可達性去分析,若是沒有強引用指向對象實體,或者即便有強引用指向,但強引用的所處的對象自身,已經不能從GC Roots可達了,這時GC,此對象實體會被垃圾回收。多線程
從對象實體的生命週期視角來看,new Person()
時開始給對象分配內存空間,並調用構造器等進行初始化,此時,對象生成。一旦在GC Roots中沒有強引用直達,對象實體變成「孤魂野鬼」,對象生命週期走向完結,對應內存空間能夠被回收。app
只要對象實體存在強應用可達,就不會被垃圾回收,直至發生OOM,進程終止。
Java源碼中的java.lang.ref
包,對應的是應用類型和引用隊列的類定義。在Android中,對應部分具體源碼上有稍許更改,但總體上類職責與實現邏輯是相似的,不妨礙總體上的對引用類型的分析。
爲了陳述方便,同時不引發歧義,先界定幾個基本概念,以及對應的具體解釋。
1,目標對象實體
。表示一般意義上建立出來的對象,例如上述強引用示例中的new Person()
即表示一個Person
類型的對象實體。此對象能夠被引用對象
中的referent
屬性去指向。
2,引用對象
。由具體的引用類型類(如WeakReference、SoftReference、PhantomReference)所建立出來的對象。引用對象
在建立時,外部會將目標對象實體
傳入進來,從而使得引用對象
中的referent
屬性去指向目標對象實體
。
3,referent屬性
。引用對象
中的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 Reference<T> { .... } 複製代碼
Reference
類比較關鍵的部分摘錄以下:
public abstract class Reference<T> { .... private T referent; /* Treated specially by GC */ volatile ReferenceQueue<? super T> queue; /** * Returns this reference object's referent. If this reference object has * been cleared, either by the program or by the garbage collector, then * this method returns <code>null</code>. * * @return The object to which this reference refers, or * <code>null</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
屬性爲null
,get()
方法將返回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 ReferenceQueue<T> { .... } 複製代碼
引用隊列
中存儲的元素,是引用對象
,垃圾回收器會在引用對象
中的目標對象實體
再也不可達時,對目標對象實體
進行垃圾回收,並將對應的引用對象
放入引用隊列
中。所以,咱們能夠經過引用對象
中是否存在引用對象
,去判斷對應的目標對象實體
是否已經被垃圾回收。
Reference
是一個抽象類,實際使用時,外部用的是其具體的子類,依據實際的需求場景,對應選擇使用WeakReference
、SoftReference
和PhantomReference
。
首先要說明一下,通常意義上的軟引用
、弱引用
和虛引用
,實際上指的都是引用對象
中的指向目標對象實體
的referent
屬性。而非指此引用對象
自己。由於此referent
屬性纔是真正指向的目標對象實體
,且存在於具體的引用對象
中,具備具體的引用類型的特性。固然,這個特性更可能是虛擬機賦予的。
例如:衆所周知的,當目標對象實體
沒有強引用可達,但有軟引用指向時,在內存不夠用時,纔會回收目標對象實體
。
所以,咱們發現,只要內存夠用(是否夠用由虛擬機判斷),即便目標對象實體
只是軟引用可達的,目標對象實體
也不會被GC,會一直存活。
能夠經過實際的例子看一下軟引用的效果。
public class SoftReferenceTest { public static void main(String[] args) { A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); SoftReference<A> srA = new SoftReference<A>(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 { @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("in A finalize"); } } 複製代碼
運行結果爲:
a對象還沒有進入垃圾回收流程com.corn.javalib.A@60e53b93
a對象還沒有進入垃圾回收流程com.corn.javalib.A@60e53b93
引用對象:null
複製代碼
當a對象沒有強引用可達時,只有軟引用可達,此時,不管系統是否發生GC,a對象的生命週期依然是存活的,不會被垃圾回收。也正由於以下,引用隊列
中是不存在對應的srA這個引用對象
的。
上述過程對A這個類型的目標對象實體
的引用關係,起始是這樣的:
當執行a = null
時,此時引用關係以下:
強引用斷裂,但不影響引用對象
中的對A對象這個目標對象實體
的引用關係。
所以,只要內存足夠,經過引用對象
的get()
方法,均可以獲取到A對象實體。
若是恰巧此時,內存不夠了呢,虛擬機在GC流程中,會將引用對象
的referent
強制置爲null
,此時A對象實體完全變成「孤魂野鬼」,能夠被垃圾回收。
固然,這裏須要說明一點的是,示例中只是一個demo。當方法執行完畢後,方法中所佔用的棧內存空間的引用(A a、SoftReference srA)會自動出棧,A對象實體也會自動變成「孤魂野鬼」,直至等待被垃圾回收。
實際使用中,SoftReference
不必定被常常用到,雖然SoftReference
能夠適當應用到如緩存等場景,但通常更通用的建議是使用如LruCache
等緩存方案。
與弱引用直接關聯的引用對象
類型爲WeakReference
。弱引用的特性以下:
當目標對象實體
沒有強引用可達,但有弱引用可達,此時,在發生GC以前,此目標對象實體
都是存活的,一旦發生GC,GC過程當中會將弱引用對象
中的referent
屬性置爲null
,並直接將此目標對象實體
進行回收,並將此引用對象
入隊到引用隊列
中。
繼續看一個具體的示例:
public class WeakReferenceTest { public static void main(String[] args) { A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); WeakReference<A> wrA = new WeakReference<A>(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 { @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("in A finalize"); } } } 複製代碼
輸出結果爲:
引用對象:java.lang.ref.WeakReference@60e53b93 a對象還沒有進入垃圾回收流程com.corn.javalib.WeakReferenceTest$A@5e2de80c in A finalize a對象進入垃圾回收流程 引用對象:java.lang.ref.WeakReference@60e53b93 複製代碼
示例代碼中,System.gc();
執行後,之因此讓當前線程sleep(1)
,是基於進一步確保GC線程能被調度執行考慮的。最終的輸運行結果,對應的弱引用對象
,被入隊到引用隊列中
,代表A對象實體已經被垃圾回收。
引用關係起初是這樣的:
執行a = null
時,此時引用關係以下:
當虛擬機GC時,首先會將referent
置爲null
,引用關係變爲以下:
此時,A對象實體已經變成「孤魂野鬼」,能夠被垃圾回收。GC過程當中,弱引用對象
入隊引用隊列
。
由此,咱們發現,弱引用一個強大的地方在於,弱引用本質上,是不改變目標對象實體
的生命週期的,也不影響目標對象實體
被GC的時機,而且,還提供了一種機制,即基於引用隊列
下的,能夠直接去監測目標對象實體
是否已經被GC。
這無疑是至關強大的,至關於提供了一種能夠監測到對象是否被GC的方法,且不影響到對象生命週期自己。
不管是SoftReference
、WeakReference
仍是PhantomReference
,做爲Reference
類的子類,自身更多隻是做爲引用類型的對象,去標記用的,類中沒有過多的自身的邏輯。與引用類型的邏輯處理過程,絕大部分都是在虛擬機中實現的。
固然,有一大不一樣的是,PhantomReference
類中,重寫了T get()
方法,直接返回了null
。
public class PhantomReference<T> extends Reference<T> { /** * Returns this reference object's referent. Because the referent of a * phantom reference is always inaccessible, this method always returns * <code>null</code>. * * @return <code>null</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 <tt>null</tt> * queue, but such a reference is completely useless: Its <tt>get</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 <tt>null</tt> if registration is not required */ public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } } 複製代碼
也就是說,經過虛引用對象
的get()
方法,是沒法獲取到目標對象實體
的。但實際上,虛引用對象
中的referent
仍是指向目標對象實體
的。也正由於如此,使用到虛引用對象
時,每每都須要傳一個引用隊列
,不然,構建的虛引用就沒有任何意義了。
虛擬機在GC時,接下來的處理流程與弱引用相似。目標對象實體
被GC後,會被入隊到引用隊列
中。
相比SoftReference
和PhantomReference
,WeakReference
應用更加廣泛。主要得益於WeakReference
提供的特性:
1,提供了一種監測目標對象實體
是否已經被垃圾回收的方法;
2,同時不改變目標對象實體
自己的生命週期;
3,對外提供了T get()
方法去嘗試獲取到目標對象。
下面具體看一下WeakReference
在Java/Android中的使用場景。
很多人第一次接觸到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內存泄露的處理方式。
WeakHashMap
與HashMap
基本實現過程是同樣的,根本的區別在於,其內部的Entry
繼承的是WeakReference
,Entry
中的key
具備弱引用特性。具體定義以下:
/** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; /** * Creates new entry. */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,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") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // Help GC size--; break; } prev = p; p = next; } } } } 複製代碼
expungeStaleEntries
首先從引用隊列
中去一個取出對應的引用對象
,實際類型即爲Entry
。而後找map中找到對應的Entry
,並從map中移除。
爲了將上述狀況中的key
爲null
,與直接向map中put
一個key
自己就爲null
區分開,WeakHashMap
在put
時,會將key
爲null
轉成成一個new Object()
對象。並以此爲key
,put
到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 <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); for (Entry<K,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++; Entry<K,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
,等等,這須要實際考慮具體的業務場景。
ThreadLocal
爲多線程場景下的共享變量的線程安全,提供了一種方案。具體思路是將共享變量,分別放到各自線程內部的ThreadLocalMap
屬性中。ThreadLocalMap
,以ThreadLocal
對象爲key
,對應須要存入的對象爲value
,對外,統一封裝在ThreadLocal
類內部,並提供接口。也就是說,外界對ThreadLocalMap
是無感知的。
ThreadLocal
對外主要提供了T get()
、set(T value)
和remove()
方法。
/** * Returns the value in the current thread's 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 thread's 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 thread's 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 thread's 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); else createMap(t, value); } /** * Removes the current thread's 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 WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } 複製代碼
咱們發現,與WeakHashMap
相似,ThreadLocalMap
中的Entry
繼承的也是WeakReference
,Entry
中的key
即爲referent
,指向的目標對象實體
爲ThreadLocal
對象。所以,Entry
中的key
具有了弱引用特性。
當所指向的ThreadLocal
對象生命週期完結時,Entry
中的key
會自動被置爲null
,同時,與WeakHashMap
相似,ThreadLocalMap
中也提供了expungeStaleEntry
去清除對應的Entry
。
其中具體的引用關係以下圖所示。
如此,對於線程池等線程複用的場景,即便線程對象依然存活,ThreadLocal
對象也不會發生內存泄露,會隨着其自己生命週期的終結而終結。
最新的Android jetpack套件中,LifeCycle是其中重要的一個組成部分。LifeCycle提供了一種對象能夠觀察組件的聲明週期的機制,並在源碼層面開始支持。總體設計上,採用的是觀察者模式,具備生命週期的被觀察的組件,是被觀察者,觀察組件生命週期的對象,是觀察者。組件針對不一樣的生命週期的變化,會發出對應的事件,並對應回調觀察者對象的中相應的方法。
以ComponentActivity
爲例,源碼中直接實現了LifecycleOwner
接口,並初始化了mLifecycleRegistry
對象。LifecycleRegistry
,做爲觀察者與被觀察者的橋樑,主要完成對觀察者的註冊,並接收到被觀察者發出的事件後,分發給觀察者。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
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 won't leak * the whole Fragment / Activity. However, to leak Lifecycle object isn't great idea neither, * because it keeps strong references on all other listeners, so you'll leak all of them as * well. */ private final WeakReference<LifecycleOwner> mLifecycleOwner; .... 複製代碼
也就是說,LifecycleRegistry
對象中對被觀察者,即擁有聲明週期的組件,如Activity、Fragment,不是直接強引用的,而是經過,mLifecycleOwner,
去弱引用,防止LifecycleRegistry
在被泄露的狀況下致使組件被進一步泄露。
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 WeakReference<Object> { public final String key; public final String name; KeyedWeakReference(Object referent, String key, String name, ReferenceQueue<Object> 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 don't 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
的理解。並在對應的場景中,選擇對應現有的,或自實現相應的類結構,以完成目標功能的同時,減小沒必要要的內存泄露等問題。
end~