亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

深入了解JAVA 軟引用

瀏覽:27日期:2022-08-27 08:25:07

定義

軟引用是使用SoftReference創(chuàng)建的引用,強度弱于強引用,被其引用的對象在內(nèi)存不足的時候會被回收,不會產(chǎn)生內(nèi)存溢出。

說明

軟引用,顧名思義就是比較“軟”一點的引用。

當(dāng)一個對象與GC Roots之間存在強引用時,無論何時都不會被GC回收掉。如果一個對象與GC Roots之間沒有強引用與其關(guān)聯(lián)而存在軟引用關(guān)聯(lián)時,那么垃圾回收器對它的態(tài)度就取決于內(nèi)存的緊張程度了。如果內(nèi)存空間足夠,垃圾回收器就不會回收這個對象,但如果內(nèi)存空間不足了,它就難逃被回收的厄運。

如果一個對象與GC Roots之間不存在強引用,但是存在軟引用,則稱這個對象為軟可達(soft reachable)對象。

在垃圾回收器沒有回收它的時候,軟可達對象就像強可達對象一樣,可以被程序正常訪問和使用,但是需要通過軟引用對象間接訪問,需要的話也能重新使用強引用將其關(guān)聯(lián)。所以軟引用適合用來做內(nèi)存敏感的高速緩存。

String s = new String('Frank'); // 創(chuàng)建強引用與String對象關(guān)聯(lián),現(xiàn)在該String對象為強可達狀態(tài)SoftReference<String> softRef = new SoftReference<String>(s); // 再創(chuàng)建一個軟引用關(guān)聯(lián)該對象s = null; // 消除強引用,現(xiàn)在只剩下軟引用與其關(guān)聯(lián),該String對象為軟可達狀態(tài)s = softRef.get(); // 重新關(guān)聯(lián)上強引用

這里變量s持有對字符串對象的強引用,而softRef持有對該對象的軟引用,所以當(dāng)執(zhí)行s = null后,字符串對象就只剩下軟引用了,這時如果因為內(nèi)存不足發(fā)生Full GC,就會把這個字符串對象回收掉。

注意,在垃圾回收器回收一個對象前,SoftReference類所提供的get方法會返回Java對象的強引用,一旦垃圾線程回收該對象之后,get方法將返回null。所以在獲取軟引用對象的代碼中,一定要先判斷返回是否為null,以免出現(xiàn)NullPointerException異常而導(dǎo)致應(yīng)用崩潰。

下面的代碼會讓s再次持有對象的強引用:

s = softRef.get();

如果在softRef指向的對象被回收前,用強引用指向該對象,那這個對象又會變成強可達。

來看一個使用SoftReference的栗子:

