龙空技术网

Java内存溢出场景及解决办法

佳云分享 178

前言:

此刻咱们对“java直接内存溢出”大约比较珍视,各位老铁们都需要学习一些“java直接内存溢出”的相关内容。那么小编同时在网络上收集了一些对于“java直接内存溢出””的相关资讯,希望姐妹们能喜欢,兄弟们快快来了解一下吧!

Java内存溢出即程序在申请内存时,没有足够的空间供其使用,出现out of memory。常见于四种情况:栈溢出(StackOverflowError)、堆溢出(OutOfMemoryError:java heap space)、永久代溢出(OutOfMemoryError: PermGen space)、OutOfMemoryError:unable to create native thread,以下一一进行总结。

1、栈溢出

首先搞清楚java栈空间存储的是什么。java栈空间是线程私有的,是java方法执行的内存模型。每个方法执行时都会在java栈空间产生一个栈帧,存放方法的变量表,返回值等信息,方法的执行到结束就是一个栈帧入栈到出栈的过程。

所以栈溢出的原因一般是循环调用方法导致栈帧不断增多,栈深度不断增加,最终没有内存可以分配,出现StackOverflowError,比如下面这种情况:

public class stack{

public void test(){

this.test();

}

public static void main(String[] args){

for(; ; ;)

new stack().test;

}

}

栈内存溢出一般是程序错误导致,如递归死循环等等。

2、堆溢出

java堆是线程共有的区域,主要用来存放对象实例,几乎所有的java对象都在这里分配内存,也是JVM内存管理最大的区域。java堆内存分年轻代和年老代,堆内存溢出一般是年老代溢出。当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出。当一个对象产生时,主要过程是这样的:

JVM首先在年轻代的Eden区为它分配内存;

若分配成功,则结束,否则JVM会触发一次Young GC,试图释放Eden区的不活跃对象;

如果释放后还没有足够的内存空间,则将Eden区部分活跃对象转移到Suvivor区,Suvivor区长期存活的对象会被转移到老年代;

当老年代空间不够,会触发Full GC,对年老代进行完全的垃圾回收;

回收后如果Suvivor和老年代仍没有充足的空间接收从Eden复制过来的对象,使得Eden区无法为新产生的对象分配内存,即溢出。

由此可见,当程序不断地创建大量对象实例并且没有被GC回收时,就容易产生内存溢出。如下:

public class heap{

public static void main(String[] args){

ArrayList list = new ArrayList();

while(true){

list.add(new heap());

}

}

}

堆内存溢出很可能伴随内存泄漏,应首先排查可能泄露的对象,再通过工具检查GC roots引用链,从而发现泄露对象是由于何种引用关系使得GC无法回收他们;若不存在内存泄漏,换句话说就是内存中的对象还都需要继续存活,则可通过修改虚拟机的堆参数将堆内存增大。

3、永久代溢出

永久代也是java堆内存的一部分,主要用来存放Class的相关信息,如类名,访问修饰符等等。一般永久代溢出的原因是动态加载大量的Class并且没有及时被GC回收。只能通过调整永久代内存参数的方式解决。

4、无法创建本地线程

我们知道,操作系统对每个进程的内存都是有一定限制的,当堆内存和非堆内存分配过大时,剩余的内存不足以创建足够的线程栈,就会产生OutOfMemoryError。因此我们可以增大进程占用的总内存或减小堆内存等来解决问题。

总结:

栈内存溢出:程序所要求的栈深度过大导致。

堆内存溢出:分清 内存泄露还是 内存容量不足。泄露则看对象如何被 GC Root 引用。不足则通过 调大 -Xms,-Xmx参数。

持久带内存溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。

无法创建本地线程:总容量不变,堆内存,非堆内存设置过大,会导致能给线程的内存不足。

————————————————

版权声明:本文为CSDN博主「code_kkk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

标签: #java直接内存溢出