20210520 使用jmap分析虚拟机内存状况

Posted 陈如水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20210520 使用jmap分析虚拟机内存状况相关的知识,希望对你有一定的参考价值。

堆(Heap)和非堆(Non-heap)内存

按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的。

栈与堆

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

什么是堆Dump

堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。 一般,在内存不足、GC异常等情况下,我们就会怀疑有内存泄露。这个时候我们就可以制作堆Dump来查看具体情况,分析原因。

outOfMemoryError 年老代内存不足。

outOfMemoryError:PermGen Space 永久代内存不足。

outOfMemoryError:GC overhead limit exceed 垃圾回收时间占用系统运行时间的98%或以上。

 

进行内存分析主要分析以下几点:

1.程序是否内存泄露;

2.程序中大对象的占用是否合理;

3.程序中部分对象产生内存是否可以优化;

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

jmap -histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。  
jmap -histo:live :同上,但是只打印存活对象的情况,显示堆中对象的统计信息。打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.live选项会在转储前强制触发一次full GC(以减小文件体积)

jmap -dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件, live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.dump 将内存使用的详细情况输出到文件


可以看出使用的是什么垃圾收集器?
Concurrent Mark-Sweep GC   CMS gc  并发标记清除垃圾收集器。

jmap -heap pid 命令查询JVM Heap初始化参数配置。虚拟机初始化参数配置。
jmap -heap pid可以查看当前JVM Heap初始化参数配置.当前虚拟机的配置参数。
jmap -dump:live,format=b,file=dmp.hprof (pid) 打印这一时刻的内存快照 (打印所有对象,包含死去的)
jmap -histo:live 1117 | head -n 20   打印占用堆内存最多的实例数量
jmap -histo:live 1117 命令 其中最后一行(total行)分别记录了实例总数、程序占用总内存数,本例显示的程序总占用内存约295M   对象只占用了300M左右的内存,但是为什么内存使用率80%?
但是如果去掉live选项,会有1G的内存占用,为什么对象没有存活,但是也没有回收?

jinfo pid,查看指定pid的所有JVM信息
jinfo -flags pid 查询虚拟机运行参数信息。

jmap -heap <pid>  :使用的垃圾收集器,虚拟机的初始化配置参数

[work(chenrushui)@tjtx145-59-206 ~]$ jmap -heap 1049
Attaching to process ID 1049, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.231-b11

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 4294967296 (4096.0MB)
   NewSize                  = 1610612736 (1536.0MB)
   MaxNewSize               = 1610612736 (1536.0MB)
   OldSize                  = 1610612736 (1536.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 268435456 (256.0MB)
   CompressedClassSpaceSize = 796917760 (760.0MB)
   MaxMetaspaceSize         = 805306368 (768.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 1449590784 (1382.4375MB)
   used     = 1170377376 (1116.1588439941406MB)
   free     = 279213408 (266.2786560058594MB)
   80.73846694654483% used
Eden Space:
   capacity = 1288568832 (1228.875MB)
   used     = 1167246768 (1113.1732635498047MB)
   free     = 121322064 (115.70173645019531MB)
   90.58474324482187% used
From Space:
   capacity = 161021952 (153.5625MB)
   used     = 3130608 (2.9855804443359375MB)
   free     = 157891344 (150.57691955566406MB)
   1.944211929563492% used
To Space:
   capacity = 161021952 (153.5625MB)
   used     = 0 (0.0MB)
   free     = 161021952 (153.5625MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 1610612736 (1536.0MB)
   used     = 305178048 (291.04046630859375MB)
   free     = 1305434688 (1244.9595336914062MB)
   18.947947025299072% used

55575 interned Strings occupying 6233032 bytes.

查看堆内存空间的变化:

1)创建大对象之前,

2)创建大对象之后,

3)触发gc以后; Eden区域used的变化。

 

jmap -histo <pid>

instances(对象的实例个数)、bytes(总占用的字节数)、classs name(对应的就是 Class 文件里的 class 的标识 )。它基本是按照使用使用大小逆序排列的。

jmap -histo pid 输出的[B 占用很高,请问问题会在哪里?

[C对象占用Heap这么多,往往跟String有关,String其内部使用final char[]数组来保存数据的。

[work(chenrushui)@tjtx145-59-206 ~]$ jmap -histo 1049 | head -n 10

 num     #instances         #bytes  class name
----------------------------------------------
   1:       4530762      383164064  [C
   2:        220104      111601216  [B
   3:        151132       67165712  [I
   4:       2146366       51512784  java.lang.String
   5:        584411       51428168  java.lang.reflect.Method
   6:        487305       26470200  [Ljava.lang.Object;
   7:        624597       19987104  java.util.HashMap$Node
Total      22888910     1426623680

jmap -dump:format=b,file=dum.hprof  <pid> 打印当前时刻的内存快照

 

对象的大小有两种统计方式:

  • 本身大小(Shallow Size):对象本来的大小。
  • 保留大小(Retained Size): 当前对象大小 + 当前对象直接或间接引用到的对象的大小总和。

看本身大小时,占大头的都是char[] ,byte[]之类的,没什么用(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。

注意事项:

1,jmap -dump这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用。

2,jmap -histo:live这个命令执行,JVM会先触发gc,然后再统计信息。

1) jstat -gcutil 1000 查看full gc执行的次数;

2) jmap -histo:live 执行这个命令;

3) jstat -gcutil 1000 查看full gc执行的次数,结果增加了一次;

以上是关于20210520 使用jmap分析虚拟机内存状况的主要内容,如果未能解决你的问题,请参考以下文章

20210520 使用jstat分析垃圾收集状况

20210520 使用jstat分析垃圾收集状况

JDK中自带的用于分析JVM内存状况的工具Jmap

Jvm基础故障处理工具

深入理解Java虚拟机——Java内存映像工具(Jmap)

深入理解Java虚拟机——Java内存映像工具(Jmap)