你能看出以下代碼哪里內(nèi)存泄漏嗎?
// 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);
}
}
答案是:
pop()方法存在內(nèi)存泄漏。
內(nèi)存泄漏可以稱為“ 無意識的對象保持(unintentional object retention)”。
在pop()方法中從棧中彈出來的對象將不會被當(dāng)做垃圾回收。棧內(nèi)部維護著對這些對象的過期引用(obsolete reference)。所謂的過期引用,是指永遠也不會再被解除的引用。凡是在elements數(shù)組的“活動部分”(active portion)之外的任何引用都是過期的。活動部分是指elements中下標(biāo)小于size的那些元素。
解決方法:上述述例子中的Stack類而言,只要一個單元被彈出棧,指向它的引用就過期了。一旦數(shù)組元素變成了非活動部分的一部分,就手工清空這些數(shù)組元素。修改后的pop()方法如下:
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];//result相當(dāng)于一個temp。
elements[size] = null; // Eliminate obsolete reference
return result;
}
清空對象引用應(yīng)該是一種例外,而不是一種規(guī)范行為。消除過期引用最好的方法是讓包含該引用的變量結(jié)束其生命周期。
延伸閱讀
length、size()、length()的區(qū)別:
- 數(shù)組的長度(length):數(shù)組能容納元素個數(shù)的值
- 泛型集合的大小(size()):泛型中元素的個數(shù)
- 字符串的長度(length()):字符的個數(shù)
前綴遞減和后綴遞增:
- 前綴遞減,"--"操作符位于變量或表達式前,先執(zhí)行運算,再生成值。如上例中
elements[--size]
,size大小先減1,所以Object result = elements[--size];
中result元素下標(biāo)為size=size-1。 - 后綴遞增,"++"操作符位于變量或表達式后,先生成值,再執(zhí)行運算。如上例中
elements[size++] = e;
,元素下標(biāo)為size,再執(zhí)行運算size=size+1。
Arrays.copyOf()方法:
- a copy of the original array, truncated or padded with nulls to obtain the specified length(原始數(shù)組的副本,縮短或填補null來獲取指定的長度)。
- 作用:如果數(shù)組元素的個數(shù)等于數(shù)組的長度,新建副本數(shù)組,將長度擴大為兩倍加一,將數(shù)組副本賦值給elements。