SoftReference > WeakReference > PhantomReference..."/>
您好,登錄后才能下訂單哦!
java 四種引用
Java4種引用的級別由高到低依次為:
StrongReference > SoftReference > WeakReference > PhantomReference
1. StrongReference
String tag = new String("T");
此處的 tag 引用就稱之為強引用。而強引用有以下特征:
1. 強引用可以直接訪問目標對象。
2. 強引用所指向的對象在任何時候都不會被系統(tǒng)回收。
3. 強引用可能導致內(nèi)存泄漏。
我們要討論的其它三種Reference較之于強引用而言都屬于“弱引用”,也就是他們所引用的對象只要沒有強引用,就會根據(jù)條件被JVM的垃圾回收器所回收,它們被回收的時機以及用法各不相同。下面分別來進行討論。
2. SoftReference
軟引用有以下特征:
1. 軟引用使用 get() 方法取得對象的強引用從而訪問目標對象。
2. 軟引用所指向的對象按照 JVM 的使用情況(Heap 內(nèi)存是否臨近閾值)來決定是否回收。
3. 軟引用可以避免 Heap 內(nèi)存不足所導致的異常。
當垃圾回收器決定對其回收時,會先清空它的 SoftReference,也就是說 SoftReference 的 get() 方法將會返回 null,然后再調用對象的 finalize() 方法,并在下一輪 GC 中對其真正進行回收。
3. WeakReference
WeakReference 是弱于 SoftReference 的引用類型。弱引用的特性和基本與軟引用相似,區(qū)別就在于弱引用所指向的對象只要進行系統(tǒng)垃圾回收,不管內(nèi)存使用情況如何,永遠對其進行回收(get() 方法返回 null)。
弱引用有以下特征:
1. 弱引用使用 get() 方法取得對象的強引用從而訪問目標對象。
2. 一旦系統(tǒng)內(nèi)存回收,無論內(nèi)存是否緊張,弱引用指向的對象都會被回收。
3. 弱引用也可以避免 Heap 內(nèi)存不足所導致的異常。
4. PhantomReference(虛引用)
PhantomReference 是所有“弱引用”中最弱的引用類型。不同于軟引用和弱引用,虛引用無法通過get()方法來取得目標對象的強引用從而使用目標對象,觀察源碼可以發(fā)現(xiàn) get() 被重寫為永遠返回 null。
虛引用有以下特征:
虛引用永遠無法使用 get() 方法取得對象的強引用從而訪問目標對象。
虛引用所指向的對象在被系統(tǒng)內(nèi)存回收前,虛引用自身會被放入 ReferenceQueue 對象中從而跟蹤對象垃圾回收。
虛引用不會根據(jù)內(nèi)存情況自動回收目標對象。
虛引用必須和引用隊列(ReferenceQueue)聯(lián)合使用
Reference與ReferenceQueue 使用demo
定義一個對象Brain
public class Brain { public int mIndex; // 占用較多內(nèi)存,當系統(tǒng)內(nèi)存不足時,會自動進行回收 private byte []mem; public Brain(int index) { mIndex = index; mem = new byte[1024 * 1024]; } @Override protected void finalize() throws Throwable { super.finalize(); LogUtils.e("Brain", "finalize + index=" + mIndex); } }
創(chuàng)建Reference并添加到RefrenceQueue中
結果打?。?/p>
2019-01-29 19:22:27.499 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=0 wf=java.lang.ref.WeakReference@e1f904c
2019-01-29 19:22:27.499 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=1 wf=java.lang.ref.WeakReference@82fc895
2019-01-29 19:22:27.500 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=2 wf=java.lang.ref.WeakReference@3b3fdaa
2019-01-29 19:22:27.500 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=3 wf=java.lang.ref.WeakReference@668fd9b
2019-01-29 19:22:27.501 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=0
2019-01-29 19:22:27.501 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100000
2019-01-29 19:22:27.502 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=4 wf=java.lang.ref.WeakReference@8db6538
2019-01-29 19:22:27.502 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=5 wf=java.lang.ref.WeakReference@f915911
2019-01-29 19:22:27.503 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=1
2019-01-29 19:22:27.503 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100001
2019-01-29 19:22:27.504 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=2
2019-01-29 19:22:27.505 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100002
2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=3
2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100003
2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=4
2019-01-29 19:22:27.508 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100004
2019-01-29 19:22:27.508 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=5
2019-01-29 19:22:27.509 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100005
2019-01-29 19:22:27.629 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=0 pr=null
2019-01-29 19:22:27.629 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=1 pr=null
2019-01-29 19:22:27.629 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=6 wf=java.lang.ref.WeakReference@e2c4a76
2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=2 pr=null
2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=7 wf=java.lang.ref.WeakReference@4cfd877
2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=3 pr=null
2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=4 pr=null
2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=8 wf=java.lang.ref.WeakReference@37d9ce4
2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=5 pr=null
2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=9 wf=java.lang.ref.WeakReference@ea1754d
結果分析:
WeakReference在Leakcanery中的應用
LeakCanery是Android檢測內(nèi)存泄漏的工具,可以檢測到Activity/Fragment存在的內(nèi)存泄漏。
檢測原理:
在Application中注冊監(jiān)聽所有Activity生命周期的listener,registerActivityLifecycleCallbacks。
//ActivityRefWatcher 中的代碼 public void watchActivities() { // Make sure you don't get installed twice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); } public void stopWatchingActivities() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); }
當Activity的onDestroy被調用時,生成一個uuid,標記這個Activity的WeakReference。
創(chuàng)建一個弱引用,并與一個跟蹤所有activit回收的ReferenceQueue相關聯(lián)。(放入一個map中,key : uuid, value:weakReference)
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityDestroyed(Activity activity) { refWatcher.watch(activity); } };
具體的watch執(zhí)行如下:
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); }
ensureGoneAsync執(zhí)行如下:
// watchExecutor 在一定時間后檢查被注冊的WeakReference有沒有被添加到ReferenceQueue中 private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) { watchExecutor.execute(new Retryable() { @Override public Retryable.Result run() { return ensureGone(reference, watchStartNanoTime); } }); }
在onDestry被調用后若干秒執(zhí)行如下操作:到ReferenceQueue中去取這個Activity,如果能夠取到說明這個Activity被正?;厥樟恕H绻麩o法回收,觸發(fā)GC,再去RerenceQueue中取如果還是無法取到,說明Activity沒有被系統(tǒng)回收,可能存在內(nèi)存泄漏。
真正核心的代碼如下:
long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); // 如果ReferenceQue中有activity的弱引用,則將retainedKeys中的uuid移除 removeWeaklyReachableReferences(); if (debuggerControl.isDebuggerAttached()) { // The debugger can create false leaks. return RETRY; } // 如果activity對應的uuid已經(jīng)被移除,說明activity已經(jīng)被回收,無內(nèi)存泄漏 if (gone(reference)) { return DONE; } // 觸發(fā)gc,進行垃圾回收 gcTrigger.runGc(); removeWeaklyReachableReferences(); // 如果uuid還沒有被移除,說明activiy存在內(nèi)存泄漏,需要dump內(nèi)存,進行分析 if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); 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(); heapdumpListener.analyze(heapDump); } return DONE; }
HeapDump dump內(nèi)存和分析的過程這里就不細說。
總結
以上所述是小編給大家介紹的java四種引用及在LeakCanery中應用詳解,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。