linux-命令-子篇-工具命令-jmap

jmap命令(Java Memory Map)

jmap用于生成虚拟机的内存快照信息(特别是可以获得运行中的jvm的堆的快照---,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用jmap生成Heap Dump。 
java memory = direct memory(直接内存) + jvm memory(MaxPermSize +Xmx)

1)直接内存跟堆
直接内存则是一块由程序本身管理的一块内存空间,它的效率要比标准内存池要高,主要用于存放网络通信时数据缓冲和磁盘数据交换时的数据缓冲。 
DirectMemory容量可以通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认为与Java堆的最大值(-Xmx指定)一样。但是,在OSX上的最新版本的 JVM,对直接内存的默认大小进行修订,改为“在不指定直接内存大小的时默认分配的直接内存大小为64MB”,可以通过 -XX:MaxMemorySize来显示指定直接内存的大小。 
2)堆(Heap)和非堆(Non-heap)内存

按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。
可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,
所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中(还有直接内存也不是非堆)。

3)栈与堆

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

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

Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等 指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时 动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类 型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。

命令格式

jmap [ option ] pid  (to connect to remote debug server)   
jmap [ option ] executable core  (to connect to remote debug server)   
jmap [ option ] [server-id@]remote-hostname-or-IP  (to connect to remote debug server) 

---------options明细-------

1、jmap -heap pid 展示pid的整体堆信息

#ps -ef|grep tomcat  #获取tomcat的pid

 

[root@localhost ~]# jmap -heap 27900
Attaching to process ID 27900, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: #堆内存初始化配置
   MinHeapFreeRatio = 40     #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率  
   MaxHeapFreeRatio = 70   #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率  
   MaxHeapSize = 100663296 (96.0MB)   #-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize = 1048576 (1.0MB)     #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize = 4194304 (4.0MB)  #-XX:OldSize=设置JVM堆的‘老生代’的大小
   NewRatio = 2    #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio = 8  #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
   PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:设置JVM堆的‘持久代’的初始大小  
   MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:设置JVM堆的‘持久代’的最大大小  
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代区内存分布,包含伊甸园区+1个Survivor区
   capacity = 30212096 (28.8125MB)
   used = 27103784 (25.848182678222656MB)
   free = 3108312 (2.9643173217773438MB)
   89.71169693092462% used
Eden Space: #Eden区内存分布
   capacity = 26869760 (25.625MB)
   used = 26869760 (25.625MB)
   free = 0 (0.0MB)
   100.0% used
From Space: #其中一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 234024 (0.22318267822265625MB)
   free = 3108312 (2.9643173217773438MB)
   7.001809512867647% used
To Space: #另一个Survivor区的内存分布
   capacity = 3342336 (3.1875MB)
   used = 0 (0.0MB)
   free = 3342336 (3.1875MB)
   0.0% used
tenured generation:   #当前的Old区内存分布  
   capacity = 67108864 (64.0MB)
   used = 67108816 (63.99995422363281MB)
   free = 48 (4.57763671875E-5MB)
   99.99992847442627% used
Perm Generation:     #当前的 “持久代” 内存分布
   capacity = 14417920 (13.75MB)
   used = 14339216 (13.674942016601562MB)
   free = 78704 (0.0750579833984375MB)
   99.45412375710227% used

 

2、jmap -histo pid 展示class的内存情况

说明:instances(实例数)、bytes(大小)、classs name(类名)。它基本是按照使用使用大小逆序排列的。 

 从打印结果可看出,类名中存在[C、[B等内容,

  • 只知道它占用了那么大的内存,但不知道由什么对象创建的。下一步需要将其他dump出来,使用内存分析工具进一步明确它是由谁引用的、由什么对象

3、 jmap -histo:live pid>a.log

可以观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。 可以将其保存到文本中去,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。

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

 

4、dump 将内存使用的详细情况输出到文件(dump出堆内存快照)

jmap -dump:live,format=b,file=a.log pid

说明:内存信息dump到a.log文件中。(heap中所有生存的对象)

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

 

拓展:

该命令通常用来分析内存泄漏OOM,通常做法是:

1)首先配置JVM启动参数,让JVM在遇到OutOfMemoryError时自动生成堆Dump文件(dump出堆内存快照)--死机那一刻不dump就无法得到此刻快照了

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path

 

2)然后使用命令--拿到现在最新的快照了(为了和死机那刻对比分析)

