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

詳解JAVA 弱引用

 更新時(shí)間:2020年08月17日 15:37:58   作者:弗蘭克的貓  
這篇文章主要介紹了 JAVA 弱引用的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)java引用對象,感興趣的朋友可以了解下

定義

弱引用是使用WeakReference創(chuàng)建的引用,弱引用也是用來描述非必需對象的,它是比軟引用更弱的引用類型。在發(fā)生GC時(shí),只要發(fā)現(xiàn)弱引用,不管系統(tǒng)堆空間是否足夠,都會將對象進(jìn)行回收。

說明

弱引用,從名字來看就很弱嘛,這種引用指向的對象,一旦在GC時(shí)被掃描到,就逃脫不了被回收的命運(yùn)。

但是,弱引用指向的對象也并不一定就馬上會被回收,如果弱引用對象較大,直接進(jìn)到了老年代,那么就可以茍且偷生到Full GC觸發(fā)前,所以弱引用對象也可能存在較長的一段時(shí)間。一旦一個(gè)弱引用對象被垃圾回收器回收,便會加入到一個(gè)引用隊(duì)列中(如果有的話)。

弱引用對應(yīng)的類為WeakReference,舉個(gè)栗子:

String s = new String("Frank"); 
WeakReference<String> weakRef = new WeakReference<String>(s);
s = null;

這里我們把s設(shè)置為null后,字符串對象便只有弱引用指向它。

弱可達(dá)

如果一個(gè)對象與GC Roots之間僅存在弱引用,則稱這個(gè)對象為弱可達(dá)(weakly reachable)對象。

注意

在垃圾回收器回收一個(gè)對象前,WeakReference類所提供的get方法會返回其引用對象的強(qiáng)引用,一旦垃圾回收器回收掉該對象之后,get方法將返回null。所以在獲取弱引用對象的代碼中,一定要判斷是否為null,以免出現(xiàn)NullPointerException異常導(dǎo)致應(yīng)用崩潰。

下面的代碼會讓s再次持有對象的強(qiáng)引用:

s = weakRef.get();

如果在weakRef包裹的對象被回收前,用強(qiáng)引用關(guān)聯(lián)該對象,那這個(gè)對象又會變成強(qiáng)可達(dá)狀態(tài)。

來看一個(gè)簡單的栗子了解一下WeakReference引用的對象是何時(shí)被回收的:

public class WeakReferenceTest {
 private static final List<Object> TEST_DATA = new LinkedList<>();
 private static final ReferenceQueue<TestClass> QUEUE = new ReferenceQueue<>();

 public static void main(String[] args) {
  TestClass obj = new TestClass("Test");
  WeakReference<TestClass> weakRef = new WeakReference<>(obj, QUEUE);
  //可以重新獲得OOMClass對象,并用一個(gè)強(qiáng)引用指向它
  //oomObj = weakRef.get();

  // 該線程不斷讀取這個(gè)弱引用,并不斷往列表里插入數(shù)據(jù),以促使系統(tǒng)早點(diǎn)進(jìn)行GC
  new Thread(() -> {
   while (true) {
    TEST_DATA.add(new byte[1024 * 100]);
    try {
     Thread.sleep(1000);
    } catch (InterruptedException e) {
     e.printStackTrace();
     Thread.currentThread().interrupt();
    }
    System.out.println(weakRef.get());
   }
  }).start();

  // 這個(gè)線程不斷讀取引用隊(duì)列,當(dāng)弱引用指向的對象唄回收時(shí),該引用就會被加入到引用隊(duì)列中
  new Thread(() -> {
   while (true) {
    Reference<? extends TestClass> poll = QUEUE.poll();
    if (poll != null) {
     System.out.println("--- 弱引用對象被jvm回收了 ---- " + poll);
     System.out.println("--- 回收對象 ---- " + poll.get());
    }
   }
  }).start();

  //將強(qiáng)引用指向空指針 那么此時(shí)只有一個(gè)弱引用指向TestClass對象
  obj = null;

  try {
   Thread.currentThread().join();
  } catch (InterruptedException e) {
   e.printStackTrace();
   System.exit(1);
  }
 }

 static class TestClass {
  private String name;

  public TestClass(String name) {
   this.name = name;
  }

  @Override
  public String toString() {
   return "TestClass - " + name;
  }
 }
}

