引言
jdk提供了很多强大的工具有命令行也有可视化的,目的是可以让开发者去查看JVM信息以及监控JVM相关的内容
为了在实际工作中能够排查和解决JVM在生产环境发生的问题,所以有必要去学习了解这些优化工具
命令工具
javap
java反编译工具,主要用于根据java字节码文件反汇编为java源代码文件
用法:javap
| 指令 | 描述 |
|---|---|
| javap -help --help -? | 输出此用法消息 |
| javap -version | 版本信息 |
| javap -v -verbose | 输出附加信息 |
| javap -l | 17.09 |
| javap -public | 仅显示公共类和成员 |
| javap -protected | 显示受保护/公共的类和成员 |
| javap -package | 显示程序包/受保护/公共类和成员 |
| javap -p -private | 显示所有类和成员 |
| javap -c | 对代码进行反汇编 |
| javap -s | 输出内部类型签名 |
| javap -sysinfo | 显示正在处理的类的系统信息(路径,大小,日期,MD5散列) |
| javap -constants | 显示静态最终常量 |
| Javap -classpath | 指定查找用户类文件的位置 |
| javap -bootclasspath | 覆盖引导类文件的位置 |
这个命令是所有的java版本都支持的,包括一些版本号以及输出类的相关信息显示类的成员变量
当然我们最常用的还是javap -v -verbose命令因为它能够看到所有的内容比如说方法定义、字节码
jps
jps是用来查看当前java进程pid的
用法:jps
| 指令 | 描述 |
|---|---|
| jps -q | 仅输出VM标识符,不包括classname,jar,name,arguments in main method |
| jps -m | 输出main method的参数 |
| jps -l | 输出完全的包名,应用主类名,jar的完全路径名 |
| jsp -v | 输出jvm参数 |
| jsp -V | 输出通过flag文件传递到jvm中的参数(.hostpotrc文件或-XX:Flags=所指定的文件) |
| jps -Joption | 传递参数到vm,例如:-J-Xms512m |
jstat
jstat是平时在监控虚拟机时很常用的一个工具, 特别是在做jvm性能优化时对开发者起到很大的辅助作用
它可以输出GC的信息对GC时间做一些统计,比如说垃圾回收进行了多少次GC耗时是多少
包括说整个JVM里面加载了一些class的信息以及说每个class对应的实例数量和所占用的空间
用法:jstat [generalOption | outputOptions vmid [interval [s | ms] [count]]]
-t参数可以在输出信息前面加一个timestamp列显示程序的运行时间
-h参数可以在周期性的数据输出时,输出多少行数据后,跟着输出一个表头信息
interval指定输出统计周期,count指定输出多少次数据
| 指令 | 描述 |
|---|---|
| jstat -gc pid | 显示gc的信息,查看gc的次数及时间 |
| jstat -gccapacity pid | 内存GC分区中各对象的使用和占用大小 |
| jstat -gcutil pid | 统计gc信息 |
| jstat -gcnew pid | 年轻代对象信息 |
| jstat -gcnewcapacity pid | 年轻代对象的信息及其占用量 |
| jstat -gcold pid | old代对象的信息 |
| jstat -gcoldcapacity pid | 老年代对象的信息及其占用量 |
| jstat -gcmetacapacity pid | 元数据区内存分析 |
| jstat -class pid | 显示加载class的数量,及所占用空间等信息 |
| jstat -compiler pid | 显示VM实时编译的数据等信息 |
| jstat -printcompilation pid | 当前VM执行的信息 |
-class 类加载统计
| 参数 | 含义 |
|---|---|
| Loaded | 加载类的数量 |
| Bytes | 加载类的size,单位为Byte |
| Unloaded | 卸载类的数目 |
| Time | 加载与卸载类花费的时间 |
-compiler 编译统计
| 参数 | 含义 |
|---|---|
| Compiled | 编译任务执行数量 |
| Failed | 编译任务执行失败数量 |
| Invalid | 编译任务执行失效数量 |
| Time | 编译任务消耗时间 |
| FailedType | 最后一个编译失败任务的类型 |
| FailedMethod | 最后一个编译失败任务所在的类及方法 |
-gc 垃圾回收统计
| 参数名 | 参数含义 |
|---|---|
| S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
| S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
| S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
| S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
| EC | 年轻代中Eden(伊甸园)的容量 (字节) |
| EU | 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
| OC | Old代的容量 (字节) |
| OU | Old代目前已使用空间 (字节) |
| MC | metaspace(元空间)的容量 (字节) |
| MU | metaspace(元空间)目前已使用空间 (字节) |
| CCSC | 当前压缩类空间的容量 (字节) |
| CCSU | 当前压缩类空间目前已使用空间 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
-gccapacity 堆内存统计
| 参数 | 含义 |
|---|---|
| NGCMN | 年轻代(young)中初始化(最小)的大小(字节) |
| NGCMX | 年轻代(young)的最大容量 (字节) |
| NGC | 年轻代(young)中当前的容量 (字节) |
| S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
| S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
| EC | 年轻代中Eden(伊甸园)的容量 (字节) |
| OGCMN | old代中初始化(最小)的大小 (字节) |
| OGCMX | old代的最大容量(字节) |
| OGC | old代当前新生成的容量 (字节) |
| OC | Old代的容量 (字节) |
| MCMN | metaspace(元空间)中初始化(最小)的大小 (字节) |
| MCMX | metaspace(元空间)的最大容量 (字节) |
| MC | metaspace(元空间)当前新生成的容量 (字节) |
| CCSMN | 最小压缩类空间大小 |
| CCSMX | 最大压缩类空间大小 |
| CCSC | 当前压缩类空间大小 |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
-gcmetacapacity 元数据空间统计
| 参数 | 含义 |
|---|---|
| MCMN | 最小元数据容量 |
| MCMX | 最大元数据容量 |
| MC | 当前元数据空间大小 |
| CCSMN | 最小压缩类空间大小 |
| CCSMX | 最大压缩类空间大小 |
| CCSC | 当前压缩类空间大小 |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
-gcnew 新生代垃圾回收统计
| 参数 | 含义 |
|---|---|
| S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
| S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
| S0U | 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) |
| S1U | 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) |
| TT | 持有次数限制 |
| MTT | 最大持有次数限制 |
| DSS | 期望的幸存区大小 |
| EC | 年轻代中Eden(伊甸园)的容量 (字节) |
| EU | 年轻代中Eden(伊甸园)目前已使用空间 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
-gcnewcapacity 新生代内存统计
| 参数 | 含义 |
|---|---|
| NGCMN | 年轻代(young)中初始化(最小)的大小(字节) |
| NGCMX | 年轻代(young)的最大容量 (字节) |
| NGC | 年轻代(young)中当前的容量 (字节) |
| S0CMX | 年轻代中第一个survivor(幸存区)的最大容量 (字节) |
| S0C | 年轻代中第一个survivor(幸存区)的容量 (字节) |
| S1CMX | 年轻代中第二个survivor(幸存区)的最大容量 (字节) |
| S1C | 年轻代中第二个survivor(幸存区)的容量 (字节) |
| ECMX | 年轻代中Eden(伊甸园)的最大容量 (字节) |
| EC | 年轻代中Eden(伊甸园)的容量 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
-gcold 老年代垃圾回收统计
| 参数 | 含义 |
|---|---|
| MC | metaspace(元空间)的容量 (字节) |
| MU | metaspace(元空间)目前已使用空间 (字节) |
| CCSC | 压缩类空间大小 |
| CCSU | 压缩类空间使用大小 |
| OC | Old代的容量 (字节) |
| OU | Old代目前已使用空间 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
-gcoldcapacity 老年代内存统计
| 参数 | 含义 |
|---|---|
| OGCMN | old代中初始化(最小)的大小 (字节) |
| OGCMX | old代的最大容量(字节) |
| OGC | old代当前新生成的容量 (字节) |
| OC | Old代的容量 (字节) |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
-gcutil 垃圾回收统计
| 参数 | 含义 |
|---|---|
| S0 | 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 |
| S1 | 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 |
| E | 年轻代中Eden(伊甸园)已使用的占当前容量百分比 |
| O | old代已使用的占当前容量百分比 |
| M | 元数据区已使用的占当前容量百分比 |
| CCS | 压缩类空间已使用的占当前容量百分比 |
| YGC | 从应用程序启动到采样时年轻代中gc次数 |
| YGCT | 从应用程序启动到采样时年轻代中gc所用时间(s) |
| FGC | 从应用程序启动到采样时old代(全gc)gc次数 |
| FGCT | 从应用程序启动到采样时old代(全gc)gc所用时间(s) |
| GCT | 从应用程序启动到采样时gc用的总时间(s) |
监控输出信息
-h代表 每隔多少行输出一次表头,1000代表每隔多少毫秒输出一次监控内容
jcmd
jcmd可以替代jps工具,对于查看本地jvm信息两者之间更多的是使用jcmd,因为jcmd可以让运行中的jvm执行相关指令
用法:jcmd <pid | main class> <command...|PerfCounter.print|-f file>
| 指令 | 描述 |
|---|---|
| jcmd | 查看本地的java进程,获取其pid |
| jcmd pid help | 查看其支持的命令列表 |
| jcmd pid Thread.print -l | 打印线程栈 |
| jcmd pid VM.command_line | 打印启动命令及参数 |
| jcmd pid GC.heap_dump /data/filename.dump | 查看JVM的Heap Dump |
| jcmd pid GC.class_histogram | 查看类的统计信息 |
| jcmd pid GC.system_properties | 查看系统属性内容 |
| jcmd pid VM.uptime | 查看虚拟机启动时间 |
| jcmd pid PerfCounter.print | 查看性能统计 |
jinfo
jinfo可以查看运行中的jvm的全部参数,还可以设置部分参数.可以去看本地运行中的jvm也可以去看远程的
用法:jinfo [option] pid、jinfo [option] executable core、jinfo [option] [server-id@] remote-hostname-or-IP
| 参数 | 说明 |
|---|---|
| pid | 对应jvm的进程id |
| executable core | 产生core dump文件 |
| [server-id@] remote-hostname-or-IP | server-id标记服务唯一ID,远程的ip或者hostname |
| 指令 | 描述 | |
|---|---|---|
| no option | 输出全部的参数和系统属性 | |
| -flag name | 输出对应名称的参数 | |
| -flag [+ | -]name | 开启或关闭对应名称的参数 |
| -flag name=value | 设定对应名称的参数 | |
| -flags | 输出全部的参数 | |
| -sysprops | 输出系统属性 |
jmp
打印出java进程内存中对象的情况,或者将VM中的堆以二进制输出成文本,这是我们在做内存溢出和泄露排查时经常用到的一个命令
用法:jmap [option] pid 、jmap [option] executable core、jmap [option] [server-id@] remote-hostname-or-IP
| 指令 | 描述 | |
|---|---|---|
| no option | 打印目标jvm加载的每个共享对象的起始地址。映射大小及共享对象文件路径 | |
| -dump:[live] format=b,file= | 使用hprof二进制输出heap内容到文件live代表只输出存活对象 | |
| -flag [+ | -]name | 打印正等候回收的对象信息 |
| -heap | 打印heap概要信息,GC使用算法,heap的配置及wise heap的使用情况 | |
| -histo:[] | 打印每个class的内存信息VM内部类会加上前缀“*”live代表只输出存活对象 | |
| -clstats | 打印classload详细信息和jvm heap方法区的信息 | |
| -F | 强迫在pid没有响应-dump或-histo的时候;这个模式下不支持live参数 | |
| -h | help | 打印辅助信息 |
| -J | 传递参数给jmap启动的jvm |
生成内存快照文件dump.bin
jhat
java Head Analyse Tool分析java堆的命令,它的分析不是说把里面的内容给我们查出来,而是说将堆中的对象以html的形式显示出来,支持对象查询语言OQL
它是一个网页的服务可以根据需要定制化查看内容
jhat [-stack ] [-refs ] [-prot ] [-baseline ] [-debug ] [-version] [-h|-help]
| 指令 | 描述 | |
|---|---|---|
| -J | 启动JVM时传入的启动参数,如-J-Xmx512m则指定jhat最大堆内存为512m | |
| -stack false | true | 关闭对象分配调用栈跟踪.默认为true |
| -refs false | true | 关闭对象引用跟踪.默认为true |
| -port port-number | 设置jhat HTTP server的端口号 默认7000 | |
| -exclude exclude-file | 指定文件对象查询时需要排除的数据成员列表 | |
| -baseline exclude-file | 指定一个基准堆转储,在比较两个不同的堆转储时很有用 | |
| -debug int | 设置debug级别 0表示不输出调试信息 值越大则调试信息更详细 | |
| -version | 启动后只显示版本信息就退出 |
jstack
堆栈跟踪工具jstack用于打印出给定的java进程ID或core file或远程调试服务的java堆栈信息,如果是在64位机器上需要指定选项-J-d64
一般会在分析cpu100%和死锁的时候用到,查看当前jvm正在运行哪些线程这些线程的栈信息是什么情况
用法:jstack [option] pid、jstack [option] executable core、jstack [option] [server-id@] remote-hostname-or-IP
| 参数 | 说明 |
|---|---|
| pid | 对应jvm的进程id |
| executable core | 产生core dump文件 |
| [server-id@] remote-hostname-or-IP | server-id标记服务唯一ID,远程的ip或者hostname |
| 指令 | 描述 | |
|---|---|---|
| -F | 强制打印栈信息 | |
| -l | 长列表;打印关于锁的附加信息 | |
| -m | 打印java和native c/c++框架的所有栈信息 | |
| -h | -help | 打印帮助信息 |
在控制台中看jvm线程信息,有时候线程太多控制台不好看 可以输出到文件,以文件的形式进行观察
可视化工具
jconsole
这个可视化工具通常会在测试和性能调试的时候会用到,存放在jdk/bin 目录底下
概览中可以查看堆内存使用情况、线程活动情况、类加载卸载情况、CPU占用率
内存中可以查看堆、非堆、新生代、老年的、元数据、压缩类空间等内存池的使用情况
线程中可以查看线程的栈运行情况
类中可以看到类的加载情况
VM概要中查看当前JVM进程的一些概要信息:运行时参数、线程、内存、类加载
MBean中查看实现了Java管理扩展(jmx)的对象实例
jvisualvm
jvisualvm是java提供比较全面工具,它不仅可以监控运行时的JVM还可以分析dump内存快照
监视页面中查看CPU、堆、类、线程的活动情况
线程页面中查看线程的运行情况
点击线程dump可以查看每个线程栈的情况
抽样器页面可以实时查看每个线程占用CPU、内存情况
查看堆内存快照文件
可以通过类查看类的实例和引用类型,追踪到垃圾回收的根节点,只要能够访问到根节点就是可达,有可达线路的对象就不会被回收
总结
对于这两个可视化监控工具来说更多的是在测试环境调试问题的时候会用到,因为像这样的监控信息在生产环境中不仅仅只有一台可能是多台
那么对于生产环境而言这两个可视化工具就没有太大的用武之地了,生产环境中使用更多的是将每个实例的jvm信息收集起来形成统一监控报表
但是对于命令工具还是有必要去了解的,当生产环境中某台服务器出现CPU100%、内存泄露、死锁被监控工具检测出来以后就需要到对应的服务器上排查
当然业界也有很多优秀的线上监控诊断产品,比较典型的就是Arthas它可以通过全局视角实时查看应用load、内存、gc、线程的状态信息