vlambda博客
学习文章列表

工程师利器系列5---一次线上Jvm内存占用过高分析全纪录(Jmap篇)

工程师利器系列5---一次线上Jvm内存占用过高分析全纪录(Jmap篇)

上篇文章基于jstack进行来分析,初步得出结论是线程堆栈没有问题,本篇文章将视角转到内存分析工具jmap。

一、jmap实战

1. 执行最简单命令# jmap -heap 53157

 -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.

Attaching to process ID 23621, please wait...Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: cannot open binary filesun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: cannot open binary file at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$LinuxDebuggerLocalWorkerThread.execute(LinuxDebuggerLocal.java:163) at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.attach(LinuxDebuggerLocal.java:278) at sun.jvm.hotspot.HotSpotAgent.attachDebugger(HotSpotAgent.java:671) at sun.jvm.hotspot.HotSpotAgent.setupDebuggerLinux(HotSpotAgent.java:611) at sun.jvm.hotspot.HotSpotAgent.setupDebugger(HotSpotAgent.java:337) at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:304) at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140) at sun.jvm.hotspot.tools.Tool.start(Tool.java:185) at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118) at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:49) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.tools.jmap.JMap.runTool(JMap.java:201) at sun.tools.jmap.JMap.main(JMap.java:130)Caused by: sun.jvm.hotspot.debugger.DebuggerException: cannot open binary file

HotSpot虚拟机中报出DebuggerException: cannot open binary file,那么解决方法是什么呢?

方法1、> echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 该方法仅在下次重启前有效。 
方法2、永久有效方法 sudo vi /etc/sysctl.d/10-ptrace.conf 编辑下面这行: kernel.yama.ptrace_scope = 1 修改为: kernel.yama.ptrace_scope = 0 重启系统,使修改生效。

再次执行输出和解析如下:

using parallel threads in the new generation.  ##新生代采用的是并行线程处理方式using thread-local object allocation. 
Concurrent Mark-Sweep GC ##同步并行垃圾回收
Heap Configuration:  ##堆配置情况,也就是JVM参数配置的结果[平常说的tomcat配置JVM参数,就是在配置这些]   MinHeapFreeRatio = 40 ##最小堆使用比例   MaxHeapFreeRatio = 70 ##最大堆可用比例   MaxHeapSize      = 2147483648 (2048.0MB) ##最大堆空间大小   NewSize          = 268435456 (256.0MB) ##新生代分配大小   MaxNewSize       = 268435456 (256.0MB) ##最大可新生代分配大小   OldSize          = 5439488 (5.1875MB) ##老年代大小   NewRatio         = 2  ##新生代比例   SurvivorRatio    = 8 ##新生代与suvivor的比例   PermSize         = 134217728 (128.0MB) ##perm区 永久代大小 MaxPermSize = 134217728 (128.0MB) ##最大可分配perm区 也就是永久代大小
Heap Usage: ##堆使用情况【堆内存实际的使用情况】New Generation (Eden + 1 Survivor Space):  ##新生代(伊甸区Eden区 + 幸存区survior(1+2)空间)   capacity = 241631232 (230.4375MB)  ##伊甸区容量   used     = 77776272 (74.17323303222656MB) ##已经使用大小   free     = 163854960 (156.26426696777344MB) ##剩余容量   32.188004570534986% used ##使用比例Eden Space:  ##伊甸区   capacity = 214827008 (204.875MB) ##伊甸区容量   used     = 74442288 (70.99369812011719MB) ##伊甸区使用   free     = 140384720 (133.8813018798828MB) ##伊甸区当前剩余容量   34.65220164496263% used ##伊甸区使用情况From Space: ##survior1区   capacity = 26804224 (25.5625MB) ##survior1区容量   used     = 3333984 (3.179534912109375MB) ##surviror1区已使用情况   free     = 23470240 (22.382965087890625MB) ##surviror1区剩余容量   12.43827838477995% used ##survior1区使用比例To Space: ##survior2 区   capacity = 26804224 (25.5625MB) ##survior2区容量   used     = 0 (0.0MB) ##survior2区已使用情况   free     = 26804224 (25.5625MB) ##survior2区剩余容量   0.0% used ## survior2区使用比例PS Old  Generation: ##老年代使用情况   capacity = 1879048192 (1792.0MB) ##老年代容量   used     = 30847928 (29.41887664794922MB) ##老年代已使用容量   free     = 1848200264 (1762.5811233520508MB) ##老年代剩余容量   1.6416783843721663% used ##老年代使用比例Perm Generation: ##永久代使用情况   capacity = 134217728 (128.0MB) ##perm区容量   used     = 47303016 (45.111671447753906MB) ##perm区已使用容量   free     = 86914712 (82.8883285522461MB) ##perm区剩余容量 35.24349331855774% used ##perm区使用比例

实际上,我们只是试探性的操作,这个命令也并非我们最期待能给出分析结果的命令,继续往下看。

2. 执行统计命令# jmap -histo:live 53157 | more

num #instances #bytes class name---------------------------------------------- 1: 12240 29874472 [B 2: 195859 16396784 [C 3: 396617 12691744 java.util.HashMap$Node 4: 75945 6709640 [Ljava.util.HashMap$Node; 5: 206626 4959024 java.lang.String 6: 69888 3354624 java.util.HashMap 7: 24569 2162072 java.lang.reflect.Method ......

通过上述命令打印每个class的实例数目、内存占用、类全名信息统计直方图,如果带上live则只统计活对象,其中class name具体含义说明如下:

class name是对象类型,说明如下:B byteC charD doubleF floatI intJ longZ boolean[ 数组,如[I表示int[][L+类名 其他对象

在返回的结果中,占据内存数量最大的前三位是[B  、[C 、java.util.HashMap$Node,因为其中[B  、[C 是基本的数据类型,一般需要依赖其他数据结构存储,所以初步将重点转到java.util.HashMap$Node,即可能是因为JVM进程中有太多没有被GC的HashMap导致内存占用增加。

当然此时,可以进一步通过如下命令统计实例所占内存大小和排名,具体如下:

jmap -histo:live 17863|awk ‘{if(NR>3)a+=$3}END{print a}’ 233455600 这个单位是byte , 换算后222.64061MB
一个JVM中什么类的实例最多?一个JVM中什么类的占用的合计容量最大? Q:统计实例最多的类 前十位有哪些? A:jmap -histo pid | sort -n -r -k 2 | head -10 Q:统计合计容量前十的类有哪些? A:jmap -histo pid | sort -n -r -k 3 | head -10


3. 执行class相关统计命令,分析类相关信息

1)# jmap -finalizerinfo 53157  打印正等候回收的对象的信息

 

3)# jmap -clstats 53157 打印类和类加载器相关信息

以上因为输出内容太多,不再将结果一一贴出。接下来执行最重要的分析命令。


4. 最关键分析命令

# jmap–dump:live,format=b,file=/tmp/53157.hprof 53157 #仅包含live对象

# jmap–dump:format=b,file=/tmp/53157.hprof  53157 #包含全部对象

此处,执行第2条命令输出全部对象到53157.hprof 文件,接下来要做的事就是将该文件导入相关分析工具进行分析,一般用MAT、VisualVM等分析工具进行分析,但我们接下来将采用更好的工具IDEA的JProfiler来进行分析,将在下篇文章进行详解,敬请关注。


相关推荐:




Reference:

https://blog.csdn.net/u010808135/article/details/94650074

https://www.cnblogs.com/sxdcgaq8080/p/11089664.html