《Effective Java》书中第7条中提到:一般来说,只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
此次测试过程我用的jdk8,在jdk的bin目录下可以找到jvisualvm.exe,双击打开。
idea设置堆内存限制-Xmx100M -Xms100M ,如下图所示

测试代码如下:
java
import java.util.Arrays;
import java.util.EmptyStackException;
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();
}
Object element = elements[--size];
return element;
}
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size +1);
}
}
}
java
public class ObsoleteReferenceTest {
public static void main(String[] args) throws InterruptedException {
Stack stack = new Stack();
// 压入大量大对象
for (int i = 0; i < 80; i++) {
stack.push(new byte[1024 * 1024]);
System.out.println("压入第 " + i + " 个 1MB 数组");
}
// 全部弹出!业务上不再使用这些对象
for (int i = 0; i < 80; i++) {
stack.pop();
}
System.out.println("所有对象pop完成,业务层面不再使用");
Thread.sleep(30000);
}
}
运行代码后,进入Java VisualVM,在左侧找到当前代码运行的程序,双击,然后点击右侧上方的"监视",如下图所示

等待idea控制台输出 所有对象pop完成,业务层面不再使用 后点击"执行垃圾回收"按钮,如下图所示,可以看到,堆内存的使用量并没有明显下降。说明elements 数组内部仍然持有大量byte对象引用。

修改pop()方法,手动清空过期引用
typescript
public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
Object element = elements[--size];
//手动清空过期引用
elements[size] = null;
return element;
}
再次运行代码,点击执行垃圾回收,观察堆使用情况,等待idea控制台输出 所有对象pop完成,业务层面不再使用 后点击"执行垃圾回收"按钮,可以看到堆使用量大幅下降,说明大量过期对象已被回收。
