OutOfMemoryError异常

除程序计数器外,虚拟机内存的其他几个运行时区域都会发生OOM异常的可能

java堆内存异常测试

测试分析:

java堆用于存储对象实例,只要不断创建对象,并保证GC Roots到对象之间有可达路径避免垃圾回收机制清理这些对象。就可以产生预期
ps:保证GC Roots到对象之间有可达路径,即保证对象可以通过常亮、全局变量、静态变量,以及方法内的局部变量中保存的该对象的引用间接访问

测试:
(1)参数设置: 限制java堆大小为20M,并不可扩展(将堆的最小值-Xms与最大值-Xmx设置为一样即可)
-XX:+HeapDumpOnOutOfMemoryError:用于当虚拟机出现内存溢出异常时,Dump出当前的内存堆转存快照以便事后分析。
(2) 工具:windows下IDEA
添加vm环境变量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试代码:

public class JavaVMStackSOF {

    static class OOMobject{
    }

    public static void main(String[] args) {
        List<OOMobject> list=new ArrayList<>();
        while(true){
            list.add(new OOMobject());
        }
    }
 }

测试结果:

在这里插入图片描述
解决这个问题的方案:
(1)通过内存映像分析工具对Dump出来的堆快照进行分析,确认是内存泄漏还是内存溢出
(2)内存泄漏:通过工具进一步查看泄漏对象找到GC Roots的引用链,找到泄漏对象是如何并和哪些GC ROOTS相关联的,才导致垃圾回收器无法回收他们。通过泄漏对象的类型信息去定位内存泄漏代码的具体位置
(3)内存溢出:即内存中的对象必须要都存活。检查java虚拟机的堆参数(-Xms和-Xmx),代码方面检查是否存在某些对象生命周期过长等

虚拟机栈和本地方法栈溢出测试

测试分析:

存在两种异常:(1)如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
(2)当虚拟机的栈允许动态扩展时,扩展栈容量无法申请到足够的内存会抛出outOfMemoryError异常
对于HotSpot而言,不支持扩展。只有在创建线程申请内存时,无法获得足够内存而出现outOfMemoryError异常,在线程运行时只会因为栈容量无法容纳新的栈帧而导致StackOverflowError异常
测试1: Xss调整栈容量

在这里插入图片描述
测试代码:

private int stackLength=1;
    public void stackLeak(){
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF oom=new JavaVMStackSOF();
        try{
            oom.stackLeak();
        }catch (Throwable e){
            System.out.println("stack length:"+oom.stackLength);
            throw e;
        }
    }

测试结果:

在这里插入图片描述
测试2: 多占用局部变量表空间,验证(2)
测试代码:

public class JavaVMStackSOF {
    private static int stackLength =0;
    public  static void test(){
        long unused1,unused2,unused3,unused5,unused6,unused7,unused8,unused9,unused10,              unused11,unused12,unused13,unused15,unused16,unused17,unused18,unused19,unused20,                unused21,unused22,unused23,unused25,unused26,unused27,unused28,unused29,unused30,              unused31,unused32,unused33,unused35,unused36,unused37,unused38,unused39,unused40,             unused41,unused42,unused43,unused45,unused46,unused47,unused48,unused49,unused50,                unused51,unused52,unused53,unused55,unused56,unused57,unused58,unused59,unused60;
        stackLength++;
        test();
        unused1=unused2=unused3=unused5=unused6=unused7=unused8=unused9=unused10=                unused11=unused12=unused13=unused15=unused16=unused17=unused18=unused19=unused20=                       unused21=unused22=unused23=unused25=unused26=unused27=unused28=unused29=unused30=                              unused31=unused32=unused33=unused35=unused36=unused37=unused38=unused39=unused40=                                      unused41=unused42=unused43=unused45=unused46=unused47=unused48=unused49=unused50=                                               unused51=unused52=unused53=unused55=unused56=unused57=unused58=unused59=unused60=0;
    }
    public static void main(String[] args) {
        try{
            test();
        }catch (Throwable e){
            System.out.println("stack length:"+stackLength);
            throw e;
        }
    }
}

测试结果

在这里插入图片描述
总结:无论时栈帧太大还是虚拟机栈容量太小,当新的栈帧内存无法分配时,Hotspot都会抛出StackOverflowError。在可扩展栈容量大小的虚拟机上,相同代码会抛出outOfMemoryError异常
结果参考《深度学习虚拟机》
ps:栈内存和线程之间的关系:
线程数量越多,分配给每个线程栈的内存就会越小,反之越大。当无限创造内存时就会导致内存溢出。出现outOfMemoryError异常。
优化可以通过减少最大堆和减少栈容量来换取更多的线程。

方法区和运行时常量池溢出

运行时常量池时方法区的一部分
ps:String::intern()方法:
如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。

测试分析

在jdk6之间,常量池分配在永久代中,可以通过-XX:PermSize和-XX:MaxPermSize限制永久代的大小,即可间接限制其中常量池的容量。
在这里插入图片描述

在jdk7之后,字符串常量池被移至Java堆中,调整以上参数不会出现异常。将堆的大小通过-Xms调小时会出现如下结果:
在这里插入图片描述
方法区主要存放类型的相关信息:类名,访问修饰符,常量池,字段描述,方法描述等。可以通过运行时产生大量的类去填充方法区直至溢出为止。
在jdk7中借助:CGlib动态生成大量子类,实现该异常
在这里插入图片描述
jdk8之后元空间替代永久代,提供了一些方法来防止方法区出现异常:
-XX:MaxMetaspaceSize:设置元空间最大值
-XX:MetaspaceSize:指定元空间的初始空间大小,
-XX:MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因为元空间不足导致的垃圾收集的频率。
-XX:Max-MetaspaceFreeRatio,用于控制最 大的元空间剩余容量的百分比。

本机直接内存溢出

可以通过-XX:MaxDirectMemorySize参数来指定,如果不去指定,则默认与Java堆最大值(由-Xmx指定)一致。
特点:直接内存导致的内存溢出,在HeapDump文件中不会有明显的异常情况。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值