- 相同与不同
内存泄漏和内存溢出在表现出来的形式上都是一样的,都会发生OOM异常。但是他们在本质上是有很大区别的,有什么不同,我们举个例子来说明,例如一个水桶有5升的容量,然后我现在池子里面有6升的水,要把6升的水放进我们的桶里,会发生什么情况呢?就会发生溢出,这就是内存溢出。内存泄漏的意思呢,就是我们还是一个5升的水桶,另外一个桶里有4升的水,但是这个水不干净,有许多石子和沙子,把这个4升的浑浊水倒入我们5升的水,倒进去之后我们的沙子和石头是。其实就是GC想回收内存,但是因为我们代码编写的方式不对,导致GC没法回收,这就不是都沉淀下载5升的桶下面了。上面的水我用抽水机把上面的水抽出来,但是这个沙子和石头抽不出来,加入这个石头和沙子的容量是1升。那么这个原来的5升的水桶就变成了只有4L的容量了。这就是所谓的内存泄漏,就是说该回收的内存没有回收,导致我的可能内存减少是我们的内存泄漏。
- 如何避免内存泄漏
我们用栈来模拟,首先我们定义一个我们自己的栈,代码如下图所示:
package com.zhuguang.allen;
public class Stack {
public Object[] elements;
private int size = 0;//指示器,指示当前栈顶的位置
private static final int Cap = 16;
public Stack() {
elements = new Object[Cap];
}
//入栈
public void push(Object e){
elements[size] = e;
size++;
}
//出栈
public Object pop(){
size = size-1;
Object o = elements[size];
elements[size] = null;//
return o;
}
}
假如没有在出栈的时候没有将栈的元素置空,也就是没有elements[size]=null,这一行代码。那我们来执行下面的测试代码:
package com.zhuguang.allen;
public class UseStack {
public static void main(String[] args) {
Stack stack = new Stack();
Object o = new Object();
System.out.println("o="+o);
stack.push(o);
Object o1 = stack.pop();
System.out.println("o1="+o1);
System.out.println(stack.elements[0]);
}
}
我们会发现最后的这一行也会打印出对象,我们看到明明入栈和出栈了一个元素,为什么打印栈内的第一个元素还能拿到对象呢?其实这里就出现了内存泄漏。如果要防止泄露我们要在出栈的时候加上elements[size] = null。
内存泄漏一般出现在通过某种容器自己去保存元素的时候特别容易发生内存泄漏。我们来看下JDK的源码,看下ArrayList的代码,看到remove方法this.elementData[--this.size] = null;这行代码其实就是让元素置空,然后让GC进行回收。为了防止内存泄漏。所以如果大家来写代码的时候,特别是用数组来存储元素的时候,大家要特别注意。