Java虛擬機15:再談四種引用狀態

来源:http://www.cnblogs.com/xrq730/archive/2017/06/27/7082471.html
-Advertisement-
Play Games

JVM的四種引用狀態 在Java虛擬機5:Java垃圾回收(GC)機制詳解一文中,有簡單提到過JVM的四種引用狀態,當時只是簡單學習,知道有這麼一個概念,對四種引用狀態理解不深。這兩天重看虛擬機這部分的時候,寫了很多例子詳細研究了一下JVM的幾種引用,對於JVM的引用理解加深了不少,因此總結寫一篇文 ...


JVM的四種引用狀態

Java虛擬機5:Java垃圾回收(GC)機制詳解一文中,有簡單提到過JVM的四種引用狀態,當時只是簡單學習,知道有這麼一個概念,對四種引用狀態理解不深。這兩天重看虛擬機這部分的時候,寫了很多例子詳細研究了一下JVM的幾種引用,對於JVM的引用理解加深了不少,因此總結寫一篇文章總結並分享下。

首先,還是先從JVM四種引用狀態開始,這部分摘抄自周志明老師的《深入理解Java虛擬機:JVM高級特性與最佳實踐》一書。

在JDK1.2之前,Java中的引用的定義很傳統:如果reference類型的數據中存儲的數值代表的是另外一塊記憶體的起始地址,就稱這塊記憶體代表著一個引用。這種定義很純粹,但是太過狹隘,一個對象在這種頂一下只有被引用或者沒有被引用兩種狀態,對於如何描述一些"食之無味,棄之可惜"的對象就顯得無能為力。我們希望能描述這樣一類對象:當記憶體空間還足夠時,則能保留在記憶體之中;如果記憶體空間在進行垃圾收集後還是非常緊張,則可以拋棄這些對象(註意和前面一段藍字的對比學習)。很多系統的緩存功能都符合這樣的引用場景。

在JDK1.2之後,Java對引用的概念進行了擴充,將引用分為強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4中引用強度一次減弱。

  • 強引用就是指在程式代碼之中普遍存在的,類似"Object obj = new Object()"這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象
  • 軟引用是用來描述一些還有用但並非必需的對象,對於軟引用關聯著的對象,在系統將要發生記憶體溢出異常之前,將會把這些對象列進回收範圍進行第二次回收。如果這次回收還沒有足夠的記憶體,才會拋出記憶體溢出異常。在JDK1.2之後,提供了SoftReference類來實現軟引用
  • 弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象,只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前記憶體是否足夠,都會回收掉只被弱引用關聯的對象。在JDK1.2之後,提供了WeakReference類來實現弱引用
  • 虛引用也成為幽靈引用或者幻影引用,它是最弱的一中引用關係。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。為一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。在JDK1.2之後,提供給了PhantomReference類來實現虛引用

 

寫於代碼開始前

在通過代碼研究幾種引用狀態之前,先定義一些參數,後面所有部分的代碼示例都使用這些參數。

首先是JVM的參數,這裡我使用的是:

-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+UseParNewGC -verbose:gc -XX:+PrintGCDetails

這意味著:

  • 堆大小固定為20M
  • 新生代大小為10M,SurvivorRatio設置為8,則Eden區大小=8M,每個Survivor區大小=1M,每次有9M的新生代記憶體空間可用來new對象
  • 新生代使用使用ParNew收集器,Server模式下預設是Parallel收集器,不過這個收集器的GC日誌我看著沒有ParNew收集器的GC日誌舒服,因此就改成ParNew收集器了
  • 當發生GC的時候列印GC的簡單信息,當程式運行結束列印GC詳情

其次,再定義一個常量類"_1MB":

/**
 * 1M
 */
private static final int _1MB = 1024 * 1024;

代碼示例使用byte數組,每個byte為1個位元組,因此定義一個"_1MB"的常量就可以方便得到1M、2M、3M...的記憶體空間了。

 

