虚拟机夯实之路————OOM的异常

本文讨论了Java程序中常见的内存溢出问题,包括Java堆、虚拟机栈、本地方法栈和方法区/运行时常量池溢出,以及如何通过分析和调整堆参数、检查内存泄漏和优化代码来解决这些问题。特别提到了JDK版本变迁对内存管理的影响。
摘要由CSDN通过智能技术生成

    除了程序计数器外,虚拟机内存其它几个运行时区域都会发生OOM。

Java堆溢出

    Java堆用于存储对象实例,当不断创建对象,并保证GC Roots到对象之间有可达路径来避免垃圾回收机制清楚这些对象,那么随着对象的增加,总容量触及最大堆的容量限制后就会产生内存溢出异常。

要解决内存区域异常,常规的处理方法是通过内存映像分析工具对Dump出的堆转储快照进行分析。首先确认内存中导致OOM的对象是否是必要的,也就是要弄清楚是出现了内存泄漏还是内存溢出。

如果是内存泄漏,进一步通过工具查看泄露对象到GC Roots的引用链,找到泄露对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄露对象的类型信息以及它到GC Roots引用链的信息,可以准确定位到这些对象创建的位置,进而找出产生内存泄漏的具体位置。

如果不是内存泄漏,那内存中的对象确实都是必须存活的,那就应当检查虚拟机堆参数的设置,与机器内存对比,看是否有向上调整空间。再从代码中检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等,尽量减少程序运行期间内存消耗。

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

1.如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

2.如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出 OutOfMemoryError异常。

    无论是因为栈帧太大还是虚拟机栈容量太小,新的栈帧的内存无法分配时,虚拟机抛出StackOverflowError异常。如果允许动态扩展栈容量大小的虚拟机会产生OOM。(不限于单线程,通过不断建立线程的方式,也可能产生内存溢出异常。在这种情况下,给每个线程的栈分配的内存越大,越容易产生内存溢出异常。)

    出现StackOverflowError异常时,会有明确错误堆栈可供分析,相对而言比较容易定位到问题所在。如果是建立过多线程导致的内存溢出,在不能减少线程数量或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。这种通过“减少内存”的手段来解决内存溢出的方式。

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

运行时常量池是方法区的一部分,JDK7开始逐步“去永久代”,JDK8以后完全使用元空间来代替永久代。String::intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的 字符串,则返回代表池中这个字符串的String对象的引用。

我的理解是:JDK7以前常量池分配给永久代中(好像在方法区里),JDK7以后,原本存放在永久代的字符串常量池被移至Java堆中。

   

本机直接内存溢出

    直接内存如果不指定默认和Java堆最大值一致。(Unsafe类的getUnsafe()方法指定只有引导类加载器才会返回实例,体现了设计者希望只有虚拟机标准类库里面的类才能使用Unsafe的功能,在JDK 10时才将Unsafe 的部分功能通过VarHandle开放给外部使用)因为虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配就会在代码里手动抛出溢出异常,真正申请分配内存的方法是Unsafe::allocateMemory()。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值