Java jmap命令学习

Java dump的学习

参考博客感谢原作者Hoils

Java虚拟机运行的快照,将Java虚拟机运行时的状态和信息保存到文件,主要分为两种
- 线程Dump,包含所有线程的运行状态。文本格式
- 堆Dump,包含线程Dump,幵包含所有堆对象的状态。二进制格式。

制作java dump的方式

自动制作java dump使用图形化工具制作Dump使用命令行制作Dump
指示虚拟机在发生内存不足错误时,自动生成堆Dump,-XX:+HeapDumpOnOutOfMemoryError使用JDK(1.6)自带的工具:Java VisualVMjstack:打印线程的栈信息,制作线程Dump,jmap:打印内存映射,制作堆Dump。

jps

功能

ps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。

底层实现

jdk中的jps命令可以显示当前运行的java进程以及相关参数,它的实现机制如下:
java程序在启动以后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个类似于hsperfdata_User的文件夹,这个文件夹里(在Linux中为/tmp/hsperfdata_{userName}/),有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。

参数说明

-q 只显示pid,不显示class名称,jar文件名和传递给main 方法的参数
-m 输出传递给main 方法的参数 
-l 输出应用程序main class的完整package名 

C:\Users\Think>jps
2248 Jps

C:\Users\Think>jps -l
4604 sun.tools.jps.Jps



-v 输出传递给JVM的参数 在这里,在启动main方法的时候,我给jvm传递一个参数:-Dfile.encoding=UTF-8,执行jps -v

下面的例子是我启动我的eclipse之后的显示

C:\Users\Think>jps -v
10728 Jps -Denv.class.path=.;E:\Java\lib\dt.jar;E:\Java\lib\tools.jar; -Dapplication.home=E:\Java -Xms8m
10444  -Dosgi.requiredJavaVersion=1.8 -Xms40m -Dosgi.module.lock.timeout=10 -Dorg.eclipse.swt.browser.IEVersion=10001 -X
mx1200m

jps失效的原因

用ps -ef|grep java能看到启动的java进程,但是用jps查看却不存在该进程的id。待会儿解释过之后就能知道在该情况下,jconsole、jvisualvm可能无法监控该进程,其他java自带工具也可能无法使用

总结原因:jps只会从/tmp目录读取,有可能我们设置了(-Djava.io.tmpdir)虚拟机参数,还可能是我们在linux系统上没有权限读这个文件,或者是/tmp目录被清空

jstack

功能

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈

线程的几种状态 及基本知识

NEW 未启动的。不会出现在Dump中。

RUNNABLE,在虚拟机内执行的。

BLOCKED,受阻塞并等待监视器锁。

WATING,无限期等待另一个线程执行特定操作。

TIMED_WATING,有时限的等待另一个线程的特定操作。

TERMINATED,已退出的。

在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor下 面这个图,描述了线程和 Monitor之间关系,以 及线程的状态转换图:

image

  • 进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。

  • 拥有者(The Owner):表示某一线程成功竞争到对象锁。

  • 等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

从图中可以看出,一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。 先看 “Entry Set”里面的线程。我们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。

locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。

waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在冲入区等待。 即是 Entry Set

waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。即是 Wait Set

parking to wait for <地址> 目标

parking to wait for

park是基本的线程阻塞原语,不通过监视器在对象上阻塞。随concurrent包会出现的新的机制,与synchronized体系不同。(ps:我看作者博客也没看懂)

几种线程资源堵塞的情况

  • 进入队列阻塞
"d&a-3588" daemon waiting for monitor entry [0x000000006e5d5000]  
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()
- waiting to lock <0x0000000602f38e90> (a java.lang.Object)
at com.jiuqi.dna.bap.authority.service.UserService$LoginHandler.handle()

线程状态BLOCKED,线程动作wait on monitor entry(第一行),调用修饰waiting to lock总是一起出现。表示在代码级别已经存在冲突的调用。
- 同步块阻塞

可以看出是blockee-11 和 blockee-86”都在等待 ,都在 monitor entry 队列等待