強引用的研究

關於強引用的研究,研究的重點在於驗證"當一個對象到GC Roots沒有任何引用鏈相連,則證明此對象是不可用的(要被回收)"這句話的正確性。虛擬機參數上面列了,首先寫一個空方法:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testStrongReference0() {
 6 
 7 }

程式運行結果為:

 1 Heap
 2  par new generation   total 9216K, used 3699K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
 3   eden space 8192K,  45% used [0x00000000f9a00000, 0x00000000f9d9cdc0, 0x00000000fa200000)
 4   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 5   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 6  tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
 7    the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 8  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
 9    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243d88, 0x00000000fb243e00, 0x00000000fc2c0000)
10 No shared spaces configured.

這意味著新生代中本身就有3699K的記憶體空間。很好理解,因為虛擬機啟動的時候就會載入一部分數據到記憶體中,這部分數據的大小為3699K。

下一步我們放一個4M的byte數組進去(4M是因為找一個相對大點的數字,結果會比較明顯),4M=4096K,加上原來的3966K等於8062K。對這8062K記憶體空間觸發一次GC:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testStrongReference0() {
 6     System.out.println("**********強引用測試(放一個4M的數組,觸發GC)**********");
 7         
 8     byte[] bytes = new byte[4 * _1MB];
 9         
10     // 手動觸發GC
11     System.gc();
12 }

運行結果為:

 1 **********強引用測試(放一個4M的數組,觸發GC)**********
 2 [Full GC[Tenured: 0K->5161K(10240K), 0.0085630 secs] 7958K->5161K(19456K), [Perm : 4354K->4354K(21248K)], 0.0086002 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 3 Heap
 4  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
 5   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47300, 0x00000000fa200000)
 6   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 7   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 8  tenured generation   total 10240K, used 5161K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
 9    the space 10240K,  50% used [0x00000000fa400000, 0x00000000fa90a548, 0x00000000fa90a600, 0x00000000fae00000)
10  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
11    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc0, 0x00000000fb243e00, 0x00000000fc2c0000)
12 No shared spaces configured.

總結一下這次GC的結果(由於這篇不是研究記憶體分配的文章,因此我們只關註結果,至於過程到底為什麼就不細究了):

  • 新生代中只留下了284K大小的對象
  • 7958K大小的對象被移到了老年代中
  • 7958K大小的對象被進行了一次回收,剩餘5161K大小的對象

總結起來就是4M的byte數組並沒有被回收(因為總共有5161K的對象,虛擬機啟動的時候也才載入了3699K不到5161K,那4M的byte數組肯定是在的),原因是有bytes引用指向4M的byte數組。既然如此,我們把bytes置空看看結果如何:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testStrongReference0() {
 6     System.out.println("**********強引用測試(放一個4M的數組,bytes置空,觸發GC)**********");
 7         
 8     byte[] bytes = new byte[4 * _1MB];
 9         
10     bytes = null;
11         
12     // 手動觸發GC
13     System.gc();
14 }

運行結果為:

 1 **********強引用測試(放一個4M的數組,bytes置空,觸發GC)**********
 2 [Full GC[Tenured: 0K->1064K(10240K), 0.0096213 secs] 7958K->1064K(19456K), [Perm : 4354K->4354K(21248K)], 0.0096644 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
 3 Heap
 4  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
 5   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47300, 0x00000000fa200000)
 6   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 7   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 8  tenured generation   total 10240K, used 1064K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
 9    the space 10240K,  10% used [0x00000000fa400000, 0x00000000fa50a368, 0x00000000fa50a400, 0x00000000fae00000)
10  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
11    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc0, 0x00000000fb243e00, 0x00000000fc2c0000)
12 No shared spaces configured.

從GC詳情我們可以看到:

  • 老年代只使用了1064K大小的記憶體
  • 新生代只使用了284K大小的記憶體

很顯然4M的byte數組被回收

