
导读
读OOM本质是程序程序申请内存过大,虚拟机无法满足我们,然后自杀了。OOM经常伴随Full GC,高JDBC, Pending User Request等一系列的问题内存溢出的案例数不胜数, 所以日常运维中我们需要尽量避免OOM的发生。
一般来说OOM会在标准输出日志中打印出这句话:
Exception in thread "main" java.lang.OutOfMemoryError:
本案例就从在标准输出日志中找到的一段话说起。
某企业的某新项目的测试环境机器上的部分节点总是出现内存溢出,为此,重启了测试环境的所有节点,庆幸状态有所缓解,可惜过不了多久又复现。因此,东方龙马工程师得知后第一时间协助排查此问题。
东方龙马的工程师首先登陆测试环境,通过ps命令查看这台机器的进程。发现,这台机器上的Java进程已经按照运维规范,增加了参数
-XX:+HeapDumpOnOutOfMemoryError
但是,在我们排查问题的过程中,并没有发现内存溢出后自动产生的内存快照文件。
反而发现了一个hs_error_pid.log的文件。hs_error_pid.log文件,代表JVM产生了致命错误,其中往往包含了虚拟机崩溃原因的重要信息。
对很多人来说,OOM和JVM致命错误所产生的崩溃,最终都是进程丢失,认为没什么不同。
但是其实对比下OOM和JVM致命错误崩溃两种场景。
OOM属于”自杀”,JVM无法承受工作压力(内存压力),进行了自我终结,在自杀前,它会留下一份”遗书”(内存快照文件),向世人展示自己所遭受的苦楚(内存压力的主要大对象),所以如果认真解析下内存快照的话,得到的信息是相当多的,我们也能进行更近一步的进行优化。
JVM致命错误崩溃,则是”死于意外”,它因为一些意料之外的原因(很多时候是JVM Bug,也有可能是一些JVM设计时没想到的奇葩情况。)所以,来不及留下非常详细的内容,我们只能从”案发的一些只言片语”,结合经验乃至源码,进行故障的诊断。
在hs_error_pid.log文件中存在如下信息:
# There is insufficient memory for the Java Runtime Environment to continue.
# Cannot create worker GC thread. Out of system resources.
# Possible reasons:
# The system is out of physical RAM or swap space
# In 32 bit mode, the process size limit was hit
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Use 64 bit Java on a 64 bit OS
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
# Out of Memory Error (workgroup.cpp:99), pid=7107, tid=0x00007f6fbd54a700
#
可以看到JVM致命崩溃的原因是无法创建GC工作线程。
而无法创建GC工作线程有多种原因,我们按照hs_error_pid.log文件中罗列的项进行逐项检查,发现主机的ulimit -u值很小。

东方龙马工程师写了一个定时的批处理文件,检查后端用户线程数,最终确定了,就是这个值,可能无法满足当前的线程需求,导致了无法创建新的线程,导致如果需要创建GC线程的时候无法创建GC线程,JVM崩溃。

产生OOM的报错,不一定是内存不够用,也可能是无法创建线程,无法回收内存。开发人员的代码质量会影响JVM运行,操作人员的奇葩操作会影响JVM运行,而系统的各项基础指标限制,更会影响JVM运行。
运维工程师不仅仅要关注软件自身的配置,对于可能限制软件的一些硬件配置,也要做到心中有数,这样在面对生产故障时,才能更快的定位解决问题。

| 北京 | 上海 | 广州 | 成都 |
4008-906-960





