Java的内存泄漏
查看代码:
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITAL_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);
}
}
}
这段程序中并没有很明显的错误。但是这个程序中隐藏着一个问题。不严格的讲,就是“内存泄漏”,随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会表现出来。在极端情况下,这种内存泄漏会导致磁盘交换,甚至导致OutOfMemoryError。
如果一个栈先增长,然后再收缩,那么,从栈中弹出的对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。这是因为,栈内部维护着对这些对象的过期引用。
过期引用:是指永远不会被解除的引用
在上面的例子中,凡是在elements数组的活动部分之外的任何引用都是过期的。
在Java这样的支持垃圾回收的语言中,内存泄漏是隐藏的。如果一个对象引用被无意识地保留起来了,那么垃圾回收机制不仅不会处理这个对象,而且也不会处理这个对象所引用的所有其他对象。
这里我们会对弹出的元素做一些简单的处理:如下
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。
内存泄漏的另一个常见来源是缓存,一旦把对象引用放到缓存中,它就很容易被遗忘掉,从而使得它不再有用之后很长一段时间内仍然留在缓存内。
如果想要实现这样的一个缓存:只要在缓存之外存在对某个项的健的引用,该项就有意义,那么就可以用WeakHashMap
代表缓存;当缓存中的项过期之后,它们就会被自动删除。只有当所要的缓存项的生命周期是由该健的外部引用而不是由值决定时,WeakHashMap才有用处
内存泄漏的另一个常见来源是监听器和其他回调,如果实现了一个API,客户端在这个API中注册回调,却没有显式的取消注册,那么除非采取某些动作,否则他们会聚集。确保回调立即被当做垃圾回收的最佳方法是只保存它们的弱引用。
内存泄漏往往是一个不明显的Bug。BeCareful!!!
网友评论