public class TestA { static class OOMClass{ private int[] oom = new int[1024 * 100];// 100KB } public static void main(String[] args) throws InterruptedException { ReferenceQueue<OOMClass> queue = new ReferenceQueue<>(); List<SoftReference> list = new ArrayList<>(); while(true){ for (int i = 0; i < 100; i++) {list.add(new SoftReference<OOMClass>(new OOMClass(), queue)); } Thread.sleep(500); } }}

注意,ReferenceQueue中聲明的類型為OOMClass,即與SoftReference引用的類型一致。

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

-verbose:gc -Xms4m -Xmx4m -Xmn2m

運行結(jié)果:

[GC (Allocation Failure) 1017K->432K(3584K), 0.0017239 secs][GC (Allocation Failure) 1072K->472K(3584K), 0.0099237 secs][GC (Allocation Failure) 1323K->1296K(3584K), 0.0009528 secs][GC (Allocation Failure) 2114K->2136K(3584K), 0.0009951 secs][Full GC (Ergonomics) 2136K->1992K(3584K), 0.0040658 secs][Full GC (Ergonomics) 2807K->2791K(3584K), 0.0036280 secs][Full GC (Allocation Failure) 2791K->373K(3584K), 0.0032477 secs][Full GC (Ergonomics) 2786K->2773K(3584K), 0.0034554 secs][Full GC (Allocation Failure) 2773K->373K(3584K), 0.0032667 secs][Full GC (Ergonomics) 2798K->2775K(3584K), 0.0036231 secs][Full GC (Allocation Failure) 2775K->375K(3584K), 0.0055482 secs][Full GC (Ergonomics) 2799K->2776K(3584K), 0.0031358 secs]...省略n次GC信息

在TestA中,我們使用死循環(huán)不斷的往list中添加新對象,如果是強引用,會很快因為內(nèi)存不足而拋出OOM,因為這里的堆內(nèi)存大小設(shè)置為了4M,而一個對象就有100KB,一個循環(huán)添加100個對象,也就是差不多10M,顯然一個循環(huán)都跑不完就會內(nèi)存不足,而這里,因為使用的是軟引用,所以JVM會在內(nèi)存不足的時候?qū)④浺没厥盏簟?/p>

[Full GC (Allocation Failure) 2791K->373K(3584K), 0.0032477 secs]

從這一條可以看出,在內(nèi)存不足發(fā)生Full GC時,回收掉了大部分的軟引用指向的對象,釋放了大量的內(nèi)存。

因為這里新生代只分配了2M,所以很快就會發(fā)生GC,如果你的程序運行沒有看到這個結(jié)果,請先確認一下虛擬機參數(shù)是否設(shè)置正確,如果設(shè)置正確還是沒有看到,那么將循環(huán)次數(shù)由1000改為10000或者100000在試試看。

應(yīng)用場景

軟引用關(guān)聯(lián)的對象,只有在內(nèi)存不足的時候JVM才會回收該對象。這一點可以很好地用來解決OOM的問題,并且這個特性很適合用來實現(xiàn)緩存:比如網(wǎng)頁緩存、圖片緩存等。

現(xiàn)在考慮這樣一個場景 ,在很多應(yīng)用中,都會出現(xiàn)大量的默認圖片,比如說QQ的默認頭像,應(yīng)用內(nèi)的默認圖標(biāo)等等,這些圖片很多地方會用到。

如果每次都去讀取圖片,由于讀取文件速度較慢,大量重復(fù)的讀取會導(dǎo)致性能下降。所以可以考慮將圖片緩存起來,需要的時候直接從內(nèi)存中讀取。但是,由于圖片占用內(nèi)存空間比較大,緩存的圖片過多會占用比較多的內(nèi)存,就可能比較容易發(fā)生OOM。這時候,軟引用就派得上用場了。

注意,SoftReference對象是用來保存軟引用的,但它同時也是一個Java對象。所以,當(dāng)軟可及對象被回收之后,雖然這個SoftReference對象的get()方法返回null,但SoftReference對象本身并不是null,而此時這個SoftReference對象已經(jīng)不再具有存在的價值,需要一個適當(dāng)?shù)那宄龣C制,避免大量SoftReference對象帶來的內(nèi)存泄漏。

ReferenceQueue就是用來保存這些需要被清理的引用對象的。軟引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊列中。

下面用SoftReference來實現(xiàn)一個簡單的緩存類:

