亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Java 理論和實踐: 用軟引用阻止內存泄漏

2019-11-18 11:28:48
字體:
來源:轉載
供稿:網友

  在 java 理論和實踐 的 前一期文章 中,Java™ 清潔工程師 Brian Goetz 探究了弱引用(weak references),它讓您警告垃圾收集器,您想要維護一個對象的引用,而不會阻止該對象被垃圾收集。在本期文章中,他將解釋 Reference 對象的另外一種形式,即軟引用(soft references),用于幫助垃圾收集器治理內存使用和消除潛在的內存泄漏。
垃圾收集可以使 Java 程序不會出現內存泄漏,至少對于比較狹窄的 “內存泄漏” 定義來說如此,但是這并不意味著我們可以完全忽略 Java 程序中的對象生存期(lifetime)問題。當我們沒有對對象生命周期(lifecycle)引起足夠的重視或者破壞了治理對象生命周期的標準機制時,Java 程序中通常就會出現內存泄漏。例如,上一次 我們看到了,不能劃分對象的生命周期會導致,在試圖將元數據關聯到瞬時對象時出現意外的對象保持。還有一些其他的情況可以類似地忽略或破壞對象生命周期治理,并導致內存泄漏。

對象游離

一種形式的內存泄漏有時候叫做對象游離(object loitering),是通過清單 1 中的 LeakyChecksum 類來說明的,清單 1 中有一個 getFileChecksum() 方法用于計算文件內容的校驗和。getFileChecksum() 方法將文件內容讀取到緩沖區中以計算校驗和。一種更加直觀的實現簡單地將緩沖區作為 getFileChecksum() 中的本地變量分配,但是該版本比那樣的版本更加 “聰明”,不是將緩沖區緩存在實例字段中以減少內存 churn。該 “優化”通常不帶來預期的好處;對象分配比很多人期望的更便宜。(還要注重,將緩沖區從本地變量提升到實例變量,使得類若不帶有附加的同步,就不再是線程安全的了。直觀的實現不需要將 getFileChecksum() 聲明為 synchronized,并且會在同時調用時提供更好的可伸縮性。) 


清單 1. 展示 “對象游離” 的類

// BAD CODE - DO NOT EMULATE
public class LeakyChecksum {
    PRivate byte[] byteArray;
    
    public synchronized int getFileChecksum(String fileName) {
        int len = getFileSize(fileName);
        if (byteArray == null  byteArray.length < len)
            byteArray = new byte[len];
        readFileContents(fileName, byteArray);
        // calculate checksum and return it
    }
}
 


這個類存在很多的問題,但是我們著重來看內存泄漏。緩存緩沖區的決定很可能是根據這樣的假設得出的,即該類將在一個程序中被調用許多次,因此它應該更加有效,以重用緩沖區而不是重新分配它。但是結果是,緩沖區永遠不會被釋放,因為它對程序來說總是可及的(除非 LeakyChecksum 對象被垃圾收集了)。更壞的是,它可以增長,卻不可以縮小,所以 LeakyChecksum 將永久保持一個與所處理的最大文件一樣大小的緩沖區。退一萬步說,這也會給垃圾收集器帶來壓力,并且要求更頻繁的收集;為計算未來的校驗和而保持一個大型緩沖區并不是可用內存的最有效利用。

LeakyChecksum 中問題的原因是,緩沖區對于 getFileChecksum() 操作來說邏輯上是本地的,但是它的生命周期已經被人為延長了,因為將它提升到了實例字段。因此,該類必須自己治理緩沖區的生命周期??而不是讓 JVM 來治理。 


軟引用

在 前一期文章 中我們看到了,弱引用如何可以給應用程序提供當對象被程序使用時另一種到達該對象的方法,但是不會延長對象的生命周期。Reference 的另一個子類 ?? 軟引用 ?? 可滿足一個不同卻相關的目的。其中弱引用答應應用程序創建不妨礙垃圾收集的引用,軟引用答應應用程序通過將一些對象指定為 “eXPendable” 而利用垃圾收集器的幫助。盡管垃圾收集器在找出哪些內存在由應用程序使用哪些沒在使用方面做得很好,但是確定可用內存的最適當使用還是取決于應用程序。假如應用程序做出了不好的決定,使得對象被保持,那么性能會受到影響,因為垃圾收集器必須更加辛勤地工作,以防止應用程序消耗掉所有內存。

