首页 > 运维 > linux运维 > 正文

Java虚拟机OOM解析

雪夜
发布: 2025-06-25 09:58:16
原创
423人浏览过

[注:本文内容摘选自作者之前的博客]

对于以Java为核心的企业级应用开发,Java虚拟机(JVM)是项目架构的核心。只有深入理解其内存分配和垃圾回收机制,才能在项目建设中得心应手,无论是基于当前流行的微服务架构(如Spring Cloud或Dubbo),还是即将(或已经)流行的服务网格体系。

不多废话,先看一张Java虚拟机的内存体系结构图:

Java虚拟机OOM解析 注:本文主要基于Oracle JDK体系进行阐述

此图主要针对Java Lang Out Of Memory Error异常进行了适当调整,因为核心问题和要素可以通过此图展示。本文主要解析OOM异常,若想深入了解Java虚拟机的相关知识,可参考官方文档。

立即学习Java免费学习笔记(深入)”;

当我们的应用程序因Java虚拟机内存分配异常时,会抛出java.lang.OutOfMemoryError错误。以下结合实际项目经验,总结了常见的OOM原因及其解决方法,如有遗漏或错误,欢迎补充指正。

1、Java Heap Space

此情况表明当堆内存(Heap Space)没有足够空间存放新创建的对象时,会抛出java.lang.OutOfMemoryError: Java heap space错误。

原因分析

Java heap space错误的常见原因包括以下几类:

1、请求创建一个超大对象,通常是一个大数组

2、超出预期的访问量/数据量,通常是上游系统请求流量飙升

3、过度使用终结器(Finalizer),该对象没有立即被GC

4、内存泄漏(Memory Leak),大量对象引用没有释放,JVM无法自动回收,常见于使用File等资源未回收

解决方案

针对大多数情况,通常只需通过-Xmx参数增加JVM堆内存空间即可。如果问题仍然存在,可以参考以下情况进行进一步处理:

1、如果是超大对象,检查其合理性,例如是否一次性查询了数据库全部结果,而没有限制结果数量。

2、如果是业务峰值压力,可以考虑增加机器资源或进行限流降级。

3、如果是内存泄漏,需要找到持有的对象,优化代码设计。

2、GC overhead limit exceeded

此情况为JDK1.6新增的错误类型。如果没有此异常,会发生什么?垃圾回收释放的2%可用内存空间会迅速被填满,迫使GC再次执行,导致频繁执行GC操作,服务器会因为频繁的GC操作达到100%的使用率,运行变慢,应用系统会出现卡顿现象,通常几毫秒可完成的操作,现在需要更长时间,甚至几分钟才能完成。

原因分析

1、垃圾回收可用空间较小

2、频繁执行GC操作,使得资源使用率较高

解决方案

1、增加heap堆内存

2、增加内存后错误依旧,获取heap内存快照,使用Eclipse MAT工具,找出内存泄露并进行修复

3、优化代码以使用更少的内存或重用对象,而不是创建新的对象,从而减少垃圾收集器的运行次数。如果代码中在循环中创建了许多临时对象,应该尝试重用它们

4、升级JDK到1.8,至少也是1.7,并使用G1 GC垃圾回收算法

5、优化代码

3、Unable to create new native thread

每个Java线程都需要占用一定的内存空间,当JVM向底层操作系统请求创建一个新的native线程时,如果没有足够的资源分配就会报此类错误。

原因分析

JVM向OS请求创建native线程失败,就会抛出Unable to create new native thread错误,常见原因包括以下几类:

1、线程数超过操作系统最大线程数ulimit限制

2、线程数超过kernel.pid_max(只能重启)

3、native内存不足

解决方案

1、升级配置,为机器提供更多的内存

2、降低Java Heap Space大小

3、修复应用程序的线程泄漏问题

4、限制线程池大小

5、使用-Xss参数减少线程栈的大小

6、调高OS层面的线程最大数:执行ulimit -a查看最大线程数限制,使用ulimit -u xxx调整最大线程数限制

4、Out of swap space?

此错误表示所有可用的虚拟内存已被耗尽。虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交换空间(Swap Space)组成。当程序运行时请求的虚拟内存溢出时,会报Out of swap space错误。

原因分析

此错误出现的常见原因包括以下几类:

1、地址空间不足

2、物理内存已耗光

百度虚拟主播
百度虚拟主播

百度智能云平台的一站式、灵活化的虚拟主播直播解决方案

百度虚拟主播 36
查看详情 百度虚拟主播

