性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析

问题:

内存使用率88%高于80%报警。

原因:

指标含义:内存使用率百分比(%)。
指标解释:容器的内存使用率是读取物理机cgroup下面的文件的,获取的是整个容器的内存使用率并不是针对某个程序。物理机内存使用率和使用free命令计算结果是一致的。物理机和容器两者内存计算数据是独立的

解决步骤:

1、查看哪些应用占用内存比较大

查看哪几个进程内存占用最高:top -c,输入大写M,以内存使用率从高到低排序

2、先通过jmap -heap 进程id 命令排除是由于堆分配内存问题。得到如下结果

Attaching to process ID 542287, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.20-b23

using thread-local object allocation.
Garbage-First (G1) GC with 43 thread(s)
//堆配置信息
Heap Configuration:
   //指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩 ,Xmx==Xms 的情况下无效 , 如 
   MinHeapFreeRatio         = 40
   //指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张 ,Xmx==Xms 的情况下无效 , 如 
   MaxHeapFreeRatio         = 70
   //最大堆空间
   MaxHeapSize              = 5393874944 (5144.0MB)
   //设置Yong Generation的初始值大小,一般情况下,不允许-XX:Newratio值小于1,即Old要比Yong大。
   NewSize                  = 1363144 (1.2999954223632812MB)
   //设置Yong Generation的最大值大小
   MaxNewSize               = 3235905536 (3086.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   //设置年轻代和老年代的比例,默认情况下,此选项为2
   NewRatio                 = 2
   //默认eden空间大小和survivor空间大小的比,默认情况下为8
   SurvivorRatio            = 8
   //初始化元空间大小,控制gc阀值,gc后动态增加或者降低元空间大小,默认情况下平台的不同,步长为12-20M
   MetaspaceSize            = 209715200 (200.0MB)
   //默认1G,这个参数主要是设置Klass Metaspace的大小,不过这个参数设置了也不一定起作用,前提是能开启压缩指针,假如-Xmx超过了32G,压缩指针是开启不来的。如果有Klass Metaspace,那这块内存是和Heap连着的。
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   //为类元数据分配的最大空间量
   MaxMetaspaceSize         = 536870912 (512.0MB)
   //堆内存中一个Region的大小可以通过-XX:G1HeapRegionSize参数指定,大小区间只能是1M、2M、4M、8M、16M和32M,总之是2的幂次方,如果G1HeapRegionSize为默认值,则在堆初始化时计算Region的实践大小
   G1HeapRegionSize         = 2097152 (2.0MB)
//堆的使用信息
Heap Usage:
G1 Heap:
//区域数量
   regions  = 2572
//堆内存大小
   capacity = 5393874944 (5144.0MB)
//已经使用了
   used     = 3216639400 (3067.62638092041MB)
//空闲着的堆内存
   free     = 2177235544 (2076.37361907959MB)
   59.63503850933923% used
以下同理
G1 Young Generation:
Eden Space:
   regions  = 425
   capacity = 2650800128 (2528.0MB)
   used     = 891289600 (850.0MB)
   free     = 1759510528 (1678.0MB)
   33.62341772151899% used
Survivor Space:
   regions  = 1
   capacity = 2097152 (2.0MB)
   used     = 2097152 (2.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 1109
   capacity = 2740977664 (2614.0MB)
   used     = 2323252648 (2215.62638092041MB)
   free     = 417725016 (398.37361907958984MB)
   84.75999927009985% used

35394 interned Strings occupying 3871104 bytes.

3、找到最耗内存的对象
jmap -histo 进程ID(带上:live则表示先进行一次FGC再统计,如jmap -histo:live 进程ID)

性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析
可以看到上面最大的实例进程 将近30M。

4、导出内存转储快照:
4.1、通过java进程命令定位 系统进程并使用jmap工具dump文件。

ps -ef | grep java 
生成dump文件的命令:
jmap -dump:format=b,file=20181218.dump 16048
file后面的是自定义的文件名,最后的数字是进程的pid。

4.2、使用jvisualvm来分析dump文件:

jvisualvm是JDK自带的Java性能分析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。
jvisualvm可以监控本地、远程的java进程,实时查看进程的cpu、堆、线程等参数,对java进程生成dump文件,并对dump文件进行分析。
假设我现在下载下来的是txt文件也可以直接扔给jvisualvm来分析。

性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析

4.3、使用方式:直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开。

导入文件以后界面如下图:
性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析
性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析

可以看到,dump文件里记录的堆中的实例,总大小大概5392M左右,(用第一行的实例大小除以百分比就能算出来)

4.4、现在看堆转储的线程问题

性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析
每一个部分的含义如下:
“http-nio-1601-Acceptor-0” 线程名称
daemon 线程的类型
prio=5 线程的优先级别
tid=290 线程ID
RUNNABLE 线程当前的状态

4.5、线程当前的状态是我们主要关注的内容。
dump文件中描述的线程状态

runnable:运行中状态,在虚拟机内部执行,可能已经获取到了锁,可以观察是否有locked字样。
blocked:被阻塞并等待锁的释放。
wating:处于等待状态,等待特定的操作被唤醒,一般停留在park(), wait(), sleep(),join() 等语句里。
time_wating:有时限的等待另一个线程的特定操作。
terminated:线程已经退出

4.6、进程的区域划分

进入区(Entry Set):等待获取对象锁,一旦对象锁释放,立即参与竞争。
拥有区(The Owner):已经获取到锁。
等待区(Wait Set):表示线程通过wait方法释放了对象锁,并在等待区等待被唤醒。

4.7、方法调用修饰

locked: 成功获取锁
waiting to lock:还未获取到锁,在进入去等待;
waiting on:获取到锁之后,又释放锁,在等待区等待;

4.8、OQL(对象查询语言)
如果需要根据某些条件来过滤或查询堆的对象,比如现在我们查询下系统中类加载器一共有几种?

性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析

4.9、引导计数
引导类 (即 JVM 在未使用任何 java.lang.ClassLoader 实例的情况下加载的 Java 平台类) 的计数
其余展示的与名称一样

5、统计进程打开的句柄数:ls /proc/进程ID/fd |wc -l

6、统计进程打开的线程数:ls /proc/进程ID/task |wc -l

7、使用jstat查看进程的内存使用情况
jstat [Options] vmid [interval] [count]
Options,选项,我们一般使用 -gcutil 查看gc情况
vmid,VM的进程号,即当前运行的java进程号
interval,间隔时间,单位为秒或者毫秒
count,打印次数,如果缺省则打印无数次

  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00 100.00  32.20   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
  0.00 100.00  32.57   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
  0.00 100.00  32.94   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
  0.00 100.00  33.31   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775
  0.00 100.00  33.62   7.05  48.98  95.35 102490 10125.674     1   39.100 10164.775

 S0C:年轻代中第一个survivor(幸存区)的容量 (字节) 
S1C:年轻代中第二个survivor(幸存区)的容量 (字节) 
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节) 
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节) 
EC:年轻代中Eden(伊甸园)的容量 (字节) 
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节) 
OC:Old代的容量 (字节) 
OU:Old代目前已使用空间 (字节) 
PC:Perm(持久代)的容量 (字节) 
PU:Perm(持久代)目前已使用空间 (字节) 
YGC:从应用程序启动到采样时年轻代中gc次数 
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s) 
FGC:从应用程序启动到采样时old代(全gc)gc次数 
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s) 
GCT:从应用程序启动到采样时gc用的总时间(s) 
NGCMN:年轻代(young)中初始化(最小)的大小 (字节) 
NGCMX:年轻代(young)的最大容量 (字节) 
NGC:年轻代(young)中当前的容量 (字节) 
OGCMN:old代中初始化(最小)的大小 (字节) 
OGCMX:old代的最大容量 (字节) 
OGC:old代当前新生成的容量 (字节) 
PGCMN:perm代中初始化(最小)的大小 (字节) 
PGCMX:perm代的最大容量 (字节) 
PGC:perm代当前新生成的容量 (字节) 
S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 
S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 
E:年轻代中Eden(伊甸园)已使用的占当前容量百分比 
O:old代已使用的占当前容量百分比 
P:perm代已使用的占当前容量百分比 
M:元空间中已使用的占当前容量百分比
S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节) 
S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节) 
ECMX:年轻代中Eden(伊甸园)的最大容量 (字节) 
DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满) 
TT: 持有次数限制 
MTT : 最大持有次数限制

8、.用jstack查看一下
jstack pid | grep tid(线程ID) -A 30

性能调优-------(六)内存使用率高,5分钟如何快速解决dump文件分析