由這個例子我們回顧可以作為GC Roots的對象:

  • 虛擬機棧(棧幀中的本地變數表)中引用的對象,比如在方法中定義"Object obj = new Object();"
  • 方法區中類靜態屬性引用的對象,比如在類中定義"private static Object lock = new Object();",將Object對象作為一個鎖,所有類共用
  • 方法區中常量引用的對象,比如在介面中定義"public static final char c = 'a';",字元'a'是一個常量
  • 本地方法棧中JNI(即一般說的Native方法)引用的對象,這個不好找例子

這次的回收正是因為第一條。本身有bytes(在虛擬機棧中)指向4M的byte數組,由於將bytes置空。因此4M的byte數組此時沒有任何一個可以作為GC Roots對象的引用指向它,即4M的byte數組被虛擬機標記為可回收的垃圾,在GC時被回收。

稍微擴展一下,這裡上面代碼的做法是手動將bytes置空,其實方法調用結束也是一樣的,棧幀消失,棧幀消失意味著bytes消失,那麼4M的byte數組同樣沒有任何一個可以作為GC Roots對象的引用指向它,因此方法調用結束之後,4M的byte數組同樣會被虛擬機標記為可回收的垃圾,在GC時被回收。

 

軟引用的研究

軟引用之前說過了,JDK提供了SoftReference類共開發者使用,那我們就利用SoftReference研究一下軟引用,測試代碼為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testSoftReference0() {
 6     System.out.println("**********軟引用測試**********");
 7         
 8     byte[] bytes = new byte[4 * _1MB];
 9     SoftReference<byte[]> sr = new SoftReference<byte[]>(bytes);
10     System.out.println("GC前:" + sr.get());
11         
12     bytes = null;
13         
14     System.gc();
15     System.out.println("GC後:" + sr.get());
16 }

同樣的new一個4M的byte數組,通過SoftReference構造方法放到SoftReference中。

這段代碼最值得註意的是第9行"bytes=null"這一句,如果不將bytes置空,那麼4M的byte數組還與強引用關聯著,記憶體不夠虛擬機將拋出異常而不會嘗試回收它;將bytes置空則不一樣,4M的byte數組失去了強引用,但是它又在SoftReference中,這意味著這個4M的byte數組目前僅僅與軟引用關聯

