消除过期的对象引用
在手工管理内存的语言中(如“C++”),当对象使用之后,需要手动释放其所占有的内存,例如 C++ 中的析构函数。但 Java 具有垃圾回收器,降低了内存管理的难度,但仍会出现内存泄漏的风险。如:
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];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2*size +1);
}
}
}
在这段程序中,看似没有问题,但如果让栈中的元素先增多后减少,在 pop() 方法中,由于仅仅对 size 做了减小,而并没有删除对象,所以仍然存在着对无效对象的引用,因而垃圾回收器就不会将其当作垃圾进行回收。对象的过期引用造成了内存泄漏。
因此应该将 pop() 方法更改为
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
内存泄漏的另一个常见来源是缓存。放入缓存中的对像一般很容易被遗忘掉,因此即使它不再使用之后,也可能很长一段时间内都会留在缓存中。可以使用 WeakHashMap 解决某些场景下的问题。
WeakHashMap,如果 Map 之外存在对键的引用,则可以正常使用,但当键的外部引用消失时,该项就会被删除。
内存泄漏第三个常见的来源是监听器和其他回调。如果实现了一个 API ,客户端中 API 中注册回调,却没有显示地取消注册,它们就会不断堆积起来。此时也可以使用 WeakHashMap 进行解决。