3、应用程序的本地内存泄漏(native leak),例如不断申请本地内存却不释放

4、执行jmap -histo:live 命令,强制执行Full GC;如果几次执行后内存明显下降,则基本确认为Direct ByteBuffer问题

解决方案

根据错误原因可以采取如下解决方案:

1、升级地址空间为64 bit

2、使用Arthas检查是否为Inflater/Deflater解压缩问题,如果是,则显式调用end方法

3、Direct ByteBuffer问题可以通过启动参数-XX:MaxDirectMemorySize调低阈值

4、升级服务器配置/隔离部署,避免争用

5、Kill process or sacrifice child

有一种内核作业(Kernel Job)称为Out of Memory Killer,它会在可用内存极低的情况下“杀死”(kill)某些进程。OOM Killer会对所有进程进行评分,然后将评分较低的进程“杀死”,具体的评分规则可以参考Surviving the Linux OOM Killer。

不同于其他的OOM错误,Kill process or sacrifice child错误不是由JVM层面触发的,而是由操作系统层面触发的。

原因分析

1、默认情况下,Linux内核允许进程申请的内存总量大于系统可用内存,通过这种“错峰复用”的方式可以更有效地利用系统资源

2、然而,这种方式也会无可避免地带来一定的“超卖”风险。例如某些进程持续占用系统内存,然后导致其他进程没有可用内存。此时,系统将自动激活OOM Killer,寻找评分低的进程,并将其“杀死”,释放内存资源

解决方案

1、升级服务器配置/隔离部署,避免争用

2、OOM Killer调优

6、Requested array size exceeds VM limit

JVM限制了数组的最大长度,此错误表示程序请求创建的数组超过最大长度限制。JVM在为数组分配内存前,会检查要分配的数据结构在系统中是否可寻址,通常为Integer.MAX_VALUE-2。

此类问题比较罕见,通常需要检查代码,确认业务是否需要创建如此大的数组,是否可以拆分为多个块,分批执行。

7、Direct buffer memory

Java允许应用程序通过Direct ByteBuffer直接访问堆外内存,许多高性能程序通过Direct ByteBuffer结合内存映射文件(Memory Mapped File)实现高速IO。

原因分析

Direct ByteBuffer的默认大小为64 MB,一旦使用超出限制,就会抛出Direct buffer memory错误。

解决方案

1、Java只能通过ByteBuffer.allocateDirect方法使用Direct ByteBuffer,因此,可以通过Arthas等在线诊断工具拦截该方法进行排查

2、检查是否直接或间接使用了NIO,如netty,jetty等

3、通过启动参数-XX:MaxDirectMemorySize调整Direct ByteBuffer的上限值

4、检查JVM参数是否有-XX:+DisableExplicitGC选项,如果有就去掉,因为该参数会使System.gc()失效

5、检查堆外内存使用代码,确认是否存在内存泄漏;或者通过反射调用sun.misc.Cleaner的clean()方法来主动释放被Direct ByteBuffer持有的内存空间

6、内存容量确实不足,升级配置

8、Permgen space

此错误表示永久代(Permanent Generation)已用满,通常是因为加载的class数目太多或体积太大。

原因分析

永久代存储对象主要包括以下几类:

1、加载/缓存到内存中的class定义,包括类的名称,字段,方法和字节码

2、常量池

3、对象数组/类型数组所关联的class

4、JIT编译器优化后的class信息[PermGen的使用量与加载到内存的class的数量/大小正相关]

解决方案

根据Permgen space报错的时机,可以采用不同的解决方案,如下所示:

1、程序启动报错,修改-XX:MaxPermSize启动参数,调大永久代空间

2、应用重新部署时报错,很可能是应用没有重启,导致加载了多份class信息,只需重启JVM即可解决

3、运行时报错,应用程序可能会动态创建大量class,而这些class的生命周期很短暂,但是JVM默认不会卸载class,可以设置-XX:+CMSClassUnloadingEnabled和-XX:+UseConcMarkSweepGC这两个参数允许JVM卸载class

9、Metaspace

原因分析

JDK1.8使用Metaspace替换了永久代(Permanent Generation),此错误表示Metaspace已被用满,通常是因为加载的class数目太多或体积太大。

解决方案

此类问题的原因与解决方法跟Permgen space非常类似,可以参考上文。需要特别注意的是调整Metaspace空间大小的启动参数为-XX:MaxMetaspaceSize。

以上就是Java虚拟机OOM解析的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号