OOM表示程序存在漏洞,可能是代码或JVM参数配置引起的。本文就和读者聊聊,Java进程触发OOM后如何排查?常说对生产环境敬畏,快速解决问题也是一种敬畏的表现。为什么OOMOOM代表“OutOfMemory”,意思是内存用完了。当JVM没有足够的内存来为对象分配空间,并且垃圾收集器已用完空间来回收时,会抛出此错误。为什么会出现OOM,一般都是这些问题引起的。分配太少:JVM初始化内存少,业务占用内存大;或不同JVM区域内存分配不合理代码漏洞:某个对象频繁申请,但不用后不释放,导致内存耗尽内存泄漏:申请使用完毕内存没有释放,以至于虚拟机无法再次使用该内存,此时这块内存就泄漏了。因为申请者不再使用了,不能被虚拟机分配给别人。内存溢出:申请的内存超过了JVM所能提供的内存大小。这时候,就叫做溢出。内存泄漏持续存在,最终会溢出。两者都是CommoncausalOOM比较常见的OOM类型有如下几种:编译器编译的数据。每当第一次加载类时,元数据将存储在永久代中。一般出现在大量的Class对象或者JSP页面中,或者使用CgLib动态代理技术让我们可以通过-XX:PermSize和-XX:MaxPermSizeJava8修改方法区的大小,将永久代改为元空间,报错:java.lang.OutOfMemoryError:Metadataspace,元空间内存不足,默认进行动态扩容。的。如果堆栈大小设置得太小,也会发生溢出。您可以通过-Xss设置堆栈大小。虚拟机抛出栈溢出错误,可以在日志中定位错误的类和方法java.lang.OutOfMemoryError:JavaheapspaceJavaheapmemoryoverflow,溢出的原因一般是JVM堆内存设置不合理或者内存泄漏。如果是内存泄漏,可以使用工具查看从泄漏对象到GCRoots的引用链。通过掌握泄露对象的类型信息和GCRoots引用链信息,可以准确定位泄露代码的位置。如果没有内存泄漏,也就是内存中的对象一定还活着,那么就应该查看虚拟机的堆参数(-Xmx和-Xms),看看虚拟机的内存是否可以增加.总结:方法区和虚拟机栈的溢出场景本文不过多讨论。下面主要讲解Java堆空间常见OOM排查思路查看JVM内存分布假设我们的Java应用PID为15162,输入命令查看JVM内存分布jmap-heap15162[xxx@xxx~]#jmap-heap15162AttachingtoprocessID15162,pleasewait...Debuggerattachedsuccessfully.Servercompilerdetected.JVMversionis25.161-heapb12usingthreadConfipapweplocal-GCobjectall:MinHeapFreeRatio=40#最小堆使用率MaxHeapFreeRatio=70#最大堆可用率MaxHeapSize=482344960(460.0MB)#最大堆空间大小=1850.0MB(1850.0MB)#最大堆空间大小NewSize)#新生代分配大小MaxNewSize=160759808(153.3125MB)#新生代最大可分配大小OldSize=20971520(20.0MB)#OldgenerationsizeNewRatio=2#CenozoicratioSurvivorRatio=8#CenozoicandSurvivorratioMetaspaceSize=21807104(20.796875MB)#MetaspacesizeCompressedClassSpaceSize=1073741824(1024.0MB)#CompressedClassSpace空间大小限制MaxMetaspaceSize=17592186044415MB#最大元空间大小G1HeapRegionSize=0(0.0MB)#G1单个Region大小HeapUsage:#HeapusageNewGeneration(Eden+1SurvivorSpace):#Newgenerationcapacity=9502720(9.0625MB)#新生代已使用总容量=4995320(4.763908386230469MB)#新生代已使用free=4507400(4.298591613769531MB)#新生代剩余容量52.56726495150862%used#新生代已使用EdenSpace比例:capacity=8454145(8.062)Eden区总使用容量=4029752(3.8430709838867188MB)#Eden区已使用free=4424392(4.219429016113281MB)#Eden区剩余容量为47.665996699370154%usedFrom#SpacEdene区#其中一个Survivor区的内存分配capacity=4875(1.0MB)used=965568(0.92083740234375MB)free=83008(0.07916259765625MB)92.083740234375%usedToSpace:#另一个Survivor区的内存分配capacity=1048576)used=0(0.0MB0.0MB4.805=free)0.0%usedtenuredgeneration:#老年代capacity=20971520(20.0MB)used=10611384(10.119804382324219MB)free=10360136(9.880195617675781MB)50.599021911621094%used10730internedStringsoccupying906232bytes.通过查看JVM内存分配以及运行时使用情况,可以判断内存是否为llocation是否合理另外可以查看JVM运行时最耗资源的对象,jmap-histo:live15162|moreJVM内存对象列表是根据对象实例的内存大小排序的:实例数bytes:单位字节类名:明显的类名看到CustomObjTest对象实例占用内存太多可惜解决方案有局限性,因为它只能排查“[”代表数组的对象占用内存过大的问题,比如“[C”代表一个Char数组,"[B"代表字节数组。如果数组内存占用过多,我们不知道是哪些对象持有它,所以我们需要将内存dump出来进行离线分析jmap-histo:live执行这条命令,JVM会先触发GC,然后统计信息Dump文件分析Dump文件是Java进程的内存映像,主要包括系统信息、虚拟机属性、完整的线程转储、所有类和对象的状态等。当程序出现内存溢出或GC异常时,它是怀疑是JVM发生了Memoryleak,那么我们可以导出Dump文件分析JVM启动参数配置,添加如下参数-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=./(参数为生成Dump文件的路径)当JVM出现OOM异常时自动导出Dump文件,文件名称的默认格式:java_pid{pid}.hprof以上配置是应用抛出OOM后自动导出Dump,即Dumpfile可以在JVM运行时导出jmap-dump:file=[filepath][pid]#Examplejmap-dump:file=./jvmdump.hprof15162本地写一段测试代码验证OOM,分析Dump文件设置VM参数:-Xms3m-Xmx3m-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=./publicstaticvoidmain(String[]args){List
