Java 强、弱、软、虚,你属于哪一种?
作者:CodeBear的園子
來源:www.cnblogs.com/CodeBear/p/12447554.html
Java中的四種引用
Java中有四種引用類型:強引用、軟引用、弱引用、虛引用。
Java為什么要設計這四種引用
Java的內存分配和內存回收,都不需要程序員負責,都是由偉大的JVM去負責,一個對象是否可以被回收,主要看是否有引用指向此對象,說的專業點,叫可達性分析。
Java設計這四種引用的主要目的有兩個:
可以讓程序員通過代碼的方式來決定某個對象的生命周期;
有利用垃圾回收。
強引用
強引用是最普遍的一種引用,我們寫的代碼,99.9999%都是強引用:
Object?o?=?new?Object();這種就是強引用了,是不是在代碼中隨處可見,最親切。?
只要某個對象有強引用與之關聯,這個對象永遠不會被回收,即使內存不足,JVM寧愿拋出OOM,也不會去回收。
那么什么時候才可以被回收呢?當強引用和對象之間的關聯被中斷了,就可以被回收了。
我們可以手動把關聯給中斷了,方法也特別簡單:
o?=?null;我們可以手動調用GC,看看如果強引用和對象之間的關聯被中斷了,資源會不會被回收,為了更方便、更清楚的觀察到回收的情況,我們需要新寫一個類,然后重寫finalize方法,下面我們來進行這個實驗:
public?class?Student?{????@Overrideprotected?void?finalize()?throws?Throwable?{System.out.println("Student?被回收了");} }public?static?void?main(String[]?args)?{Student?student?=?new?Student();student?=?null;System.gc(); }運行結果:
Student?被回收了可以很清楚的看到資源被回收了。
當然,在實際開發中,千萬不要重寫finalize方法
在實際的開發中,看到有一些對象被手動賦值為NULL,很大可能就是為了“特意提醒”JVM這塊資源可以進行垃圾回收了。點擊這里獲取一份 JVM 實戰教程。
軟引用
下面先來看看如何創建一個軟引用:
?SoftReference<Student>studentSoftReference = new?SoftReference<Student>(new?Student());軟引用就是把對象用SoftReference包裹一下,當我們需要從軟引用對象獲得包裹的對象,只要get一下就可以了:
SoftReference<Student>studentSoftReference = new?SoftReference<Student>(new?Student()); Student?student?=?studentSoftReference.get(); System.out.println(student);軟引用有什么特點呢:?
當內存不足,會觸發JVM的GC,如果GC后,內存還是不足,就會把軟引用的包裹的對象給干掉,也就是只有在內存不足,JVM才會回收該對象。
還是一樣的,必須做實驗,才能加深印象:
SoftReference<byte[]>?softReference?=?new?SoftReference<byte[]>(new?byte[1024*1024*10]); System.out.println(softReference.get()); System.gc(); System.out.println(softReference.get());???????? byte[]?bytes?=?new?byte[1024?*?1024?*?10]; System.out.println(softReference.get());我定義了一個軟引用對象,里面包裹了byte[],byte[]占用了10M,然后又創建了10Mbyte[]。
運行程序,需要帶上一個參數:
-Xmx20M代表最大堆內存是20M。
運行結果:
[B@11d7fff [B@11d7fff null可以很清楚的看到手動完成GC后,軟引用對象包裹的byte[]還活的好好的,但是當我們創建了一個10M的byte[]后,最大堆內存不夠了,所以把軟引用對象包裹的byte[]給干掉了,如果不干掉,就會拋出OOM。
軟引用到底有什么用呢?比較適合用作緩存,當內存足夠,可以正常的拿到緩存,當內存不夠,就會先干掉緩存,不至于馬上拋出OOM。說到緩存,大家可以關注微信公眾號Java技術棧獲取更多干貨。
弱引用
弱引用的使用和軟引用類似,只是關鍵字變成了WeakReference:
WeakReference<byte[]>?weakReference?=?new?WeakReference<byte[]>(new?byte[1024\*1024\*10]); System.out.println(weakReference.get());弱引用的特點是不管內存是否足夠,只要發生GC,都會被回收:
WeakReference<byte[]>?weakReference?=?new?WeakReference<byte[]>(new?byte[1]); System.out.println(weakReference.get()); System.gc(); System.out.println(weakReference.get());運行結果:
[B@11d7fffnull可以很清楚的看到明明內存還很充足,但是觸發了GC,資源還是被回收了。?
弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。
虛引用
虛引用又被稱為幻影引用,我們來看看它的使用:
ReferenceQueue?queue?=?new?ReferenceQueue(); PhantomReference<byte[]>?reference?=?new?PhantomReference<byte[]>(new?byte[1],?queue); System.out.println(reference.get());虛引用的使用和上面說的軟引用、弱引用的區別還是挺大的,我們先不管ReferenceQueue 是個什么鬼,直接來運行:
null竟然打印出了null,我們來看看get方法的源碼:
public?T?get()?{????????return?null; }這是幾個意思,竟然直接返回了null。
這就是虛引用特點之一了:無法通過虛引用來獲取對一個對象的真實引用。
那虛引用存在的意義是什么呢?這就要回到我們上面的代碼了,我們把代碼復制下,以免大家再次往上翻:
ReferenceQueue?queue?=?new?ReferenceQueue(); PhantomReference<byte[]>?reference?=?new?PhantomReference<byte[]>(new?byte[1],?queue); System.out.println(reference.get());創建虛引用對象,我們除了把包裹的對象傳了進去,還傳了一個ReferenceQueue,從名字就可以看出它是一個隊列。
虛引用的特點之二就是 虛引用必須與ReferenceQueue一起使用,當GC準備回收一個對象,如果發現它還有虛引用,就會在回收之前,把這個虛引用加入到與之關聯的ReferenceQueue中。
我們來用代碼實踐下吧:
?
ReferenceQueue?queue?=?new?ReferenceQueue(); List<byte[]>?bytes?=?new?ArrayList<>(); PhantomReference<Student>?reference?=?new?PhantomReference<Student>(new?Student(),queue); new?Thread(()?->?{for?(int?i?=?0;?i?<?100;i++?)?{bytes.add(new?byte[1024?*?1024]);} }).start();new?Thread(()?->?{while?(true)?{Reference?poll?=?queue.poll();if?(poll?!=?null)?{System.out.println("虛引用被回收了:"?+?poll);}} }).start(); Scanner?scanner?=?new?Scanner(System.in); scanner.hasNext(); }運行結果:
Student?被回收了 虛引用被回收了:java.lang.ref.PhantomReference@1ade6f1我們簡單的分析下代碼:?
第一個線程往集合里面塞數據,隨著數據越來越多,肯定會發生GC。?
第二個線程死循環,從queue里面拿數據,如果拿出來的數據不是null,就打印出來。
從運行結果可以看到:當發生GC,虛引用就會被回收,并且會把回收的通知放到ReferenceQueue中。
虛引用有什么用呢?在NIO中,就運用了虛引用管理堆外內存。
總結
以上是生活随笔為你收集整理的Java 强、弱、软、虚,你属于哪一种?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你知道Spring是怎么解析配置类的吗?
- 下一篇: 只需 4 步,自己搞个 Spring B