設(shè)置一下虛擬機(jī)參數(shù):

-verbose:gc -Xms4m -Xmx4m -Xmn2m

運(yùn)行結(jié)果如下:

[GC (Allocation Failure) 1017K->464K(3584K), 0.0014345 secs]
[GC (Allocation Failure) 1483K->536K(3584K), 0.0017221 secs]
[GC (Allocation Failure) 1560K->648K(3584K), 0.0036572 secs]
TestClass - Test
TestClass - Test
TestClass - Test
[GC (Allocation Failure) 1621K->984K(3584K), 0.0011455 secs]
--- 弱引用對象被jvm回收了 ---- java.lang.ref.WeakReference@51a947fe
--- 回收對象 ---- null
null
...省略n個(gè)null和幾次GC信息
[Full GC (Ergonomics) 2964K->2964K(3584K), 0.0025450 secs]
[Full GC (Allocation Failure) 2964K->2964K(3584K), 0.0021907 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid6860.hprof ...
Heap dump file created [3912229 bytes in 0.011 secs]
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at weakhashmap.WeakReferenceTest.lambda$main$0(WeakReferenceTest.java:22)
at weakhashmap.WeakReferenceTest$$Lambda$1/764977973.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)

可以看到,其實(shí)弱引用也并不是一發(fā)生GC就被回收掉了。

應(yīng)用場景

如果一個(gè)對象僅僅是偶爾使用,并且希望在使用時(shí)隨時(shí)就能獲取到,但又不想影響此對象的垃圾收集,那么你應(yīng)該用 WeakReference 來引用該對象。

弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機(jī)就會把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

一般來說,很少直接使用WeakReference,而是使用WeakHashMap。在WeakHashMap中,內(nèi)部有一個(gè)引用隊(duì)列,插入的元素會被包裹成WeakReference,并加入隊(duì)列中,用來做緩存再合適不過。

在Tomcat的緩存中,其實(shí)就用到了WeakHashMap:

public final class ConcurrentCache<K,V> {
 private final int size;
 private final Map<K,V> eden;
 private final Map<K,V> longterm;

 public ConcurrentCache(int size) {
  this.size = size;
  this.eden = new ConcurrentHashMap<>(size);
  this.longterm = new WeakHashMap<>(size);
 }

 public V get(K k) {
  // 先從eden中取
  V v = this.eden.get(k);
  if (v == null) {
   // 如果取不到再從longterm中取
   synchronized (longterm) {
    v = this.longterm.get(k);
   }
   // 如果取到則重新放到eden中
   if (v != null) {
    this.eden.put(k, v);
   }
  }
  return v;
 }

 public void put(K k, V v) {
  if (this.eden.size() >= size) {
   // 如果eden中的元素?cái)?shù)量大于指定容量,將所有元素放到longterm中
   synchronized (longterm) {
    this.longterm.putAll(this.eden);
   }
   this.eden.clear();
  }
  this.eden.put(k, v);
 }
}

這里有eden和longterm的兩個(gè)map,如果對jvm堆了解的話,可以看出tomcat在這里是使用ConcurrentHashMap和WeakHashMap做了類似分代緩存的操作。

在put方法里,在插入鍵值對時(shí),先檢查eden緩存的容量是否超出設(shè)定的大小。如果沒有則直接放入eden緩存,如果超了則鎖定longterm將eden中所有的鍵值對都放入longterm。再將eden清空并插入該鍵值對。

在get方法中,也是優(yōu)先從eden中找對應(yīng)的key,如果沒有則進(jìn)入longterm緩存中查找,找到后就加入eden緩存并返回。

經(jīng)過這樣的設(shè)計(jì),相對常用的對象都能在eden緩存中找到,不常用(有可能被銷毀的對象)的則進(jìn)入longterm緩存。而longterm的key的實(shí)際對象沒有其他引用指向它時(shí),gc就會自動回收heap中該弱引用指向的實(shí)際對象,并將弱引用放入其引用隊(duì)列中。

弱引用與軟引用對比

弱引用與軟引用的區(qū)別在于:

  1. 只具有弱引用的對象擁有更短暫的生命周期。
  2. 被垃圾回收器回收的時(shí)機(jī)不一樣,在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。而被軟引用關(guān)聯(lián)的對象只有在內(nèi)存不足時(shí)才會被回收。
  3. 弱引用不會影響GC,而軟引用會一定程度上對GC造成影響。