"blocker" runnable
java.lang.Thread.State: RUNNABLE
at com.jiuqi.hcl.javadump.Blocker$1.run(Blocker.java:23)
- locked <0x00000000eb8eff68> (a java.lang.Object)
"blockee-11" waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
"blockee-86" waiting for monitor entry
java.lang.Thread.State: BLOCKED (on object monitor)
at com.jiuqi.hcl.javadump.Blocker$2.run(Blocker.java:41)
- waiting to lock <0x00000000eb8eff68> (a java.lang.Object)
持续运行的IO IO操作是可以以RUNNABLE状态达成阻塞。例如:数据库死锁、
  • 持续运行的IO
    IO操作是可以以RUNNABLE状态达成阻塞。例如:数据库死锁、网络读写。 格外注意对IO线程的真实状态的分析。 一般来说,被捕捉到RUNNABLE的IO调用,都是有问题的。

以下堆栈显示: 线程状态为RUNNABLE。 调用栈在SocketInputStream或SocketImpl上,socketRead0等方法。 调用栈包含了jdbc相关的包。很可能发生了数据库死锁

"d&a-614" daemon prio=6 tid=0x0000000022f1f000 nid=0x37c8 runnable
[0x0000000027cbd000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at oracle.net.ns.Packet.receive(Packet.java:240)
at oracle.net.ns.DataPacket.receive(DataPacket.java:92)
at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:172)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:117)
at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1034)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:588)

jmaps操作

功能:jmap是JDK自带的工具软件,主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。可以使用jmap生成Heap Dump,堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。 一般,在内存不足、GC异常等情况下,我们就会怀疑有内存泄露。这个时候我们就可以制作堆Dump来查看具体情况。

还可以远程调试 服务器的命令jmap [option] [server_id@]

参数

<no option> 如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
-dump:[live,]format=b,file=<filename> 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
-finalizerinfo 打印等待终结的对象信息。
-heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
-histo[:live] 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。
-permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
-F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
-h 打印帮助信息。
-help 打印帮助信息。
-J<flag> 指定传递给运行jmap的JVM的参数。

-heap 打印一个堆的摘要信息 包括使用的GC算法、堆配置信息和generation wise heap usage。


C:\Users\Think>jmap -heap 10444
Attaching to process ID 10444, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0 /对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
   MaxHeapFreeRatio         = 100 对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
   MaxHeapSize              = 1258291200 (1200.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
   NewSize                  = 13631488 (13.0MB)/对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
   MaxNewSize               = 419430400 (400.0MB)  /对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
   OldSize                  = 28311552 (27.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
    //对应jvm启动参数-XX:PermSize=<value>:设置JVM堆的‘永生代’的初始大小,jdk1.8变成了元空间
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 177209344 (169.0MB)
   used     = 129068368 (123.08918762207031MB)
   free     = 48140976 (45.91081237792969MB)
   72.83383882962741% used
From Space:
   capacity = 52428800 (50.0MB)
   used     = 35522768 (33.87715148925781MB)
   free     = 16906032 (16.122848510742188MB)
   67.75430297851562% used
To Space:
   capacity = 54001664 (51.5MB)
   used     = 0 (0.0MB)
   free     = 54001664 (51.5MB)
   0.0% used
PS Old Generation
   capacity = 224395264 (214.0MB)
   used     = 141988944 (135.4112091064453MB)
   free     = 82406320 (78.58879089355469MB)
   63.27626593759127% used

90919 interned Strings occupying 16817872 bytes.

C:\Users\Think>
  • 查看堆内存(histogram)中的对象数量及大小
    应该配合过滤条件,找出占字节空间比较大的一些元素
num     #instances         #bytes  class name
编号     个数                字节     类名
----------------------------------------------
   1:             7        1322080  [I
   2:          5603         722368  <methodKlass>
   3:          5603         641944  <constMethodKlass>
   4:         34022         544352  java.lang.Integer
   5:           371         437208  <constantPoolKlass>
   6:           336         270624  <constantPoolCacheKlass>
   7:           371         253816  <instanceKlassKlass>
   后面省略

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

  • 输出内存使用情况到文件中
    将内存使用的详细情况输出到文件,执行命令: hollis@hos:~/workspace/design_apaas/apaasweb/control/bin$ jmap -dump:format=b,file=heapDump 6900

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

总结

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值