会造成"过期引用"(obsolete reference)泄漏:pop 只把 size 减一,数组槽位里仍保存着被弹出对象的强引用,GC 认为这些对象仍可达,无法回收。栈越大、弹出的对象占用内存越多,泄漏越严重,最终可能导致 OOM。
// 有问题的写法
public Object pop() {
if (size == 0) throw new EmptyStackException();
return elements[--size]; // elements[size] 仍持有引用
}
// 正确写法:弹栈后手动置空
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 消除过期引用
return result;
}
这个例子出自《Effective Java》:只要类自己管理内存(自己维护对象池、数组缓冲),程序员就要警惕内存泄漏,因为 GC 无法判断哪些引用已逻辑失效。JDK 的 ArrayDeque、Stack 等实现都做了置空处理。
延伸:并非每次 pop 都必须置空——如果栈对象本身很快会被回收,过期引用随之消失,置空只是防御性的好习惯;开销与收益需权衡。追问方向:Java 中其他常见泄漏场景?(监听器未注销、静态集合缓存、ThreadLocal 未 remove)
(约 350 字)