相似之處:都是用來描述非必需對象的。

那么什么時(shí)候用SoftReference,什么時(shí)候用WeakReference呢?

如果緩存的對象是比較大的對象,使用頻率相對較高的對象,那么使用SoftReference會更好,因?yàn)檫@樣能讓緩存對象有更長的生命周期。

如果緩存對象都是比較小的對象,使用頻率一般或者相對較低,那么使用WeakReference會更合適。

當(dāng)然,如果實(shí)在不知道選哪個(gè),一般而言,用作緩存時(shí)使用WeakHashMap都不會有太大問題。

小結(jié)

  • 弱引用是比軟引用更弱的引用類型
  • 弱引用不能延長對象的生命周期,一旦對象只剩下弱引用,它就隨時(shí)可能會被回收
  • 可以通過弱引用獲取對象的強(qiáng)引用
  • 弱引用適合用作緩存

以上就是詳解 JAVA 弱引用的詳細(xì)內(nèi)容,更多關(guān)于java 弱引用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • jsch中ChannelShell與ChannelExec的區(qū)別及說明

    jsch中ChannelShell與ChannelExec的區(qū)別及說明

    這篇文章主要介紹了jsch中ChannelShell與ChannelExec的區(qū)別及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Maven添加reactor依賴失敗的解決方案

    Maven添加reactor依賴失敗的解決方案

    起初是自己在學(xué)spring boot3,結(jié)果到了reactor這一部分的時(shí)候,在項(xiàng)目的pom.xml文件中添加下列依賴報(bào)錯,接下來通過本文給大家介紹Maven添加reactor依賴失敗的解決方案,需要的朋友可以參考下
    2024-06-06
  • mybatis-plus生成mapper擴(kuò)展文件的方法

    mybatis-plus生成mapper擴(kuò)展文件的方法

    這篇文章主要介紹了mybatis-plus生成mapper擴(kuò)展文件的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java實(shí)戰(zhàn)之火車票預(yù)訂系統(tǒng)的實(shí)現(xiàn)

    Java實(shí)戰(zhàn)之火車票預(yù)訂系統(tǒng)的實(shí)現(xiàn)

    這篇文章主要介紹了利用Java實(shí)現(xiàn)的火車票預(yù)訂系統(tǒng),文中用到了JSP?、Servlert、JQuery、Ajax?等技術(shù),文中示例代碼講解詳細(xì),需要的可以參考一下
    2022-02-02
  • java 數(shù)據(jù)結(jié)構(gòu)之棧與隊(duì)列

    java 數(shù)據(jù)結(jié)構(gòu)之棧與隊(duì)列

    這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)之棧與隊(duì)列的相關(guān)資料,這里對java中的棧和隊(duì)列都做出實(shí)現(xiàn)實(shí)例來幫助大家理解學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu),需要的朋友可以參考下
    2017-07-07
  • 探究MyBatis插件原理以及自定義插件實(shí)現(xiàn)

    探究MyBatis插件原理以及自定義插件實(shí)現(xiàn)

    這篇文章主要介紹了探究MyBatis插件原理以及自定義插件實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • MyBatis控制臺顯示SQL語句的方法實(shí)現(xiàn)

    MyBatis控制臺顯示SQL語句的方法實(shí)現(xiàn)

    這篇文章主要介紹了MyBatis控制臺顯示SQL語句的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 深入分析Java內(nèi)存區(qū)域的使用詳解

    深入分析Java內(nèi)存區(qū)域的使用詳解

    本篇文章對Java內(nèi)存區(qū)域的使用進(jìn)行了詳細(xì)的介紹。需要的朋友參考下
    2013-05-05
  • 關(guān)于webLucene 安裝方法

    關(guān)于webLucene 安裝方法

    webLucene是一個(gè)基于開源項(xiàng)目lucene實(shí)現(xiàn)站內(nèi)搜索的工具,關(guān)于它的安裝,百度得到的大多是一樣的,按照步驟也能正確安裝并運(yùn)行,需要注意的問題是
    2009-06-06
  • springboot 如何使用jackson來處理實(shí)體類

    springboot 如何使用jackson來處理實(shí)體類

    這篇文章主要介紹了springboot使用jackson來處理實(shí)體類的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論