成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

第7項:清除過期對象的引用

stormgens / 2466人閱讀

摘要:具有垃圾收集功能的編程語言中的內(nèi)存泄漏更恰當?shù)胤Q為無意識的對象保留是隱蔽的。清空對象引用應(yīng)該是一種例外,而不是一種規(guī)范行為。消除過期引用最好的方法是讓包含該引用的變量結(jié)束其生命周期。

??當你從手工管理內(nèi)存的語言(比如C或者C++)轉(zhuǎn)換到具有垃圾回收功能的語言的時候,程序猿的工作就會變得更加容易,因為當你用完了對象之后,他們就會被自動回收。當你第一次經(jīng)歷對象回收功能的時候,會覺得這簡直有點不可思議。這很容易給你留下這樣的印象,認為自己不再需要考慮內(nèi)存管理的事情了,其實不然。

??考慮下面這個簡單的棧實現(xiàn)的例子:

// Can you spot the "memory leak"?
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }
    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }
    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }
    /**
     * Ensure space for at least one more element, roughly
     * doubling the capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

??這個程序沒有明顯的錯誤(它的通用版本請見29項)。無論如何測試,它都會成功地通過每一項測試,但是這個程序中隱藏著一個問題。簡而言之,改程序存在“內(nèi)存泄漏”,由于垃圾收集器的活動增加或者內(nèi)存占用增加,程序性能的降低會逐漸表現(xiàn)出來。在極端的情況下,這種內(nèi)存泄漏會導(dǎo)致磁盤分頁(Disk Paging),甚至導(dǎo)致程序失敗并出現(xiàn)OutOfMemoryError,但這種失敗情形相對比較少見。

??那么,程序中哪里發(fā)生了內(nèi)存泄漏呢?如果一個棧先是增長,然后再收縮,那么,從棧中彈出來的對象將不會被當做垃圾回收,即使使用棧的程序不再引用這些對象,它們也不會回收,因為,棧內(nèi)部維護著對這些對象的過期引用(obsolete references),所謂的過期引用,是指永遠也不會再被解除的引用。在本例中,凡是在element數(shù)組的“活動部分”(active portion)之外的任何引用都是過期的?;顒硬糠质侵竐lement中下標小于size的那些元素。

??具有垃圾收集功能的編程語言中的內(nèi)存泄漏(更恰當?shù)胤Q為無意識的對象保留)是隱蔽的。如果無意中保留了對象引用,則不僅將該對象從垃圾回收中排除,而且該對象引用的任何對象也是如此,依此類推。即使無意中保留了少量對象引用,也會阻止許多對象被垃圾回收器收集,對性能可能產(chǎn)生很大影響。

??這類問題的修復(fù)方法很簡單:一旦對象引用已經(jīng)過期,只需要清空這些引用即可。對于上述例子中的Stack類而言,只要一個單元被彈出棧,指向它的引用就過期了,pop方法的修訂版本如下所示:

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; // Eliminate obsolete reference
    return result;
}

??清空過期引用的另一個好處是,如果它們以后又被錯誤地解除引用,程序就會立即拋出NullPointerException異常,而不是悄悄地錯誤運行下去。盡快檢測出程序中的錯誤總是有益的。

??當程序員第一次被類似這樣的問題困擾的時候,它們往往會過分小心:對于每一個對象的引用,一旦程序不再用到它,就把它清空。其實這樣做即沒必要,也不是我們所期望的,因為這樣做會把程序代碼弄得很亂。清空對象引用應(yīng)該是一種例外,而不是一種規(guī)范行為。消除過期引用最好的方法是讓包含該引用的變量結(jié)束其生命周期。如果你是在最緊湊的作用域范圍內(nèi)定義每一個變量(第57項),這種情形就會自然而然地發(fā)生。

??那么,何時應(yīng)該清空引用呢?Stack類的哪方面特性使它易于遭受內(nèi)存泄漏的影響呢?簡而言之,問題在于,Stack類自己管理內(nèi)存(manage its own memory)、存儲池(storage pool)包含了elements數(shù)組(對象引用單元,而不是對象本身)的元素。數(shù)組活動區(qū)域(同前面的定義)中的元素是已分配的(allocated),而數(shù)組其余部分的元素則是自由的(free)。但是垃圾回收器無法知道這一點;對于垃圾回收器而言,elements數(shù)組中的所有對象引用都同等有效。只有程序猿知道數(shù)組的非活動部分是不重要的。程序猿可以把這個情況告知垃圾回收器,做法很簡單:一旦數(shù)組元素變成了非活動部分的一部分,程序猿就手動清空這些數(shù)組元素。

??通常來說,只要類是自己管理內(nèi)存,程序猿就應(yīng)該警惕內(nèi)存泄漏問題。一旦元素被釋放掉,則該元素中包含的任何對象引用都應(yīng)該被清空。

??內(nèi)存泄漏的另一個常見來源是緩存。一旦你把對象引用放到緩存中,它就很容易被遺忘掉,從而使得它在很長一段時間沒有使用,但是卻仍然留在緩存中。對于這個問題,這里有好幾種解決方案。如果你正好要實現(xiàn)這樣的緩存,只要在緩存之外存在對某個項的鍵的引用,該項就有意義,那么就可以用WeakHashMap代表緩存,當緩存中的項過期之后,它們就會自動被刪除。記住只有當所要的緩存項的生命周期是由該鍵的外部引用而不是由值決定時,WeakHashMap才有用處。

??更為常見的情形則是,“緩存項的生命周期是否有意義”并不是很容易確定,隨著時間的推移,其中的項會變得越來越?jīng)]有價值。在這種情況下,緩存應(yīng)該時不時地清除掉沒用的項。這項清除工作可以由一個后臺線程(可能是Timer或者ScheduledThreadPoolExecutor)來完成,或者也可以在給緩存添加新項的時候順便進行清理。LinkedHashMap類利用它的removeEldestEntry方法可以很容易地實現(xiàn)后一種方案。對于更加復(fù)雜的緩存,必須直接使用java.lang.ref。

??內(nèi)存泄漏的第三個常見來源是監(jiān)聽器和其他回調(diào)。如果你實現(xiàn)了一個API,客戶端在這個API中注冊回調(diào),卻沒有顯示地取消注冊,那么除非你采取某些動作,否則他們就會積累下來。確?;卣{(diào)立即被當做垃圾回收的最佳方法是只保存他們的弱引用(weak reference),例如,只將它們保存成WeakHashMap中的鍵。

??由于內(nèi)存泄漏通常不會表現(xiàn)出明顯的失敗跡象,所以他們可以在一個系統(tǒng)中存在很多年。往往只有通過仔細檢查代碼,或者借助于Heap剖析工具(Heap Profiler)才能發(fā)現(xiàn)內(nèi)存泄漏問題。因此,如果能在內(nèi)存泄漏發(fā)生之前就知道如何預(yù)測此類問題,并阻止它們發(fā)生,那是最好不過的了。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/77468.html

相關(guān)文章

  • 二章 創(chuàng)建和銷毀對象

    摘要:一個類可以提供一個公共靜態(tài)工廠方法,它僅僅是一第項遇到多個構(gòu)造器參數(shù)時要考慮使用構(gòu)建器靜態(tài)工廠和構(gòu)造器有個共同的局限性他們都不能很好地擴展到大量的可選參數(shù)。 ??本章涉及創(chuàng)建和銷毀對象,包括何時以及如何創(chuàng)建它們,何時以及如何避免創(chuàng)建它們,如何確保它們被及時銷毀,以及如何管理在銷毀之前必須進行的清理操作。 第1項:用靜態(tài)工廠方法代替構(gòu)造器 ??類允許客戶端獲取實例的傳統(tǒng)方法是提供公共構(gòu)造...

    Jeffrrey 評論0 收藏0
  • Effective Java 3rd.Edition 翻譯

    摘要:推薦序前言致謝第一章引言第二章創(chuàng)建和銷毀對象第項用靜態(tài)工廠方法代替構(gòu)造器第項遇到多個構(gòu)造器參數(shù)時要考慮使用構(gòu)建器第項用私有構(gòu)造器或者枚舉類型強化屬性第項通過私有構(gòu)造器強化不可實例化的能力第項優(yōu)先考慮依賴注入來引用資源第項避免創(chuàng)建不必要的對象 推薦序 前言 致謝 第一章 引言 第二章 創(chuàng)建和銷毀對象 第1項:用靜態(tài)工廠方法代替構(gòu)造器 第2項:遇到多個構(gòu)造器參數(shù)時要考慮使用構(gòu)建器 第...

    KoreyLee 評論0 收藏0
  • Effective Java 三版 全文翻譯

    摘要:本章中的大部分內(nèi)容適用于構(gòu)造函數(shù)和方法。第項其他方法優(yōu)先于序列化第項謹慎地實現(xiàn)接口第項考慮使用自定義的序列化形式第項保護性地編寫方法第項對于實例控制,枚舉類型優(yōu)先于第項考慮用序列化代理代替序列化實例附錄與第版中項目的對應(yīng)關(guān)系參考文獻 effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個人業(yè)余翻譯,不合理的地方,望指正,感激...

    galois 評論0 收藏0
  • 前端知識集錦1

    摘要:原文鏈接征服前端面試,僅供學習使用前端知識集錦原型我們創(chuàng)建的每一個函數(shù),都可以有一個屬性,該屬性指向一個對象。線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高。但是線程不能夠獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。 原文鏈接:征服前端面試,僅供學習使用前端知識集錦2 1. JavaScript 1.1 原型 我們創(chuàng)建的每一個函數(shù),都可以有一個prototype...

    Paul_King 評論0 收藏0
  • 前端知識集錦1

    摘要:原文鏈接征服前端面試,僅供學習使用前端知識集錦原型我們創(chuàng)建的每一個函數(shù),都可以有一個屬性,該屬性指向一個對象。線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高。但是線程不能夠獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。 原文鏈接:征服前端面試,僅供學習使用前端知識集錦2 1. JavaScript 1.1 原型 我們創(chuàng)建的每一個函數(shù),都可以有一個prototype...

    spademan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<