欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java四種引用及在LeakCanery中應(yīng)用詳解

 更新時(shí)間:2019年09月01日 09:17:42   作者:NeilZhang  
這篇文章主要介紹了java四種引用及在LeakCanery中應(yīng)用,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

java 四種引用

Java4種引用的級(jí)別由高到低依次為:

StrongReference  >  SoftReference  >  WeakReference  >  PhantomReference

1. StrongReference

String tag = new String("T");

此處的 tag 引用就稱之為強(qiáng)引用。而強(qiáng)引用有以下特征:

1. 強(qiáng)引用可以直接訪問(wèn)目標(biāo)對(duì)象。
2. 強(qiáng)引用所指向的對(duì)象在任何時(shí)候都不會(huì)被系統(tǒng)回收。
3. 強(qiáng)引用可能導(dǎo)致內(nèi)存泄漏。

我們要討論的其它三種Reference較之于強(qiáng)引用而言都屬于“弱引用”,也就是他們所引用的對(duì)象只要沒(méi)有強(qiáng)引用,就會(huì)根據(jù)條件被JVM的垃圾回收器所回收,它們被回收的時(shí)機(jī)以及用法各不相同。下面分別來(lái)進(jìn)行討論。

2. SoftReference

軟引用有以下特征:

1. 軟引用使用 get() 方法取得對(duì)象的強(qiáng)引用從而訪問(wèn)目標(biāo)對(duì)象。

2. 軟引用所指向的對(duì)象按照 JVM 的使用情況(Heap 內(nèi)存是否臨近閾值)來(lái)決定是否回收。

3. 軟引用可以避免 Heap 內(nèi)存不足所導(dǎo)致的異常。

當(dāng)垃圾回收器決定對(duì)其回收時(shí),會(huì)先清空它的 SoftReference,也就是說(shuō) SoftReference 的 get() 方法將會(huì)返回 null,然后再調(diào)用對(duì)象的 finalize() 方法,并在下一輪 GC 中對(duì)其真正進(jìn)行回收。

3. WeakReference

WeakReference 是弱于 SoftReference 的引用類型。弱引用的特性和基本與軟引用相似,區(qū)別就在于弱引用所指向的對(duì)象只要進(jìn)行系統(tǒng)垃圾回收,不管內(nèi)存使用情況如何,永遠(yuǎn)對(duì)其進(jìn)行回收(get() 方法返回 null)。

弱引用有以下特征:

1. 弱引用使用 get() 方法取得對(duì)象的強(qiáng)引用從而訪問(wèn)目標(biāo)對(duì)象。
2. 一旦系統(tǒng)內(nèi)存回收,無(wú)論內(nèi)存是否緊張,弱引用指向的對(duì)象都會(huì)被回收。
3. 弱引用也可以避免 Heap 內(nèi)存不足所導(dǎo)致的異常。
4. PhantomReference(虛引用)
PhantomReference 是所有“弱引用”中最弱的引用類型。不同于軟引用和弱引用,虛引用無(wú)法通過(guò)get()方法來(lái)取得目標(biāo)對(duì)象的強(qiáng)引用從而使用目標(biāo)對(duì)象,觀察源碼可以發(fā)現(xiàn) get() 被重寫為永遠(yuǎn)返回 null。

虛引用有以下特征:

虛引用永遠(yuǎn)無(wú)法使用 get() 方法取得對(duì)象的強(qiáng)引用從而訪問(wèn)目標(biāo)對(duì)象。
虛引用所指向的對(duì)象在被系統(tǒng)內(nèi)存回收前,虛引用自身會(huì)被放入 ReferenceQueue 對(duì)象中從而跟蹤對(duì)象垃圾回收。
虛引用不會(huì)根據(jù)內(nèi)存情況自動(dòng)回收目標(biāo)對(duì)象。
虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用

Reference與ReferenceQueue 使用demo

定義一個(gè)對(duì)象Brain

public class Brain {

  public int mIndex;
  // 占用較多內(nèi)存,當(dāng)系統(tǒng)內(nèi)存不足時(shí),會(huì)自動(dòng)進(jìn)行回收
  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中

結(jié)果打?。?/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

結(jié)果分析:

  • 當(dāng)對(duì)象被回收后,持有他的引用WeakReference/PhantomReference會(huì)被放入ReferenceQueue中
  • WeakReference在原始對(duì)象回收之前被放入ReferenceQueue中,而PhantomReference是在回收之后放入ReferenceQueue中

WeakReference在Leakcanery中的應(yīng)用

LeakCanery是Android檢測(cè)內(nèi)存泄漏的工具,可以檢測(cè)到Activity/Fragment存在的內(nèi)存泄漏。

檢測(cè)原理:

在Application中注冊(cè)監(jiān)聽(tīng)所有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);
 }

