过期对象引用可能产生的内存泄露问题
1 内存泄露
内存泄漏是指程序中存在无用的内存占用,这些占用的内存无法被垃圾回收机制回收。这种情况下,随着时间推移,内存占用越来越大,最终导致程序崩溃。Java内存泄漏的原因有很多,下面列出一些常见的原因:
- 静态集合类的不适当使用:如果使用不适当的方式,静态集合类可能会导致内存泄漏。由于静态集合类的生命周期与程序的生命周期相同,如果在使用时没有妥善释放,那么这些集合中的对象将无法被垃圾回收。
- 资源未正确关闭:Java中一些对象会占用底层的系统资源,比如文件系统句柄、网络连接、数据库连接等等。如果这些资源在使用后未正确关闭,那么系统将无法释放这些资源,从而导致内存泄漏。
- 内部类的使用不当:内部类的生命周期比较复杂,如果在使用内部类时没有释放对它的引用,那么内部类可能持有外部类的引用,从而导致外部类也无法被垃圾回收。
- 单例模式的不适当使用:单例模式是一种常见的设计模式,但是如果不适当使用,可能会导致内存泄漏。比如,如果单例对象持有了一些其他对象的引用,那么这些对象将无法被垃圾回收。
2 以栈为例
以下是一个由java实现堆栈的程序。
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
element = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public void pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/**
* 确保栈至少有一个元素,如果超出容量则扩大
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
上述实现的push
方法向数组尾部添加元素,pop
方法弹出栈顶元素并返回其值。如果栈为空,pop
方法将抛出一个EmptyStackException
。同时,ensureCapacity
方法确保可用的容量足够存储所有元素,如果不足则使用Arrays.copyOf
方法将数组扩大两倍的容量加1。但是他存在着一个隐藏的问题,随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序的性能会逐渐降低。
3 原因和解决办法
在上述实现中,如果一个栈先增长,然后再收缩,那么从栈中弹出来的对象将不会被当作垃圾回收,就算栈中的对象不再被引用,它们也不会被回收。
像是支持垃圾回收的语言,例如Python,Java,内存泄漏是比较隐蔽的,如果有ige对象引用被无意识地保留起来,那么垃圾回收机制不会处理该对象,而且也不会处理被这个对象所引用的其他所有对象,这很可能对性能造成潜在的重大影响。
解决办法很简单,当不需要对象时(栈中没有元素时),清除引用即可,对于pop
方法的修改如下所示:
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // 置空元素引用
return result;
}