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表示每三行打印一次列名称, 采样间隔5s
jstat -gc -h3 14542 5s |
每列说明如下:
- 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表示每三行打印一次列名称, 采样间隔250ms
jstat -gcutil -h3 14542 3s |
上面发生了一次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" { |
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
转储的文件)分析工具,可以分析堆内存中每种对象的数量,还可以跟踪对象的引用链,排查内存泄漏问题。