JDK提供的命令行
以下每个工具都可以
cmd --help
的方式查看说明
jps
查看当前用户启动jvm进程
jps -m
: 显示传递给Main方法的参数jps -l
: 显示Java进程的完整包名jps -v
: 显示JVM的参数jps -lvm
jvm启动后会在
/tmp/hsperfdata_<username>/
目录下生成一个pid为名的文件, 这个目录由-Djava.io.tmpdir
参数指定, 如果因为某些原因这个文件没有生成, jps也就不起作用
文件内容:?
jstat
查看每个分代的使用率和GC次数,在没有GUI图形的服务器上是运行期定位虚拟机性能问题的首选。
注意jstat返回只有 YGC和 FGC,并不区分 Major GC和 Full GC;
jstat和-XX:+PrintGCDetails
提供的结果有不同,在于:
jstat无法统计并行的任务,比如UseConcMarkSweepGC
情况下,初始mark和remark阶段都会有 Stop the World的耗时,jstat的输出会把两个STW阶段视作两次 Full GC;
而在GC日志里可以清楚的看到 UseConcMarkSweepGC情况下,每个阶段的耗时。
例如, 如果配置了CMS垃圾回收器,那么 jstat中的 FGC增加1并不表示就一定发生了 Full GC,很有可能是发生了老年代的 CMS GC,而且每发生一次老年代的 CMS GC,jstat中的 FGC就会+2
常用jstat命令:
jstat -gc pid
: 统计JVM内存(Young/Old/Method)的已使用/总空间大小,以及Young GC和Full GC发生次数和耗时;jstat -gcutil pid
: 统计JVM内存(Young/Old/Method)的占用百分比,以及Young GC和Full GC发生次数和耗时;jstat -class pid
: 类装载、卸载数量、总空间, 及类装载所耗费的时间jstat -gccapacity pid
: 查看三代(young,old,perm)对象的使用量大小(字节)jstat -gcnew pid
: 年轻代的容量和GC情况jstat -gcnewcapacity pid
:jstat -gcold pid
: 老年代的容量和GC情况jstat -gcoldcapacity pid
:
如果用jstat查看远程机器上的jvm, 需要在远程主机启动jstatd(详见 jstatd)
jstat -gc pid@remote_IP # 用jstat连接远端的jstatd
jstat -gc返回列解析
示例1: attaches 到pid=14542的进程上, -h3表示每三行打印一次列名称, 采样间隔5sjstat -gc -h3 14542 5s
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
10240.0 10752.0 0.0 0.0 133120.0 10215.4 87552.0 9225.3 27904.0 26661.4 3328.0 3007.2 20 0.305 13 1.410 1.715
10240.0 10752.0 0.0 0.0 133120.0 10659.2 87552.0 9225.3 27904.0 26661.4 3328.0 3007.2 20 0.305 13 1.410 1.715
10240.0 10752.0 0.0 0.0 133120.0 10659.2 87552.0 9225.3 27904.0 26661.4 3328.0 3007.2 20 0.305 13 1.410 1.715
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
10240.0 512.0 0.0 0.0 133120.0 79.2 87552.0 9224.0 27904.0 26662.1 3328.0 3007.2 21 0.318 14 1.522 1.840
10240.0 512.0 0.0 0.0 133120.0 523.0 87552.0 9224.0 27904.0 26662.1 3328.0 3007.2 21 0.318 14 1.522 1.840
10240.0 512.0 0.0 0.0 133120.0 523.0 87552.0 9224.0 27904.0 26662.1 3328.0 3007.2 21 0.318 14 1.522 1.840
每列说明如下:
- S0C: S0 Capacity(KB)
- S0U: S0 Utilization(KB)
- EC: Current eden space capacity (kB).
- EU: Eden space utilization (kB).
- MC: Metaspace capacity (kB).
- MU: Metacspace utilization (kB).
- YGC: 从JVM进程启动到当前采样,发生young gen GC总次数
- YGCT: 从JVM进程启动到当前采样,young gen GC总消耗时间(秒), 相邻两次相减就是该次耗时
- FGC: 从JVM进程启动到当前采样,发生full GC总次数
- FGCT: 从JVM进程启动到当前采样,full GC总消耗时间(秒), 相邻两次相减就是该次耗时
注: 上面是java 1.8的jstat的返回值, 可以看到有Metaspace(元空间), 如果是java 1.7或更老版本, 则没有MC/MU, 而是PC/PU:
- PC:Perm Capacity(KB)
- PU: Perm Utilization(KB)
jstat -gcutil返回列解析
示例2: attaches 到pid=14542的进程上, -h3表示每三行打印一次列名称, 采样间隔250msjstat -gcutil -h3 14542 3s
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
16.35 0.00 94.62 63.31 97.41 94.84 4522 83.661 32 0.931 84.592
16.35 0.00 95.52 63.31 97.41 94.84 4522 83.661 32 0.931 84.592
16.35 0.00 96.92 63.31 97.41 94.84 4522 83.661 32 0.931 84.592
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 14.90 1.43 63.31 97.41 94.84 4523 83.678 32 0.931 84.609
0.00 14.90 2.65 63.31 97.41 94.84 4523 83.678 32 0.931 84.609
0.00 14.90 3.68 63.31 97.41 94.84 4523 83.678 32 0.931 84.609
上面发生了一次Young GC, S0从16.35%降到0%, S1从0%增长到14.90%, Eden从96.92%降到1.43, 耗时0.017s
以上参考Oracle Java 8 jstat手册:
jstat
jstatd
jstatd是一个 RMI的server,它可以监控 Hotspot的JVM的启动和结束,同时提供接口可以让远程机器连接到JVM。 比如 jstat / JVisualVM 都可以通过jstatd来远程观察JVM的运行情况。
在远程服务器上启动jstatd: nohup jstatd -J-Djava.security.policy=/home/xxx/jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.0.2 -p 1099 &
, 1099是jstatd的默认端口
jstatd.all.policy内容如下:grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
jstack
查看jvm进程的线程状态, 也可以做线程的dump,jstack pid
: 查看当前所有线程的运行栈, 包括线程当前状态(blocked, waitting), 线程占用了哪个对象锁, 线程在等待哪个对象锁;
$ jstack 18303 |more |
jmap
查看堆内存的情况, 也可以生成堆内存的dump信息,
jmap dump会触发Full GC, 所以在生产环境要小心使用.
jmap -heap pid
: 打印Heap(新生代/老年代/永久代等等..)的size参数和实际占用jmap -histo pid
: 打印出每个类的对象数量, 以及占用内存。如果出现jvm堆占用率过高,可以用histo
查看哪个类的对象最多,猜测出哪里的代码有问题jmap -histo:live pid
: 只打印存活的jmap -dump:format=b,file=FileName 6900
: 把内存详细使用情况dump到文件(小心, 这个命令可能会暂停当前应用)-dump:[live,]format=b,file=FileName
: live指只有活动的对象被转储到dump文件
如果程序内存不足或者频繁GC,很有可能存在内存泄露情况:
- 可以先使用
jmap -heap
命令查看堆的使用情况,看一下各个堆空间的占用情况。- 使用
jmap -histo:[live]
查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,为什么没有被释放- 也可以使用
jmap -dump:format=b,file=<fileName>
命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容- 在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
图-使用jmap -histo pid
按类型统计存活类的个数:
jhat
查看jmap转储的二进制文件
jhat -port 5000 FileName
: 在本地启动http服务显示jmap生成的dump文件信息, 在http://localhost:5000 查看
总结:对Jvm进程进行堆栈Dump的方法
jstack可以生成Jvm线程的堆栈dump文件, jmap可以生成堆栈的dump文件,
让虚拟机在内存不足时自动生成dump文件:-XX:+HeapDumpOnOutOfMemoryError
图形化的dump生成工具: Java VisualVM
jcmd
1.7之后新增, 有多种功能的命令集合, 命令格式: jcmd $PID $Command
, 查看可用的Command: jcmd $PID help
, “Oracle官方建议使用jcmd代替jmap”
jcmd -l
: 类似jps -m
jcmd pid Thread.print
: 打印当前堆栈jcmd pid GC.heap_dump /tmp/dumpFile
: 导出dump文件jcmd pid VM.system_properties
: 打印出该进程所有-D参数
jinfo
jinfo pid
, 获取jvm进程的所有参数, 后续版本可能会移除这个工具
jdb
- 被调试的java进程启动参数
-Xdebug -Xrunjdwp:transport=dt_socket,address=8787
- 连接到上面的进程进行debug:
jdb -attach 192.168.1.79:8787 -sourcepath .
JConsole
Java 5提供的JConsole
JProfiler
JProfiler是由ej-technologies GmbH开发的商业授权的性能分析工具.
参考: 深入浅出JProfiler-博客-云栖社区-阿里云 @ref
VisualVM
VisualVM 是一个性能分析工具,自从 JDK 6 Update 7 以后已经作为 Oracle JDK 的一部分,位于 JDK 根目录的 bin 文件夹下。
以下参考自: 使用 VisualVM 进行性能分析及调优 @ref
使用VisualVM需要远程服务器上运行一个jstatd守护进程, 或者远程服务器上运行的Java Application启用了JMX, 应用程序添加如下参数来启动JMX:
-Dcom.sun.management.jmxremote=true |
JVisualVM连接到JVM线程使用了Attach API, 在本文档搜索Attach API
.
启动命令: jvisualvm
Btrace
BTrace是SUN Kenai云计算开发平台下的一个开源项目,旨在为java提供安全可靠的动态跟踪分析工具。
Btrace能用来做什么?
举例, 如果要对线上运行的Java程序进行调试, 可以通过在代码里加入debug打印信息来实现, 但缺点也很明显, 需要不断地修改代码,加入System.out.println()
, 还需要不断重启应用程序. 对于线上服务这是不可接受的.
Btrace可以改变上面低效的调试方式, Btrace可以使用类似AOP式的代码植入, 在我们关心的代码位置插入自定义代码, 比如:
在每个方法结束都打印耗时, 统计最耗时的方法;
在ArrayList.add
里加入代码, 如果size过大则打印log, 找出超大的ArrayList;
当System.gc()
被调用时, 打印出调用堆栈, 找出是哪里在调用gc;
并且最重要的是, 使用Btrace不需要重新编译项目代码, 也不需要重启进程, 所以Btrace非常适合在线上发生异常的环境上进行调试埋点.
Btrace的使用
Btrece的使用:
- 启动Java程序
- 编写Btrace代码, 用注解指定要切入的类和方法
- 用btracec编译上面的代码
- 用btrace命令把 agent.jar attach到运行中的Java进程, agent.jar启动端口, Btrece Client 使用这个端口发送命令和.class字节码
更多BTrace使用例子:
Btrace用到的技术介绍
Btrace 使用 Java Complier API 编译切入代码, 生成.class文件;
再使用 Attach API 把 agent.jar 附加到目标JVM上,agent 启动端口,接受来自 Client的指令和class字节码,
当agent接收到监控命令后,主要有以下两部分工作:
- 重写类:遍历当前所有的class,根据正则找到匹配的类,使用asm重写被切入类的字节码(CGLIB代理也用到了asm)
- 替换类:使用替换掉原来的class,使用了 Instrumentation API ,instrumentation的retransformClasses方法将原始字节码替换掉
Attach API 和 Instrumentation API 都是JVM Tool Interface (JVMTI)里提供的工具类;
有关JVMTI相关的链接:http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html
https://github.com/jon-bell/bytecode-examples
参考:
- BTrace原理浅析-阿里云开发者社区
- 深入 Java 调试体系
- 第 1 部分,JPDA 体系概览
- 第 2 部分: JVMTI 和 Agent 实现
- 第 3 部分: JDWP 协议及实现
- 第 4 部分: Java 调试接口(JDI)
HouseMD
比BTrace更轻量级的Java进程运行时的诊断调式命令行工具,可以用来跟踪跟踪方法的耗时。
Memory Analyzer (MAT)
Java Heap Dump 文件(通过
jmap -dump
转储的文件)分析工具,可以分析堆内存中每种对象的数量,还可以跟踪对象的引用链,排查内存泄漏问题。