高级java工程师手把手教你解决内存不足引起JVM奔溃案例实战
一、真实事故描述:
生产环境的Java程序进程,直接宕掉,进程都没有了,JVM奔溃了。生产事故,生产直接停止了,甲方爸爸客户着急了,公司老板奔溃了,恭喜你这个时候,压力转移到我这个高级工程师上了。马上解决是就是英雄,解决不了就滚蛋,这就是我的技术人生。生死存亡就看自己,与此同时很多人都在添油加醋,真正能解决问题的人,其实很少,多数其他人都是充数,在旁边叫唤,提出问题的人很多,解决问题的很少。
Java进程奔溃了,在重启。一会又奔溃了,起了又蹦,蹦了又重新启动,一般运维的思路就说如此。但是,核心的问题没有根本的解决。
二、解决问题的线索获取JVM奔溃日志
一般JVM奔溃,奔溃之前都会有JVM的日志,这种日志命名都是很佛系的。
日志位置是在当前执行JAVA的jar包同级目录
然后会出现一些有规律的日志,如下图呈现!
这种日志,每次JAVA程序奔溃一次,产生一个日志,并且是有时间戳标识,大家注意识别。
三、解读JVM奔溃的日志内容
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 5242880 bytes for committing reserved memory.
# Possible reasons:
# The system is out of physical RAM or swap space
# The process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# JVM is running with Zero Based Compressed Oops mode in which the Java heap is
# placed in the first 32GB address space. The Java Heap base address is the
# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress
# to set the Java Heap base and to place the Java Heap above 32GB virtual address.
# This output file may be truncated or incomplete.
#
# Out of Memory Error (os_linux.cpp:2749), pid=1181802, tid=0x00007f1b0fdfd700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_211-b12) (build 1.8.0_211-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode linux-amd64 compressed oops)
# Core dump written. Default location: /home/jk/core or core.1181802
#
--------------- T H R E A D ---------------
Current thread (0x00007f1b38271800): VMThread [stack: 0x00007f1b0fcfe000,0x00007f1b0fdfe000] [id=1181825]
Stack: [0x00007f1b0fcfe000,0x00007f1b0fdfe000], sp=0x00007f1b0fdfc3f0, free space=1016k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0xad3455] VMError::report_and_die()+0x2e5
V [libjvm.so+0x4e0537] report_vm_out_of_memory(char const*, int, unsigned long, VMErrorType, char const*)+0x67
V [libjvm.so+0x910320] os::pd_commit_memory(char*, unsigned long, unsigned long, bool)+0x100
V [libjvm.so+0x90794f] os::commit_memory(char*, unsigned long, unsigned long, bool)+0x1f
V [libjvm.so+0x98c736] PSVirtualSpace::expand_by(unsigned long)+0x56
V [libjvm.so+0x98d9c8] PSYoungGen::resize(unsigned long, unsigned long)+0xd8
V [libjvm.so+0x98a166] PSScavenge::invoke_no_policy()+0x1376
V [libjvm.so+0x98a4fc] PSScavenge::invoke()+0x4c
V [libjvm.so+0x93a248] ParallelScavengeHeap::failed_mem_allocate(unsigned long)+0x68
V [libjvm.so+0xad4fa3] VM_ParallelGCFailedAllocation::doit()+0x93
V [libjvm.so+0xada1c6] VM_Operation::evaluate()+0x46
V [libjvm.so+0xad84fd] VMThread::evaluate_operation(VM_Operation*) [clone .constprop.44]+0xcd
V [libjvm.so+0xad8ae3] VMThread::loop()+0x3a3
V [libjvm.so+0xad8eb8] VMThread::run()+0x78
V [libjvm.so+0x90d952] java_start(Thread*)+0x102
VM_Operation (0x00007f1aef4f5070): ParallelGCFailedAllocation, mode: safepoint, requested by thread 0x00007f1a78472000
奔溃的日志出奇一致,很明显内存不足,笔者这次案例很明显,找几个日志一致一样的报错,集中分析。笔者这次案例很明显,内存不足。之前的案例,有个IPV6的,可以去看我旧的文章!
结合看服务器状态综合分析
top
服务器状态很明显,虚拟内存饱满,物理内存不足,JAVA程序与其他服务争取内存资源,换来换去崩溃。
四、解决问题
既然问题已经定位了,怎么解决呢?生产环境,不能重启,又不能马上扩展物理内存。怎么办?
我给你的方案是,动态扩展服务器的虚拟内存方案。
方法如下:
一、查看当前虚拟内存的配置大小
free -h
二、创建swap文件
如果文件目录已经存在,则换个目录即可,也就是创建一个虚拟内存的页面文件!
cd /usr
mkdir swap
dd if=/dev/zero of=/usr/swap/swapfile bs=1M count=64096
提前保证路径下有剩余的空间
三.将目标设置为swap分区文件
mkswap /usr/swap/swapfile
chmod 0600 /usr/swap/swapfile #改一下权限,系统建议0600
四、 启用swap分区文件
swapon /usr/swap/swapfile
free -h #检查虚拟内存是否增加了
五、将虚拟内存的配置增加到开机自动启动配置
vim /etc/fstab
/usr/swap/swapfile swap swap defaults 0 0 #将这个内容添加到上面的配置文件
保持文件退出
注意,现在是没有重启服务器情况下,临时增加了虚拟内存,解决了危机。生产环境,白天是不能紧急重启,如果重启,服务器也有可能起不来。所以才采用这种比较保守的方法。因为临时加内存条,也来不及!!
最终虚拟内存由之前的32G扩展到64G,页面文件有剩余,把JAVA服务,及其其他服务重启,问题暂时解决,周末的时候再扩展物理内存条。
最终总结:
笔者总结一下,JAVA的JVM奔溃,要先定位JVM的日志,而不是我们JAVA自身程序的日志。日志定位先正确。
作者是真实解决生产事故做一次真实案例,提供给JVM宕机网友们一个解决问题的思路与方法。希望写的东西能帮到你,如果真的帮到你了,记得给我点赞。
作者本人简介:现任国内某大型软件公司大数据研发工程师、MySQL数据库DBA,软件架构师。直接参与设计国家级亿级别大数据项目。并维护真实企业级生产数据库300余个。紧急处理数据库生产事故上百起,挽回数据丢失所操作的灾难损失不计其数。