写在前面:
内存溢出和内存泄漏都是程序中,我们要极力避免出现的异常,二者在字面上看上去相似,但实际上是不一样的异常情况,在这篇博客中,我们会总结内存溢出与内存泄漏的区别。
内存溢出和内存泄漏的定义:
内存溢出:Out Of Memory,指程序在申请内存时,没有足够的内存空间供其使用。
内存泄漏:是指程序在申请内存后,无法释放已申请的内存空间。
用通俗一点的话来解释,内存溢出类似于数组索引越界,超出了数组存储的上限。内存泄漏是指内存使用完毕后,不能释放内存,并回收使用。
内存溢出和内存泄漏所导致的结果:
内存溢出时,程序将会终止,并且抛出Java.lang.OutOfMemoryError异常
内存泄漏时,程序并不会立刻终止,但是效率会降低,直到没有剩余内存,最终也会导致内存溢出。
内存溢出的类型:
(1)Java堆溢出
Java堆用于存储堆实例,不断的创建对象,那么在对象数量到达最大堆限制时,就会产生内存溢出。
(2)虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError 异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;// 累加变量
stackLeak();// 调用自身
}
}
上述代码是在单线程中调试,程序会报
StackOverflowError 异常。 而如果是于多线程之中,程序不断增加线程,并且为每一个线程扩大内存,则会报OutOfMemoryError异常。
(3)方法区和运行时常量池溢出
运行时常量池是方法区的一部分。方法区用于存放的Class信息,如类名,访问修饰符,常量池,字段描述,方法描述等。
对于非-常量池部分,运行时生成大量的动态类填满方法区。
对于常量池部分,如果无限循环调用String的intern()方法产生不同的String对象实例,并在List中保存其引用,以防止被GC回收,最终会产生内存溢出。
(4)本机直接内存溢出
而此类内存溢出一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,可以考虑一下是不是这方面原因。
注:在JDK1.4之后加入了NIO,引入了一种基于通道与缓冲区的I/O方式。