public class SoftCache<T> { // 引用隊列 private ReferenceQueue<T> referenceQueue = new ReferenceQueue<>(); // 保存軟引用集合,在引用對象被回收后銷毀 private List<Reference<T>> list = new ArrayList<>(); // 添加緩存對象 public synchronized void add(T obj){ // 構(gòu)建軟引用 Reference<T> reference = new SoftReference<T>(obj, referenceQueue); // 加入列表中 list.add(reference); } // 獲取緩存對象 public synchronized T get(int index){ // 先對無效引用進行清理 clear(); if (index < 0 || list.size() < index){ return null; } Reference<T> reference = list.get(index); return reference == null ? null : reference.get(); } public int size(){ return list.size(); } @SuppressWarnings('unchecked') private void clear(){ Reference<T> reference; while (null != (reference = (Reference<T>) referenceQueue.poll())){ list.remove(reference); } }}

然后測試一下這個緩存類:

public class SoftCacheTest { private static int num = 0; public static void main(String[] args){ SoftCache<OOMClass> softCache = new SoftCache<>(); for (int i = 0; i < 40; i++) { softCache.add(new OOMClass('OOM Obj-' + ++num)); } System.out.println(softCache.size()); for (int i = 0; i < softCache.size(); i++) { OOMClass obj = softCache.get(i); System.out.println(obj == null ? 'null' : obj.name); } System.out.println(softCache.size()); } static class OOMClass{ private String name; private int[] oom = new int[1024 * 100];// 100KB public OOMClass(String name) { this.name = name; } }}

仍使用之前的虛擬機參數(shù):

-verbose:gc -Xms4m -Xmx4m -Xmn2m

運行結(jié)果:

[GC (Allocation Failure) 1017K->432K(3584K), 0.0012236 secs][GC (Allocation Failure) 1117K->496K(3584K), 0.0016875 secs][GC (Allocation Failure) 1347K->1229K(3584K), 0.0015059 secs][GC (Allocation Failure) 2047K->2125K(3584K), 0.0018090 secs][Full GC (Ergonomics) 2125K->1994K(3584K), 0.0054759 secs][Full GC (Ergonomics) 2822K->2794K(3584K), 0.0023167 secs][Full GC (Allocation Failure) 2794K->376K(3584K), 0.0036056 secs][Full GC (Ergonomics) 2795K->2776K(3584K), 0.0042365 secs][Full GC (Allocation Failure) 2776K->376K(3584K), 0.0035122 secs][Full GC (Ergonomics) 2795K->2776K(3584K), 0.0054760 secs][Full GC (Allocation Failure) 2776K->376K(3584K), 0.0036965 secs][Full GC (Ergonomics) 2802K->2777K(3584K), 0.0044513 secs][Full GC (Allocation Failure) 2777K->376K(3584K), 0.0041400 secs][Full GC (Ergonomics) 2796K->2777K(3584K), 0.0025255 secs][Full GC (Allocation Failure) 2777K->376K(3584K), 0.0037690 secs][Full GC (Ergonomics) 2817K->2777K(3584K), 0.0037759 secs][Full GC (Allocation Failure) 2777K->377K(3584K), 0.0042416 secs]緩存列表大小:40OOM Obj-37OOM Obj-38OOM Obj-39OOM Obj-40緩存列表大小:4

可以看到,緩存40個軟引用對象之后,如果一次性全部存儲,顯然內(nèi)存大小無法滿足,所以在不斷創(chuàng)建軟引用對象的過程中,不斷發(fā)生GC來進行垃圾回收,最終只有4個軟引用未被清理掉。

強引用與軟引用對比

沒有對比就沒有傷害,來將強引用和軟引用對比一下:

public class Test { static class OOMClass{ private int[] oom = new int[1024]; } public static void main(String[] args) { testStrongReference(); //testSoftReference(); } public static void testStrongReference(){ List<OOMClass> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(new OOMClass()); } } public static void testSoftReference(){ ReferenceQueue<OOMClass> referenceQueue = new ReferenceQueue<>(); List<SoftReference> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { OOMClass oomClass = new OOMClass(); list.add(new SoftReference(oomClass, referenceQueue)); oomClass = null; } }}

運行testStrongReference方法的結(jié)果如下:

[GC (Allocation Failure) 1019K->384K(3584K), 0.0033595 secs][GC (Allocation Failure) 1406K->856K(3584K), 0.0013098 secs][GC (Allocation Failure) 1880K->1836K(3584K), 0.0014382 secs][Full GC (Ergonomics) 1836K->1756K(3584K), 0.0039761 secs][Full GC (Ergonomics) 2778K->2758K(3584K), 0.0021269 secs][Full GC (Ergonomics) 2779K->2770K(3584K), 0.0016329 secs][Full GC (Ergonomics) 2779K->2775K(3584K), 0.0023157 secs][Full GC (Ergonomics) 2775K->2775K(3584K), 0.0015927 secs][Full GC (Ergonomics) 3037K->3029K(3584K), 0.0025071 secs][Full GC (Ergonomics) 3067K->3065K(3584K), 0.0017529 secs][Full GC (Allocation Failure) 3065K->3047K(3584K), 0.0033445 secs][Full GC (Ergonomics) 3068K->3059K(3584K), 0.0016623 secs][Full GC (Ergonomics) 3070K->3068K(3584K), 0.0028357 secs][Full GC (Allocation Failure) 3068K->3068K(3584K), 0.0017616 secs]java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid3352.hprof ...Exception in thread 'main' java.lang.OutOfMemoryError: Java heap spaceHeap dump file created [3855956 bytes in 0.017 secs][Full GC (Ergonomics) 3071K->376K(3584K), 0.0032068 secs]at reference.Test$OOMClass.<init>(Test.java:11)at reference.Test.testStrongReference(Test.java:22)at reference.Test.main(Test.java:15)

Process finished with exit code 1

可以看到,很快就拋出了OOM,原因是Java heap space,也就是堆內(nèi)存不足。

如果運行testSoftReference方法,將會得到如下結(jié)果:

[GC (Allocation Failure) 1019K->464K(3584K), 0.0019850 secs][GC (Allocation Failure) 1484K->844K(3584K), 0.0015920 secs][GC (Allocation Failure) 1868K->1860K(3584K), 0.0043236 secs][Full GC (Ergonomics) 1860K->1781K(3584K), 0.0044581 secs][Full GC (Ergonomics) 2802K->2754K(3584K), 0.0041726 secs][Full GC (Ergonomics) 2802K->2799K(3584K), 0.0031293 secs][Full GC (Ergonomics) 3023K->3023K(3584K), 0.0024830 secs][Full GC (Ergonomics) 3071K->3068K(3584K), 0.0035025 secs][Full GC (Allocation Failure) 3068K->405K(3584K), 0.0040672 secs][GC (Allocation Failure) 1512K->1567K(3584K), 0.0011170 secs][Full GC (Ergonomics) 1567K->1496K(3584K), 0.0048438 secs]

可以看到,并沒有拋出OOM,而是進行多次了GC,可以明顯的看到這一條:

[Full GC (Allocation Failure) 3068K->405K(3584K), 0.0040672 secs]

當(dāng)內(nèi)存不足時進行了一次Full GC,回收了大部分內(nèi)存空間,也就是將大部分軟引用指向的對象回收掉了。

小結(jié)

軟引用弱于強引用 軟引用指向的對象會在內(nèi)存不足時被垃圾回收清理掉 JVM會優(yōu)先回收長時間閑置不用的軟引用對象,對那些剛剛構(gòu)建的或剛剛使用過的軟引用對象會盡可能保留 軟引用可以有效的解決OOM問題 軟引用適合用作非必須大對象的緩存

至此,本篇就告一段落了,這里只簡單的介紹了軟引用的作用以及用法。其實軟引用并沒有這么好,它的使用有一些可能是致命的缺點,如果想要更深入的了解軟引用的運行原理以及軟引用到底是在何時進行回收,又是如何進行回收的話,可以查看翻閱后續(xù)的章節(jié)。

以上就是深入了解JAVA 軟引用的詳細內(nèi)容,更多關(guān)于JAVA 軟引用的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 国产一级αv片免费观看 | 亚洲性网| 国产成人高清亚洲一区久久 | 黄色视片 | 久色亚洲| 黄色视屏日本 | 久久国产香蕉视频 | 国产精品亚洲一区二区三区在线观看 | 亚洲精品国产手机 | 伊人黄网 | 中国一级黄色片 | 正在播放国产无套露脸 | 亚洲一区二区三区不卡视频 | 国产黄色a三级三级三级 | 亚洲国产日韩在线人成蜜芽 | 婷婷综合在线观看丁香 | 国产萝控精品福利视频免费 | 免费三级毛片 | 日韩在线视频一区二区三区 | 久久国产成人福利播放 | 女人被狂躁视频免费网站 | 日韩美女一级片 | 国产在线一区观看 | 黄色一级片免费播放 | 亚洲精品入口一区二区在线播放 | 1769国产精品视频免费观看 | 伊人99在线观看 | 一级毛片免费观看 | 亚洲综合精品 | 国产黄色毛片 | 国产呦精品系列在线 | 免费看黄色三级毛片 | 黄色片一 | 欧美国产一区二区二区 | 九九免费精品视频在这里 | 黑人超大 | 黄色一级毛片在线观看 | 国产一级黄色 | 日本特黄视频 | 国产黄大片 | 天天看黄|