JVM内存溢出的几种情形

5 篇文章 0 订阅

1.堆溢出

原因:大量对象占据了堆空间,而这些对象都有强引用导致无法回收,当对象大小之和>Xmx参数指定的堆大小时导致溢出!

List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 10240; i++) {
            list.add(new byte[1024 * 1024]);
        }


解决方法是使用-Xmx增大堆大小,但是堆空间毕竟不能无限增长,所以需要使用MAT和VM等工具找到大量占用堆空间的对象做出合理的程序优化。


直接内存溢出
NIO直接向操作系统申请,但是由于没有内JVM托管,如果使用不当也会导致内存溢出。


过多线程导致OOM
因为每个线程需要占用系统内存,当线程过多可能导致OOM,线程的栈空间是在堆外分配,和直接内存很相似,如果要系统支持更多的线程,那么应该使用小的堆空间。
解决办法:
1).减少堆空间 -Xmx512m 这样操作系统可以预留更多内存用于线程创建 因此程序可以正常运行
2).减少线程所占的内存空间,使用-Xss可以指定栈空间  -Xmx1g -Xss128k
使用1G堆空间,但是栈空间减少到128K,剩余可以用的内存可以容纳更多的线程,但是减少栈空间,栈溢出的风险增加。
3).减少线程总数


2.栈溢出

java虚拟机规范定义了两种异常与栈空间有关:StackOverflowError和OutOfMemoryError


线程计算过程中 栈深度>最大可用栈深度 抛出StackOverflowError
如果栈可以动态扩展,如果扩展过程中没有足够内存空间支持会抛出OutOfMemoryError


-Xss设置栈大小,栈大小决定了函数调用的可达深度


虚拟机栈在运行时使用了栈帧的数据结构保持上下文数据,栈帧中存放了局部变量表,操作数栈,动态连接方法和返回地址等信息。
每一个方法的调用都伴随着栈帧的入栈操作,函数返回表示出栈。如果参数和局部变量过多那么栈帧的局部变量表会变大。

无限递归导致

public class TestDemo {
        private int count = 0;

    public void recursion() {
        count++;
        recursion();
    }

    @Test
    public void testStack() {
        try {
            recursion();
        } catch (Throwable e) {
            System.out.println("深度>>>" + count);
            e.printStackTrace();
        }
    }
}


jclasslib可以查看class文件每个方法分配的局部变量表内容,

下载地址:https://github.com/ingokegel/jclasslib/releases

在栈帧中与性能调优关系最密切的就是局部变量表:函数的参数+函数内局部变量,局部变量表以字为单位,一个字32位长,long和double占2字,其余1字。
对于非static方法虚拟机还将对象this作为参数通过局部变量表传递给当前方法。



3.永久区溢出
如果系统定义了太多的类型,那么永久区会溢出,Jdk1.8中永久区被称为元数据的区域代替,但是功能是类似的,都是保持类的元信息。
例如使用Cglib动态产生新类(而不是new对象)N次会都爱吃OOM异常。
解决办法:
1.增加MaxPermSize
2.减少系统需要的类的数量
3.使用ClassLoader合理的装载类定期回收

参考《Java程序性能优化 让你的Java程序更快、更稳定》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值