[注:本文内容摘选自作者之前的博客]
对于以Java为核心的企业级应用开发,Java虚拟机(JVM)是项目架构的核心。只有深入理解其内存分配和垃圾回收机制,才能在项目建设中得心应手,无论是基于当前流行的微服务架构(如Spring Cloud或Dubbo),还是即将(或已经)流行的服务网格体系。
不多废话,先看一张Java虚拟机的内存体系结构图:
注:本文主要基于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、物理内存已耗光
3、应用程序的本地内存泄漏(native leak),例如不断申请本地内存却不释放
4、执行jmap -histo:live
解决方案
根据错误原因可以采取如下解决方案:
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中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号