高速緩存是一種常見的性能優化,答應應用程序重用以前的計算結果,而不是重新進行計算。高速緩存是 CPU 利用和內存使用之間的一種折衷,這種折衷理想的平衡狀態取決于有多少內存可用。若高速緩存太少,則所要求的性能優勢無法達到;若太多,則性能會受到影響,因為太多的內存被用于高速緩存上,導致其他用途沒有足夠的可用內存。因為垃圾收集器比應用程序更適合決定內存需求,所以應該利用垃圾收集器在做這些決定方面的幫助,這就是件引用所要做的。 

假如一個對象惟一剩下的引用是弱引用或軟引用,那么該對象是軟可及的(softly reachable)。垃圾收集器并不像其收集弱可及的對象一樣盡量地收集軟可及的對象,相反,它只在真正 “需要” 內存時才收集軟可及的對象。軟引用對于垃圾收集器來說是這樣一種方式,即 “只要內存不太緊張,我就會保留該對象。但是假如內存變得真正緊張了,我就會去收集并處理這個對象。” 垃圾收集器在可以拋出 OutOfMemoryError 之前需要清除所有的軟引用。 

通過使用一個軟引用來治理高速緩存的緩沖區,可以解決 LeakyChecksum 中的問題,如清單 2 所示?,F在,只要不是非凡需要內存,緩沖區就會被保留,但是在需要時,也可被垃圾收集器回收: 


清單 2. 用軟引用修復 LeakyChecksum

public class CachingChecksum {
    private SoftReference<byte[]> bufferRef;
    
    public synchronized int getFileChecksum(String fileName) {
        int len = getFileSize(fileName);
        byte[] byteArray = bufferRef.get();
        if (byteArray == null  byteArray.length < len) {
            byteArray = new byte[len];
            bufferRef.set(byteArray);
        }
        readFileContents(fileName, byteArray);
        // calculate checksum and return it
    }
}
 


一種廉價的緩存

CachingChecksum 使用一個軟引用來緩存單個對象,并讓 JVM 處理從緩存中取走對象時的細節。類似地,軟引用也經常用于 GUI 應用程序中,用于緩存位圖圖形。是否可使用軟引用的要害在于,應用程序是否可從大量緩存的數據恢復。 

假如需要緩存不止一個對象,您可以使用一個 Map,但是可以選擇如何使用軟引用。您可以將緩存作為 Map<K, SoftReference<V>> 或 SoftReference<Map<K,V>> 治理。后一種選項通常更好一些,因為它給垃圾收集器帶來的工作更少,并且答應在非凡需要內存時以較少的工作回收整個緩存。弱引用有時會錯誤地用于取代軟引用,用于構建緩存,但是這會導致差的緩存性能。在實踐中,弱引用將在對象變得弱可及之后被很快地清除掉 ?? 通常是在緩存的對象再次用到之前 ?? 因為小的垃圾收集運行得很頻繁。 

對于在性能上非常依靠高速緩存的應用程序來說,軟引用是一個不管用的手段,它確實不能取代能夠提供靈活終止期、復制和事務型高速緩存的復雜的高速緩存框架。但是作為一種 “廉價(cheap and dirty)” 的高速緩存機制,它對于降低價格是很有吸引力的。 

正如弱引用一樣,軟引用也可創建為具有一個相關的引用隊列,引用在被垃圾收集器清除時進入隊列。引用隊列對于軟引用來說,沒有對弱引用那么有用,但是它們可以用于發出治理警報,說明應用程序開始缺少內存。 

垃圾收集器如何處理 References

弱引用和軟引用都擴展了抽象的 Reference 類(虛引用(phantom references)也一樣,這將在以后的文章中介紹)。引用對象被垃圾收集器非凡地看待。垃圾收集器在跟蹤堆期間碰到一個 Reference 時,不會標記或跟蹤該引用對象,而是在已知活躍的 Reference 對象的隊列上放置一個 Reference。在跟蹤之后,垃圾收集器就識別軟可及的對象 ?? 這些對象上除了軟引用外,沒有任何強引用。垃圾收集器然??根據當前收集所回收的內存總量和其他策略考慮因素,判定軟引用此時是否需要被清除。將被清除的軟引用假如具有相應的引用隊列,就會進入隊列。其余的軟可及對象(沒有清除的對象)然后被看作一個根集(root set),堆跟蹤繼續使用這些新的根,以便通過活躍的軟引用而可及的對象能夠被標記。 

處理軟引用之后,弱可及對象的集合被識別 ?? 這樣的對象上不存在強引用或軟引用。這些對象被清除和加入隊列。所有 Reference 類型在加入隊列之前被清除,所以處理事后檢查(post-mortem)清除的線程永遠不會具有 referent 對象的訪問權,而只具有 Reference 對象的訪問權。因此,當 References 與引用隊列一起使用時,通常需要細分適當的引用類型,并將它直接用于您的設計中(與 WeakHashMap 一樣,它的 Map.Entry 擴展了 WeakReference)或者存儲對需要清除的實體的引用。 

