建議:消除過期的物件引用。
以下是簡單的棧實現的例子:
// 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 ensureCappacity() {
if(elements.length == size)
elements = Arrays.copy(elements, 2 * size + 1);
}
}
這段程式中並沒有很明顯的錯誤。無論如何測試,它都會成功的通過每一項測試,但是這個程式中隱藏著一個問題。不嚴格地講,這段程式有一個“記憶體洩漏”,隨著垃圾回收器活動的增加,或者由於記憶體佔用的不斷增加,程式效能的降低會逐漸表現出來。在極端的情況下,這種記憶體洩露會導致磁碟交換(Disk Paging),甚至導致程式失敗(OutOfMemoryError錯誤),但是這種失敗情形相對比較少見。
那麼,程式中哪裡發生了記憶體洩漏?如果一個棧先是增長,然後再收縮,那麼,從棧中彈出來的物件將不會被當做垃圾回收,即使使用棧的程式不再引用這些物件,他們也不會被回收。這是因為,棧內部維護著對這些物件的過期引用(obsolete referrence)。所謂過期引用,是指永遠也不會再被解除的引用。在本例中,凡是在elements陣列的“活動部分(active portion)”之外的任何引用都是過期的。活動部分是指elements中下標小於size的哪些部分。
在支援垃圾回收的語言中,記憶體洩漏是很隱蔽的(稱這類記憶體洩漏為“無意識的物件保持(unintentional object retention)”更為恰當)。如果一個物件引用被無意識地保留起來了,那麼,垃圾回收機制不僅不會處理這個物件,而且也不會處理被這個物件所引用的所有其他物件。即使只有少量的幾個物件引用被無意識的保留下來,他會有許許多多的物件被排除在垃圾回收機制之外,從而對效能造成潛在的重大影響。
這類問題的修復方法很簡單:一旦物件引用已經過期,只需清空這些引用即可。
對於上述例子中的Stack類而言,只要一個單元被彈出棧,指向他的引用過期了。pop方法的修訂版本如下所示:
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements[-- size];
elements[size] = null; // Elements obsolete reference
return elements[-- size];
}
清空過期引用的另一個好處是,如果他們以後又被錯誤的解除引用,程式就會立即丟擲NullPointerException異常,而不是悄悄地錯誤執行下去,儘快的檢測出程式中的錯誤總是有意義的。
清空物件引用應該是一種例外,而不是一種規範行為。因為過期引用最好的方法使讓包含該引用的變數結束其生命週期。如果你是在最緊湊的作用域範圍內定義每一個變數,這種情形就會自然而然的發生。
那麼,何時應該清空引用?Stack類的哪方面特性使他易於遭受記憶體洩露的影響呢?簡而言之,問題在於,Stack類自己管理記憶體(manage its own memory)。儲存池(storage pool)包含了elements陣列(物件引用單元,而不是物件本身)的元素。陣列活動區域(同前面的定義)中的元素是已分配的(allocated),而陣列其餘部分的元素則是自由的(free)。但是垃圾回收器並不知道這一點,對於垃圾回收器而言,elements陣列中的所有物件引用都同等有效。只有程式設計師知道陣列的非活動部分是不重要的。程式設計師可以把這個情況告知垃圾回收器,做法很簡單:一旦陣列元素變成了非活動的一部分,程式設計師就手工清空這些陣列元素。
一般而言,只要類是自己管理記憶體,程式設計師就應該警惕記憶體洩漏問題。一旦元素被釋放掉,則該元素中包含的任何物件引用都應該被清空。
記憶體洩漏的另一個常見來源是快取。只要在快取之外存在對某個項的鍵的引用,該項就有意義,那麼就可以用WeakHashMap代表快取;當快取中的項過期之後,他們就會自動被刪除。記住只有當所有快取項的生命週期是由該鍵的外部引用而不是由值決定時,WeakHashMap才有用處。
記憶體洩漏的第三個常見來源是監聽器和其他回撥。確保回撥立即被當做垃圾回收的最佳方法使只儲存他們的弱引用(weak reference),例如,只將它們儲存成WeakHashMap的鍵。
相關文章
- 6、消除過期的物件引用物件
- 第6條:消除過期物件的引用物件
- 建議:避免建立不必要的物件。物件
- #### 使用方法引用的使用場景--通過物件名引用成員方法物件
- 物件與物件引用的區別物件
- java中的引用物件Java物件
- 值物件與引用物件物件
- Vue 消除Token過期時重新整理頁面的重複提示Vue
- 引用物件與例項物件物件
- 分配給 主機的 Evaluation Mode 許可證已過期。建議升級許可證。
- PHP物件的引用及物件優化策略PHP物件優化
- 對圖靈譯製過程的建議圖靈
- 高手跳過:消除WinZip8.1版本過期提示框 (4千字)
- Java物件及物件引用變數Java物件變數
- PHP中物件的引用傳遞PHP物件
- 物件的引用計數與dealloc物件
- JavaScript引用物件的途徑(轉)JavaScript物件
- JAVA 物件引用,以及物件賦值Java物件賦值
- 過早的給方法中 引用物件 設為 null 可被 GC提前回收嗎?物件NullGC
- 過來人對大資料學習的建議大資料
- MongoDB專案過程中的總結與建議MongoDB
- Java基礎11 物件引用Java物件
- PHP物件導向之&引用PHP物件
- 關於引用物件拷貝物件
- 時間物件、引用型別物件型別
- 第28條:通過協議提供匿名物件協議物件
- 程式設計師長期保持身心健康的幾點建議程式設計師
- 程式怎樣釋放指向物件的引用物件
- JAVA中的指標,引用及物件的cloneJava指標物件
- Java 物件的生命週期Java物件
- 為什麼判斷物件是否相等, 不建議用 equals ?物件
- Delphi字串的引用計數與生命週期字串
- 破解NP的建議
- 編寫高質量程式碼:改善Java程式的151個建議(第3章:類、物件及方法___建議41~46)Java物件
- 編寫高質量程式碼:改善Java程式的151個建議(第3章:類、物件及方法___建議47~51)Java物件
- 編寫高質量程式碼:改善Java程式的151個建議(第3章:類、物件及方法___建議31~35)Java物件
- 編寫高質量程式碼:改善Java程式的151個建議(第3章:類、物件及方法___建議36~40)Java物件
- 改善C#程式建議之“避免鎖定不恰當的同步物件”C#物件