運行一下程式,結果為:

 1 **********軟引用測試**********
 2 GC前:[B@76404629
 3 [Full GC[Tenured: 0K->5161K(10240K), 0.0094088 secs] 7953K->5161K(19456K), [Perm : 4354K->4354K(21248K)], 0.0094428 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
 4 GC後:[B@76404629
 5 Heap
 6  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
 7   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47330, 0x00000000fa200000)
 8   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 9   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
10  tenured generation   total 10240K, used 5161K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
11    the space 10240K,  50% used [0x00000000fa400000, 0x00000000fa90a778, 0x00000000fa90a800, 0x00000000fae00000)
12  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
13    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243f10, 0x00000000fb244000, 0x00000000fc2c0000)
14 No shared spaces configured.

看到GC前後,bytes都是"[B@76404629",很顯然4M的byte數組並沒有被回收。從記憶體空間來看,老年代中使用了5161K,和之前強引用測試是一樣的,證明瞭這一點。

那我們怎麼能看到弱引用的回收呢?既然弱引用是發生在記憶體不夠之前,那隻需要不斷實例化byte數組,然後將之與軟引用關聯即可,代碼為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testSoftReference1() {
 6     System.out.println("**********軟引用測試**********");
 7             
 8     SoftReference<byte[]> sr0 = new SoftReference<byte[]>(new byte[4 * _1MB]);
 9     SoftReference<byte[]> sr1 = new SoftReference<byte[]>(new byte[4 * _1MB]);
10     SoftReference<byte[]> sr2 = new SoftReference<byte[]>(new byte[4 * _1MB]);
11     SoftReference<byte[]> sr3 = new SoftReference<byte[]>(new byte[4 * _1MB]);
12     SoftReference<byte[]> sr4 = new SoftReference<byte[]>(new byte[4 * _1MB]);
13     SoftReference<byte[]> sr5 = new SoftReference<byte[]>(new byte[4 * _1MB]);
14             
15     System.out.println(sr0.get());
16     System.out.println(sr1.get());
17     System.out.println(sr2.get());
18     System.out.println(sr3.get());
19     System.out.println(sr4.get());
20     System.out.println(sr5.get());
21 }

運行結果為:

 1 **********軟引用測試**********
 2 [GC[ParNew: 7958K->1024K(9216K), 0.0041103 secs] 7958K->5187K(19456K), 0.0041577 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] 
 3 [GC[ParNew: 5203K->331K(9216K), 0.0036532 secs] 9366K->9481K(19456K), 0.0036694 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 4 [GC[ParNew: 4427K->4427K(9216K), 0.0000249 secs][Tenured: 9149K->9149K(10240K), 0.0054937 secs] 13577K->13246K(19456K), [Perm : 4353K->4353K(21248K)], 0.0055600 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 5 [Full GC[Tenured: 9149K->783K(10240K), 0.0071252 secs] 13246K->783K(19456K), [Perm : 4353K->4352K(21248K)], 0.0071560 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
 6 [GC[ParNew: 4096K->41K(9216K), 0.0010362 secs] 4879K->4921K(19456K), 0.0010745 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 7 [GC[ParNew: 4137K->10K(9216K), 0.0009216 secs] 9017K->8986K(19456K), 0.0009366 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 8 null
 9 null
10 null
11 [B@4783165b
12 [B@6f30d50a
13 [B@6ef2bc8d
14 Heap
15  par new generation   total 9216K, used 4307K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
16   eden space 8192K,  52% used [0x00000000f9a00000, 0x00000000f9e32560, 0x00000000fa200000)
17   from space 1024K,   1% used [0x00000000fa200000, 0x00000000fa202978, 0x00000000fa300000)
18   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
19  tenured generation   total 10240K, used 8975K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
20    the space 10240K,  87% used [0x00000000fa400000, 0x00000000facc3f40, 0x00000000facc4000, 0x00000000fae00000)
21  compacting perm gen  total 21248K, used 4366K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
22    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb2439e0, 0x00000000fb243a00, 0x00000000fc2c0000)
23 No shared spaces configured.

從第8行~第13行的結果來看,前三個4M的byte數組被回收了,後三個4M的byte數組還在,這就證明瞭"被軟引用關聯的對象會在記憶體不夠時被回收"。

這段代碼我們可以做一個對比思考:

  • 如果4M的byte數組沒有被軟引用關聯而是被強引用關聯,且不釋放強引用,那麼new到第4個4M的byte數組時就會報錯,因為老年代總共只有10M,前兩個4M的byte數組可以進入老年代,第3個4M的byte數組new出來的時候放入新生代,但是當第四個4M的byte數組new出來的時候,第3個4M的byte數組卻沒法進入老年代(因為3個4M=12M,大於老年代的10M),虛擬機拋出OutOfMemoryError
  • 如果4M的byte數組被軟引用關聯且強引用已經釋放,那麼可以無限寫"SoftReference<byte[]> sr = new SoftReference<byte[]>(new byte[4 * _1MB]);"這句代碼,因為記憶體不夠了就回收4M的byte數組,永遠沒有記憶體溢出的可能

所以,很多時候對一些非必需的對象,我們可以將直接將其與軟引用關聯,這樣記憶體不夠時會先回收軟引用關聯的對象而不會拋出OutOfMemoryError,畢竟拋出OutOfMemoryError意味著整個應用將停止運行。

 

弱引用的研究

JDK給我們提供的了WeakReference用以將一個對象關聯到弱引用,弱引用的測試比較簡單,代碼為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testWeakReference() {
 6     System.out.println("**********弱引用測試**********");
 7         
 8     WeakReference<byte[]> wr = new WeakReference<byte[]>(new byte[4 * _1MB]);
 9     System.out.println("GC前:" + wr.get());
10         
11     System.gc();
12     System.out.println("GC後:" + wr.get());
13 }

我這裡不定義一個強引用直接關聯4M的byte數組(避免忘了將對象與強引用的關聯取消),這也是使用SoftReference、WeakReference時我個人比較推薦的做法。程式運行的結果為:

 1 **********弱引用測試**********
 2 GC前:[B@21dd63a8
 3 [Full GC[Tenured: 0K->1065K(10240K), 0.0080353 secs] 7958K->1065K(19456K), [Perm : 4353K->4353K(21248K)], 0.0080894 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 4 GC後:null
 5 Heap
 6  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
 7   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47318, 0x00000000fa200000)
 8   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 9   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
10  tenured generation   total 10240K, used 1065K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
11    the space 10240K,  10% used [0x00000000fa400000, 0x00000000fa50a6e8, 0x00000000fa50a800, 0x00000000fae00000)
12  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
13    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc8, 0x00000000fb243e00, 0x00000000fc2c0000)
14 No shared spaces configured.

看到GC後bytes為null了,且新生代、老年代中也沒見到有4M以上的大對象,從兩個角度都證明瞭,GC之後4M的byte數組被回收了。

 

Reference與ReferenceQueue

前面用代碼驗證了強引用、軟應用、弱引用三種引用狀態,虛引用就不演示了,記住虛引用是用於跟蹤對象的回收狀態就夠了。

下麵再講一個知識點ReferenceQueue,ReferenceQueue的作用分點講解下:

  1. SoftReference、WeakReference、PhantomReference,在構造的時候可以通過構造函數傳入一個ReferenceQueue,但是只有PhantomReference,ReferenceQueue是必須的
  2. 以SoftReference為例,一個類型為SoftReference的sr關聯了一個4M的byte數組,那麼當記憶體不夠的時候,回收此4M的byte數組,sr.get()為null,表示sr不再關聯此4M的byte數組
  3. 當sr對應的4M的byte數組被回收之後,sr本身被加入ReferenceQueue中,表示此軟引用關聯的對象被回收
  4. ReferenceQueue本身是一個Queue,可通過poll()方法不斷拿到隊列的頭元素,如果是null表示沒有被回收的軟引用關聯的對象,如果不是null表示有軟引用關聯的對象被回收
  5. SoftReference是這樣的,WeakReference與PhantomReference同理

講完理論,用代碼驗證一下,還是使用軟引用,不過為了顯示更清楚把GC顯示相關參數(-verbose:gc  -XX:+PrintGCDetails)去掉,代碼為:

 1 /**
 2  * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html
 3  */
 4 @Test
 5 public void testReferenceQueue() {
 6     System.out.println("**********引用隊列測試**********\n");
 7         
 8     ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<byte[]>();
 9         
10     SoftReference<byte[]> sr0 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
11     SoftReference<byte[]> sr1 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
12     SoftReference<byte[]> sr2 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
13     SoftReference<byte[]> sr3 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
14     SoftReference<byte[]> sr4 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
15     SoftReference<byte[]> sr5 = new SoftReference<byte[]>(new byte[4 * _1MB], referenceQueue);
16         
17     System.out.println("**********軟引用關聯的對象展示**********");
18     System.out.println(sr0 + "---" + sr0.get());
19     System.out.println(sr1 + "---" + sr1.get());
20     System.out.println(sr2 + "---" + sr2.get());
21     System.out.println(sr3 + "---" + sr3.get());
22     System.out.println(sr4 + "---" + sr4.get());
23     System.out.println(sr5 + "---" + sr5.get());
24         
25     System.out.println("**********引用隊列中的SoftReference展示**********");
26     System.out.println(referenceQueue.poll());
27     System.out.println(referenceQueue.poll());
28     System.out.println(referenceQueue.poll());
29     System.out.println(referenceQueue.poll());
30     System.out.println(referenceQueue.poll());
31     System.out.println(referenceQueue.poll());
32 }

運行結果為:

 1 **********引用隊列測試**********
 2 
 3 **********軟引用關聯的對象展示**********
 4 java.lang.ref.SoftReference@50ed0a5---null
 5 java.lang.ref.SoftReference@fa4033b---null
 6 java.lang.ref.SoftReference@58d01e82---null
 7 java.lang.ref.SoftReference@4783165b---[B@6f30d50a
 8 java.lang.ref.SoftReference@6ef2bc8d---[B@23905e3
 9 java.lang.ref.SoftReference@6db17b38---[B@1f10d1cb
10 **********引用隊列中的SoftReference展示**********
11 java.lang.ref.SoftReference@50ed0a5
12 java.lang.ref.SoftReference@fa4033b
13 java.lang.ref.SoftReference@58d01e82
14 null
15 null
16 null

看到由於記憶體不夠,回收了前三個軟引用,且回收的三個軟引用記憶體地址都能對得上,這證明瞭上面的說法。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 真是前所未有的大失誤啊,竟然快兩個月忘記寫博客了! 公司的項目組連著解散了倆,挺動蕩的! 終於把欠公司的錢還上了,這次改欠支付寶了! 摩托車也練的可以正常騎行了,拉過幾次高速,最快到111了,油門還沒到底,不知道磨合期過了之後是不是能更快一些! 房貸也開始還了,現在每個月都是收支都是負數,情況不太樂 ...
  • python中的多線程其實並不是真正的多線程,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進程。Python提供了非常好用的多進程包multiprocessing,只需要定義一個函數,Python會完成其他所有事情。藉助這個包,可以輕鬆完成從單進程到併發執行的轉換。mul ...
  • 轉載需註明原文地址和作者兩項內容。 正則表達式目的是能夠快速處理字元串內容,主要用於找出指定的字元串,配合其他操作完成任務。使用正則表達式時要瞭解自己語言的特性,python中的正則表達式預設情況是貪婪模式,也就是不限制的情況下會儘可能多得匹配字元串。 0x00 基礎語法 0x01 python正則 ...
  • 廢話不說,直接給思路 gitlab重部署後新加用戶不發送激活郵件,配置如下: 首先確認配置沒問題,其次查看日誌production.log log沒有給出錯誤信息。那就直接開大招 確保method為smtp,錯了就是配置寫錯了或者沒被正確載入 確保配置是我們要的 發送測試郵件,此時會列印詳細錯誤 T ...
  • 1、把lib.py里的代碼導入index.py,然後使用lib.py代碼: 導入之後會生成一個.pyc文件,即位元組碼文件。 2、解釋器 即用python來執行hello.py腳本. 3、編碼的基本知識 unicode即萬國碼,utf8是對unicode的精簡,gbk是與utf8同級別的編碼方式。 4 ...
  • 一、線程創建 註解:Thread(group=None,target=None,name=None,args=(),kwargs={})group:線程組,目前還沒有實現,庫引用時提示必須是Nonetarget:要執行的方法name:線程名args/kwargs:要傳入方法的參數,args和kwar ...
  • 模塊安裝: 數據操作用到的模塊pymysql,需要通過pip install pymysql進行安裝。 redis操作用的模塊是redis,需要通過pip install redis進行安裝。 檢驗是否安裝成功:進入到Python命令行模式,輸入import pymysql、 import redi ...
  • 簡介 特點 (1)優於OC,快速,安全 (2)取消了預編譯指令包括巨集定義(OC用的太多了) (3)取消了OC指針和不安全訪問的使用(看不到星星了) (4)捨棄 Objective C 早期應用 Smalltalk 的語法,全面改為點語法 (5)3.0中對Foundation框架做了很多改變,去除了N ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...