引用處理的性能成本

引用對象給垃圾收集過程帶來了一些附加的成本。每一次垃圾收集,都必須構造活躍 Reference 對象的一個列表,而且每個引用都必須做適當的處理,這給每次收集添加了一些每個 Reference 的開銷,而不管該 referent 此時是否被收集。Reference 對象本身服從于垃圾收集,并且可在 referent 之前被收集,在這樣的情況下,它們沒有加入隊列。 

基于數組的集合

當數組用于實現諸如堆?;颦h形緩沖區之類的數據結構時,會出現另一種形式的對象游離。清單 3 中的 LeakyStack 類展示了用數組實現的堆棧的實現。在 pop() 方法中,在頂部指針遞減之后,elements 仍然會保留對將彈出堆棧的對象的引用。這意味著,該對象的引用對程序來說仍然可及(即使程序實際上不會再使用該引用),這會阻止該對象被垃圾收集,直到該位置被未來的 push() 重用。 


清單 3. 基于數組的集合中的對象游離

public class LeakyStack {
    private Object[] elements = new Object[MAX_ELEMENTS];
    private int size = 0;
    
    public void push(Object o) { elements[size++] = o; }
    
    public Object pop() { 
        if (size == 0)
            throw new EmptyStackException();
        else {
            Object result = elements[--size];
            // elements[size+1] = null;
            return result;
        } 
    }
}
 


修復這種情況下的對象游離的方法是,當對象從堆棧彈出之后,就消除它的引用,如清單 3 中注釋掉的行所示。但是這種情況 ?? 由類治理其自己的內存 ?? 是一種非常少見的情況,即顯式地消除不再需要的對象是一個好主意。大部分時候,認為不應該使用的強行消除引用根本不會帶來性能或內存使用方面的收益,通常是導致更差的性能或者 NullPointerException。該算法的一個鏈接實現不會存在這個問題。在鏈接實現中,鏈接節點(以及所存儲的對象的引用)的生命期將被自動與對象存儲在集合中的期間綁定在一起。弱引用可用于解決這個問題 ?? 維護弱引用而不是強引用的一個數組 ?? 但是在實際中,LeakyStack 治理它自己的內存,因此負責確保對不再需要的對象的引用被清除。使用數組來實現堆?;蚓彌_區是一種優化,可以減少分配,但是會給實現者帶來更大的負擔,需要仔細地治理存儲在數組中的引用的生命期。 


結束語

與弱引用一樣,軟引用通過利用垃圾收集器在作出緩存回收決策方面的幫助,有助于防止應用程序出現對象游離。只有當應用程序可以忍受大量軟引用的對象時,軟引用才適合使用。 


參考資料:

您可以參閱本文在 developerWorks 全球站點上的 英文原文。


“Java 理論與實踐: 用弱引用堵住內存泄漏”:上一期的 Java 理論和實踐 介紹了 SoftReference 的同類 WeakReference。


“關注性能: 調優垃圾收集”:Kirk Pepperdine 和 Jack Shirazi 論證了,即使緩慢的內存泄漏最終也會給垃圾收集器帶來巨大的壓力。


Reference objects and garbage collection:這篇論文出自 Sun,寫于 Reference 對象被添加到類庫之后不久,描述了垃圾收集器如何對待 Reference 對象。

Java 理論和實踐:Brian Goetz 撰寫的完整系列。

Java 技術專區:數百篇關于 Java 編程各個方面的文章。

獲得產品和技術

