學習effective java-6創建和銷燬對象之消除過時的對象引用

 該知識點是自己從書籍中學習的筆記。

定義

  過時的引用是這樣的一個引用,該引用永遠不會被解除。按照這種定義的話,超出元素數組中的活動區域的引用都是過時的。

一個內存泄露的例子:

public class StackTest {

    public Object[] elements;

    public int size = 0;

    private static final int DEFAULT_INITIAL_CAPACITY = 16;

 

    public StackTest() {

       elements = new Object[DEFAULT_INITIAL_CAPACITY];

    }

 

    public void push(Object e) {

       ensureCapacity();

       elements[size++] = e;

    }

 

    public Object pop() {

       if (size == 0)

           throw new EmptyStackException();

       Object o =  elements[--size];

       //elements[size] = null;//消除過時的對象引用方方案

       return o;

    }

 

    /**

     * 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);

    }

    public static void main(String[] args) {

       StackTest st = new StackTest();

       st.push("1");

       st.push("2");

       st.pop();

       st.pop();

       for(Object temp : st.elements){

           if(temp != null)

           System.out.println((String)temp);

       }

    }

}

  在未採用“消除過時的對象引用”時,打印結果:

  1

  2

這不符合我們想要的結果,因爲對象已經pop了,那麼StackTest應該不存在對象了。但是存在了對象,說明內存泄露。解決的方法就是:“  

public Object pop() {

       if (size == 0)

           throw new EmptyStackException();

       Object o =  elements[--size];

       elements[size] = null;//消除過時的對象引用方案

       return o;

    }

”,再次運行就正常了。

由上可知,消除過時的對象引用,指將對象引用設置爲null,讓該引用指向的對象遊離在heap中,然後jvm會自動回收這些對象垃圾。Java雖然是可以自動回收垃圾,但是回收還是需要我們考慮。

儘管有很少部分的對象引用被保留了下來,但是將會導致有更多的對象將不會被垃圾回收。

消除過時的對象引用的好處

 1. 如果在隨後,它們又被錯誤的解除引用的話,程序就會拋出一個NullPointerException,這種方式比默默地執行錯誤的事情好。可以越早檢查出錯誤。

 2. 並不是說一旦對象引用使用完畢後,就消除這些過時的引用。消除過時的對象引用是一種例外而不是規範。使用消除過時的對象引用最好方法是包含引用的變量結束週期,最好是在最狹窄的範圍內結束。

 3.可以減輕jvm的內存使用情況。

 4.提高性能。

什麼時候清空對象引用?

1.  當一個類自己管理內存時候,我們就應該警惕內存泄露。

2.  內存泄露還有就是代碼使用了緩存。如果將一個對象引用放入緩存中的話,那麼隨着時間的延長,估計人都會忘記它,然後緩存中的該對象引用就變成了不相干。當然這種問題有解決方法:

a.  使用WeakHashMap來作爲緩存,當緩存中的實體變成過時時,實體將被自動移除。當然WeakHashMap僅僅有用的是:緩存的實體希望的生命週期是由被引用的key決定的,而不是value。

b.  使用後臺線程(a Timer or ScheduledThreadPoolExecutor)來清理緩存中不使用的實體或者添加一個具有自己處理舊對象的副作用的東西來作爲緩存(比如LinkedHashMap可以作爲緩存,因爲它的removeEldestEntry()方法,在每次put、putAll的時候,都會先清理最老的實體)。

c.  對於更復雜的緩存,你可以直接使用反射。

3.  具有監聽和回調方法的代碼可能造成內存泄露。

如果你實現了一個用戶註冊回調方法的API,但是沒有明確撤銷註冊的話,估計會造成內存泄露。可以通過這個方法解決:使用WeakHashMap的key來存儲這些不用的引用。

總結

  內存泄露是一個長期遺留下來的問題,內存泄露不像錯誤那麼明顯,基本是隻有通過運行的結果才能夠知道。目前可以通過代碼review和一些工具(heap profiler)來分析可能出現的問題,來確保不發生一些問題,這些問題不是百分之百可以發現的,所以需要加強對代碼的設計和理解。

 

發佈了42 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章