jmap  -dump:format=b,file=/path/heap.bin 进程ID  

如果只dump heap中的存活对象,则加上选项-live。 

 

 3)然后使用MAT分析工具,如jhat命令,eclipse的mat插件。

最后在eclipse中安装MAT插件(http://www.eclipse.org/mat/),然后在eclipse中,file---->open,打开这个文件heap.bin,利用现成的OOM工具进行分析。
具体操作方法:
首先输入网址http://www.eclipse.org/mat/previousReleases.php,然后查看你自己的Eclipse版本,我的是Indigo的,所以点击链接“Previous Releases”,选择Indigo版本的URLhttp://download.eclipse.org/mat/1.1.0/update-site/ 

 用jhat命令可以参看 jhat -port 5000 heapDump 在浏览器中访问:http://localhost:5000/ 查看详细信息

四、性能问题查找

转自:http://www.cnblogs.com/gaojk/articles/3886503.html

1、发现问题

1)、使用uptime命令查看CPU的Load情况,Load越高说明问题越严重;

2)、使用jstat查看FGC发生的频率及FGC所花费的时间,FGC发生的频率越快、花费的时间越高,问题越严重;

 

2、导出数据:在应用快要发生FGC的时候把堆导出来

1)、查看快要发生FGC使用命令:

jmap -heap <pid>

会看到如下图结果:

 

    以上截图包括了新生代、老年代及持久代的当前使用情况,如果不停的重复上面的命令,会看到这些数字的变化,变化越大说明系统存在问题的可能性越大,特别是被红色圈起来的老年代的变化情况。现在看到的这个值为使用率为99%或才快接近的时候,就立即可以执行导出堆栈的操作了。

    注:这是因为我这里没有在jvm参数中使用"-server"参数,也没有指定FGC的阀值,在线上的应用中通过会指定CMSInitiatingOccupancyFraction这个参数来指定当老年代使用了百分之多少的时候,通过CMS进行FGC,当然这个参数需要和这些参数一起使用“-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly”,CMSInitiatingOccupancyFraction的默认值是68,现在中文站线上的应用都是70,也就是说当老年代使用率真达到或者超过70%时,就会进行FGC。

    2)、将数据导出:

jmap -dump:format=b,file=heap.bin <pid>

这个时候会在当前目录以生成一个heap.bin这个二进制文件。

 

3、通过命令查看大对象

也是使用jmap的命令,只不过参数使用-histo

使用:jmap -histo <pid>|less

可得到如下包含对象序号、某个对象示例数、当前对象所占内存的大小、当前对象的全限定名,如下图:

 

查看对象数最多的对象,并按降序排序输出:

执行:jmap -histo <pid>|grep alibaba|sort -k 2 -g -r|less

结果如图:

 

查看占用内存最多的最象,并按降序排序输出:

执行:jmap -histo <pid>|grep alibaba|sort -k 3 -g -r|less

结果如图:

 


4、数据分析

这个时候将dump出的文件在ECLIPSE中打开,使用MAT进行分析(ECLIPSE需要先安装MAT插件),会展示如下截图:

 

可以从这个图看出这个类java.lang.ref.Finalizer占用500多M,表示这其中很多不能够被回对象的对象,此时点开hisgogram视图,并通过Retained Heap进行排序,如下截图:

 

从图中可以看出,被线线框圈起来的三个对象占用量非常大,那说明这几个大的对象并没有被释放,那现在就可以有针对性的从代码中去找这几个对象为什么没有被释放了。

再切换到dominator_tree视图:

 

这里可以看到velocity渲染也存在着问题,以及数据库的请求也比较多。

 

5、优化

优化的思路就是上面所列出来的问题,查看实现代码中所存在问题,具体问题具体分析。

 


总结

1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
2.要制作堆Dump可以直接使用jvm自带的jmap命令
3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
5.也可以使用 jmap -dump:format=b,file=<fileName>命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。

7.在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到。

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java_爱吃肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值