JTune:這個免費的 JTune 工具可以利用 GC 日志并以圖表形式顯示堆大小、GC 期間和其他有用的內存治理數據。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品吹潮在线观看| 国产精品爽爽爽| 日韩最新免费不卡| 日韩av电影手机在线| 97免费在线视频| 色妞色视频一区二区三区四区| 4438全国亚洲精品在线观看视频| 成人性生交xxxxx网站| 久久久久久国产免费| 国产精品88a∨| 亚洲iv一区二区三区| 亚洲va欧美va在线观看| 亚洲成av人片在线观看香蕉| 中文字幕在线看视频国产欧美在线看完整| 亚洲视频一区二区三区| 国产精品91在线| 狠狠躁夜夜躁人人躁婷婷91| 国产一区二区三区四区福利| 久久久最新网址| 日韩成人激情影院| 久久中文久久字幕| 影音先锋日韩有码| 精品国产一区二区在线| 亚洲美女av在线播放| 美女视频黄免费的亚洲男人天堂| 午夜欧美大片免费观看| 日韩欧美有码在线| 欧美另类第一页| 欧美日韩成人免费| 亚洲色图色老头| 亚洲精品国产综合久久| 亚洲国产精品va在线| 久久福利视频导航| 国产狼人综合免费视频| 欧美成人精品在线播放| 国产亚洲精品91在线| 亚洲欧美制服中文字幕| 久久影视电视剧免费网站清宫辞电视| 国产精品偷伦一区二区| 日韩欧美在线观看视频| 日韩电影中文字幕av| 国产日产欧美a一级在线| 亚洲天堂第一页| 成人av电影天堂| www国产亚洲精品久久网站| 国产日韩欧美91| 国产香蕉97碰碰久久人人| 伊人伊成久久人综合网站| 欧美激情亚洲精品| 亚洲品质视频自拍网| 国产精品第一页在线| 精品国内亚洲在观看18黄| 韩国国内大量揄拍精品视频| 精品国内亚洲在观看18黄| 中文综合在线观看| 97碰碰碰免费色视频| 日韩大片在线观看视频| 亚洲国产免费av| 日韩成人网免费视频| 91色在线观看| 成人福利网站在线观看| 精品无人国产偷自产在线| 欧美极品少妇全裸体| 精品亚洲一区二区三区在线播放| 欧美激情中文字幕在线| 国产一区二区免费| 91久久精品国产91久久性色| 日韩精品免费在线播放| 精品亚洲一区二区三区四区五区| 精品久久香蕉国产线看观看gif| 在线播放日韩专区| 国产精品成人免费电影| 久久国产精品首页| 日韩激情视频在线| 国产精品老牛影院在线观看| 亚洲最大成人在线| 久久琪琪电影院| 国产精品视频导航| 成人xxxx视频| 国产suv精品一区二区| 亚洲欧美日韩在线高清直播| 亚洲人成网站免费播放| 欧美电影院免费观看| 欧美精品激情在线观看| 国产精品色视频| 欧美国产日韩二区| 亚洲精品日韩丝袜精品| 8x拔播拔播x8国产精品| 97视频在线观看视频免费视频| 日本欧美爱爱爱| 国产精品白丝av嫩草影院| 久久视频免费观看| 在线激情影院一区| 欧美一区二区.| 国内精品小视频| 亚洲男人第一网站| 中文字幕亚洲激情| 国产精品天天狠天天看| 日韩一区二区三区国产| 亚洲毛茸茸少妇高潮呻吟| 91精品在线一区| 国产精品免费小视频| 日韩中文字在线| 久久人人爽人人爽人人片亚洲| 免费成人高清视频| 欧美日韩国产中文字幕| 91成品人片a无限观看| 一个人看的www久久| 97精品视频在线观看| 国产精品无av码在线观看| 日韩电影在线观看永久视频免费网站| 欧美黑人一级爽快片淫片高清| 日韩高清电影免费观看完整| 亚洲男女自偷自拍图片另类| 国产aⅴ夜夜欢一区二区三区| 国产精品自产拍在线观| 亚洲欧美在线x视频| 欧美理论电影网| 久久伊人91精品综合网站| 亚洲视频欧美视频| 精品国产乱码久久久久久天美| 欧美一级视频一区二区| 欧美网站在线观看| 8x拔播拔播x8国产精品| 福利二区91精品bt7086| 7777精品久久久久久| 国产成人精品一区二区| 大伊人狠狠躁夜夜躁av一区| 亚洲美女自拍视频| 一区二区三区 在线观看视| 在线精品播放av| 亚洲网站在线看| 成人免费网视频| 国产丝袜一区二区三区免费视频| 国产成人短视频| 第一福利永久视频精品| 国产精品视频一区二区高潮| 久久久噜噜噜久久| 亚洲国产精品女人久久久| 久久精品成人动漫| 粉嫩老牛aⅴ一区二区三区| 尤物yw午夜国产精品视频明星| 热久久视久久精品18亚洲精品| 亚洲精品视频播放| 国产精品劲爆视频| 成人午夜在线观看| 亚洲欧美一区二区三区在线| 日韩美女免费视频| 中文字幕一区日韩电影| 亚洲区一区二区| 国产精品福利无圣光在线一区| 亚洲女人天堂网| 成人高h视频在线| 日韩**中文字幕毛片| 欧美激情一区二区三区高清视频| 午夜精品久久久久久久久久久久久| 国产精品极品美女在线观看免费| 亚洲精品久久久久国产| 午夜精品一区二区三区视频免费看| 亚洲精品国产suv| 国产69精品久久久久99| 国产精品福利在线观看| 国产欧美日韩丝袜精品一区| 日韩av在线影视|