當(dāng)Activity的onDestroy被調(diào)用時(shí),生成一個(gè)uuid,標(biāo)記這個(gè)Activity的WeakReference。
創(chuàng)建一個(gè)弱引用,并與一個(gè)跟蹤所有activit回收的ReferenceQueue相關(guān)聯(lián)。(放入一個(gè)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 在一定時(shí)間后檢查被注冊(cè)的WeakReference有沒(méi)有被添加到ReferenceQueue中
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
  watchExecutor.execute(new Retryable() {
   @Override public Retryable.Result run() {
    return ensureGone(reference, watchStartNanoTime);
   }
  });
 }

在onDestry被調(diào)用后若干秒執(zhí)行如下操作:到ReferenceQueue中去取這個(gè)Activity,如果能夠取到說(shuō)明這個(gè)Activity被正?;厥樟?。如果無(wú)法回收,觸發(fā)GC,再去RerenceQueue中取如果還是無(wú)法取到,說(shuō)明Activity沒(méi)有被系統(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對(duì)應(yīng)的uuid已經(jīng)被移除,說(shuō)明activity已經(jīng)被回收,無(wú)內(nèi)存泄漏
  if (gone(reference)) {
   return DONE;
  }
  // 觸發(fā)gc,進(jìn)行垃圾回收
  gcTrigger.runGc();
  removeWeaklyReachableReferences();
  // 如果uuid還沒(méi)有被移除,說(shuō)明activiy存在內(nèi)存泄漏,需要dump內(nèi)存,進(jìn)行分析
  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)存和分析的過(guò)程這里就不細(xì)說(shuō)。

總結(jié)

以上所述是小編給大家介紹的java四種引用及在LeakCanery中應(yīng)用詳解,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!

相關(guān)文章

  • java性能優(yōu)化之分代回收

    java性能優(yōu)化之分代回收

    這篇文章主要介紹了java性能優(yōu)化之分代回收,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • Java 遍歷 String 字符串所有字符的操作

    Java 遍歷 String 字符串所有字符的操作

    這篇文章主要介紹了Java 遍歷 String 字符串所有字符的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • 使用Java將字節(jié)數(shù)組轉(zhuǎn)成16進(jìn)制形式的代碼實(shí)現(xiàn)

    使用Java將字節(jié)數(shù)組轉(zhuǎn)成16進(jìn)制形式的代碼實(shí)現(xiàn)

    在很多場(chǎng)景下,需要進(jìn)行分析字節(jié)數(shù)據(jù),但是我們存起來(lái)的字節(jié)數(shù)據(jù)一般都是二進(jìn)制的,這時(shí)候就需要我們將其轉(zhuǎn)成16進(jìn)制的方式方便分析,本文主要介紹如何使用Java將字節(jié)數(shù)組格式化成16進(jìn)制的格式并輸出,需要的朋友可以參考下
    2024-05-05
  • springboot 集成cas5.3 實(shí)現(xiàn)sso單點(diǎn)登錄詳細(xì)流程

    springboot 集成cas5.3 實(shí)現(xiàn)sso單點(diǎn)登錄詳細(xì)流程

    SSO的定義是在多個(gè)應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng)。單點(diǎn)登錄是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一,本文給大家介紹springboot 集成cas5.3 實(shí)現(xiàn)sso單點(diǎn)登錄功能,感興趣的朋友一起看看吧
    2021-10-10
  • Java實(shí)現(xiàn)排隊(duì)論的原理

    Java實(shí)現(xiàn)排隊(duì)論的原理

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)排隊(duì)論的原理,對(duì)排隊(duì)論感興趣的小伙伴們可以參考一下
    2016-02-02
  • Java文檔注釋用法+JavaDoc的使用說(shuō)明

    Java文檔注釋用法+JavaDoc的使用說(shuō)明

    這篇文章主要介紹了Java文檔注釋用法+JavaDoc的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot之配置logging日志及在控制臺(tái)中輸出過(guò)程

    SpringBoot之配置logging日志及在控制臺(tái)中輸出過(guò)程

    這篇文章主要介紹了SpringBoot之配置logging日志及在控制臺(tái)中輸出過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 淺談Java引用和Threadlocal的那些事

    淺談Java引用和Threadlocal的那些事

    這篇文章主要介紹了Java引用和Threadlocal的那些事,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能【基于swing組件】

    Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能【基于swing組件】

    這篇文章主要介紹了Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能,涉及java基于swing組件選擇與操作圖片元素的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-01-01
  • java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法

    java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法

    這篇文章主要介紹了 java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫的方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-10-10

最新評(píng)論