回过头来看对象的四种状态强软弱虚引用的理解
一、對象的四種引用狀態描述
在JDK1.2之后,Java對引用的概念進行了擴充,將引用分為強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4中引用強度一次減弱。
- 強引用就是指在程序代碼之中普遍存在的,類似"Object obj = new Object()"這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象
- 軟引用是用來描述一些還有用但并非必需的對象,對于軟引用關聯著的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進回收范圍進行第二次回收【內存不GC 不進行回收】。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。在JDK1.2之后,提供了SoftReference類來實現軟引用
- 弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象,只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK1.2之后,提供了WeakReference類來實現弱引用
- 虛引用也成為幽靈引用或者幻影引用,它是最弱的一中引用關系。一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。為一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。在JDK1.2之后,提供給了PhantomReference類來實現虛引用
?
JVM參數初始化設置 便于代碼演示
在通過代碼研究幾種引用狀態之前,先定義一些參數,后面所有部分的代碼示例都使用這些參數。
首先是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.html3 */4 @Test5 public void testStrongReference0() {6 7 }程序運行結果為:
1 Heap2 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.html3 */4 @Test5 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 Heap4 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大小的對象
- 5161K大小的對象被移到了老年代中
- 7958K大小的對象被進行了一次回收,剩余5161K大小的對象,相當于這一次的內存收集 清理掉了 7958 - 5161 = 1797K 空間
總結起來就是4M的byte數組并沒有被回收(因為總共有5161K的對象,虛擬機啟動的時候也才加載了3699K不到5161K,那4M的byte數組肯定是在的),原因是有bytes引用指向4M的byte數組。既然如此,我們把bytes置空看看結果如何:
1 /**2 * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html3 */4 @Test5 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 Heap4 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.html3 */4 @Test5 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@764046293 [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@764046295 Heap6 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.html3 */4 @Test5 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 null9 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.html3 */4 @Test5 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@21dd63a83 [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后:null5 Heap6 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的作用分點講解下:
講完理論,用代碼驗證一下,還是使用軟引用,不過為了顯示更清楚把GC顯示相關參數(-verbose:gc ?-XX:+PrintGCDetails)去掉,代碼為:
1 /**2 * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html3 */4 @Test5 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---null5 java.lang.ref.SoftReference@fa4033b---null6 java.lang.ref.SoftReference@58d01e82---null7 java.lang.ref.SoftReference@4783165b---[B@6f30d50a8 java.lang.ref.SoftReference@6ef2bc8d---[B@23905e39 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看到由于內存不夠,回收了前三個軟引用,且回收的三個軟引用內存地址都能對得上,這證明了上面的說法。
?
虛引用的研究
本來不準備寫虛引用的,因為比較簡單,虛引用唯一的目的只是跟蹤對象的回收。不過后面有網友朋友提出了問題,我自己看了一下,虛引用確實有一個容易混淆的地方,因此文章更新,最后一部分加上虛引用。
測試代碼為:
1 /**2 * @author 五月的倉頡http://www.cnblogs.com/xrq730/p/7082471.html3 */4 @Test5 public void testPhantomReference() {6 System.out.println("**********虛引用測試**********");7 8 ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<byte[]>();9 10 byte[] bytes = new byte[4 * _1MB]; 11 PhantomReference<byte[]> pr = new PhantomReference<byte[]>(bytes, referenceQueue); 12 13 bytes = null; 14 15 System.gc(); 16 17 System.out.println(referenceQueue.poll()); 18 JdkUtil.sleep(100); 19 System.out.println(referenceQueue.poll()); 20 }第13行bytes置空,此時4M的byte數組只關聯了虛引用。這里的重點代碼是第17行~第19行三行,代碼運行結果為:
1 **********虛引用測試**********2 [Full GC[Tenured: 0K->5163K(10240K), 0.0108148 secs] 7795K->5163K(19456K), [Perm : 4355K->4355K(21248K)], 0.0108713 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 3 null4 java.lang.ref.PhantomReference@6f30d50a5 Heap6 par new generation total 9216K, used 365K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)7 eden space 8192K, 4% used [0x00000000f9a00000, 0x00000000f9a5b768, 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 5163K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000) 11 the space 10240K, 50% used [0x00000000fa400000, 0x00000000fa90acf8, 0x00000000fa90ae00, 0x00000000fae00000) 12 compacting perm gen total 21248K, used 4371K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) 13 the space 21248K, 20% used [0x00000000fae00000, 0x00000000fb244e98, 0x00000000fb245000, 0x00000000fc2c0000) 14 No shared spaces configured.看到第一次從隊列中取沒有取到虛引用,休眠100ms之后卻從隊列中取到了虛引用,且GC輸出也看得出來,新生代和老年代中沒有大對象了,說明4M的byte數組確實被回收。
講這個問題,首先看一下JDK API對于PhantomReference的描述:
注意紅字,對象是虛可達到對象,對象在那時或者在以后的某一時間,它會將該引用加入隊列,那么什么時候加入該引用隊列?答案是被虛引用關聯的對象真正被回收的時候。
之前我們說了,虛引用唯一的作用是用于跟蹤對象的垃圾回收的,System.gc()方法調用的時候,4M的byte數組并沒有被馬上回收,System.gc()方法只是發出一個通知:建議觸發GC。
當4M的byte數組真正被回收的時候,虛引用加入引用隊列。因此一開始獲取隊列頭的時候隊列頭為null,休眠100ms之后確保對象被回收,隊列頭中有虛引用。
轉載于:https://www.cnblogs.com/gxyandwmm/p/9463393.html
總結
以上是生活随笔為你收集整理的回过头来看对象的四种状态强软弱虚引用的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (21) java web的struts
- 下一篇: Java学习day2