虽然阿里巴巴的 Arthas 这个工具很强大,不过如果你能够熟练使用 JDK 提供的工具,那么在不借助任何外力的情况下,就可以非常轻松的进行应用的性能监控和故障处理。
JDK 提供的工具,大家最熟悉的估计就是 java 和 javac 这两个了,然而 JDK 实际上提供了非常强大的性能监控和调试工具,今天松哥就来和大家一起捋一捋这些工具。
这个应该是大家最为熟悉的工具了,使用这个命令可以快速列出来系统上正在运行的 Java 进程。
这个命令在执行的时候一般有四个参数:
- -q 表示只输出进程 ID,不输出主类名称,准确来说,这里输出的叫做本地虚拟机进程 ID(LVMID),不过对于本地虚拟机进程来说,LVMID 和操作系统的进程 ID 是一致的。
- -l 表示输出主类的全名,如果进程执行的是 jar 包,那么就输出 jar 包的完整路径。
- -m 表示输出进程启动时传递给 main() 函数的参数。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 视频教程:https://doc.iocoder.cn/video/
jstat 是用来监控虚拟机各种运行状态信息的命令行工具,这个工具可以用来查看本地或者远程虚拟机内存、垃圾收集、即时编译等等信息。
一般来说,这些数据我们可以借助可视化工具来查看,例如 JMC 或者 VisualVM 等等,但是有时候在服务器环境下,在命令行环境下没法运行 GUI 图形界面,那么此时 jstat 是一个不错的选择。
jstat 运行参数比较多,松哥这里列举几个比较常见的参数:
- -class 这个表示监视类加载、卸载数量、总空间以及类装载所耗费的时间。
- -gc 这个表示监视 Java 堆状况,包括 Eden 区,两个 Survivor 区、老年代以及永久带的容量,已使用的空间以及垃圾收集时间合计等等信息。
命令中的 1s 8
表示每秒打印一次,一共打印 8 次,如果不设置,则打印一次。
这里输出的各参数含义如下:
❝
S0C:Survivor0 的容量。S1C:Survivor1 的容量。S0U:Survivor0 已使用大小。S1U:Survivor1 已使用大小。EC:Eden 区容量。EU:Eden 区已使用大小。OC:老年代容量。OU:老年代已使用大小。MC:元数据区容量。MU:元数据区已使用大小。CCSC:压缩类空间容量。CCSU:压缩类空间已使用大小。YGC:年轻代垃圾回收次数。YGCT:年轻代垃圾回收总耗时。FGC:FullGC 次数。FGCT:FullGC 总耗时。CGC:GC STW 的次数。CGCT:GC STW 的时间。GCT:总 GC 时间。
基本上我们想知道的关于 JVM 运行内存状态的信息,这里都有了。
- -gccapacity 这个监控内容与 -gc 类似,不过输出内容主要关注 Java 堆各个区域使用到的最大最小空间。
这里涉及到的参数我也和大家稍微解释下:
❝
NGCMN:新生代最小容量。NGCMX:新生代最大容量。NGC:新生代当前容量。OGCMN:老年代最小容量。OGCMX:老年代最大容量。OGC:老年代当前容量。MCMN:元空间最小容量。MCMX:元空间最大容量。CCSMN:压缩类空间最小容量。CCSMX:压缩类空间最大容量。CCSC:压缩类空间当前容量。
补充输出内容和前面一致的我就不重复解释了。
- -gcutil 这个和 -gc 类似,不过输出的内容主要关注已使用的空间占总空间的比例。
❝
S0:Survivor0 区使用比例。S1:Survivor1 区使用比例。E:Eden 区使用比例。O:老年代使用比例。M:元空间使用比例。CCS:压缩类空间使用比例。
这块能加的参数实际上非常多,对于我们来说比较常用的主要就是上面这几个命令。
如果小伙伴们想要查看完整指令,可以参考官方文档:https://docs.oracle.com/en/java/javase/11/tools/jstat.html
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
- 项目地址:https://github.com/YunaiV/yudao-cloud
- 视频教程:https://doc.iocoder.cn/video/
jinfo 命令可以实时查看和修改 JVM 各项参数。
能修改这就很牛了。
平时我们启动一个 Java 项目,一旦启动之后,如果想要知道项目启动时的各项 JVM 参数,那么就只能去查文档了。但是如果你能够熟练使用 jinfo,那么这个事情就容易很多了。
直接 jinfo pid
就会展示出来所有的 JVM 参数。
如果只想查看某一个 JVM 参数,那么可以使用如下命令:
jinfo -flag CICompilerCount 28893
也可以在不重启 JVM 参数的情况下,动态修改 JVM 参数。
例如松哥这里的案例,原本是没有开启 PrintClassHistogram 的,我现在动态开启:
PrintClassHistogram 前面没有符号,表示查看当前状态;PrintClassHistogram 前面有个 +
表示开启;PrintClassHistogram 前面有个 -
表示关闭。
这种方式是修改 value 为 boolean 的 JVM 参数,如果是 key-value 格式的 JVM 参数,那么可以按照下面这种方式修改:
当然这个修改并非所有的 JVM 参数都能改,至于哪些 JVM 参数能改,我们可以执行 java -XX:+PrintFlagsInitial
命令,找到标记为 manageable 的参数,这些参数是可以动态修改的。
另外我们在代码中通过 System.getProperties()
获取到的属性,也可以通过指令打印出来:
jinfo -sysprops 57002
jmap 命令一般可以用来生成 heapdump。
这个命令在新版本 JDK 中运行的时候,前面要加上 jhsdb。
从这里我们可以解读出哪些信息呢?
- 可以看到最大可分配的 Heap 大小(MaxHeapSize)
- 可以看到 JVM 启动时分配的新生代内存(NewSize)
- 可以看到可分配的最大新生代内存(MaxNewSize)
- 可以看到 JVM 启动时分配的老年代大小(OldSize)
- 可以看到老年代和新生代的内存比例(NewRatio),上面截图中表示
老/新=2
,即老年代占 2/3,新生代占 1/3。 - 可以看到新生代中 Eden 区和一个 Survivor 区的比例(SurvivoRatio)
- 可以看到初始的 Metaspace 大小(MetaspaceSize)
- 可以看到最大的 Metaspace 大小(MaxMetaspaceSize)
- 可以看到在 G1 收集器下,每个 Region 的大小(G1HeapRegionSize)
下面的这些就好理解了,分别是不同区域的 regions 数量、总空间、使用空间以及空闲空间。
也可以利用 jmap 查看 Heap 中对象的统计信息,比如对象的数量、占用内存的大小以及类的完整路径。
jmap -histo:live pid
加上 :live
表示查看活着的对象信息。
也可以查看 Heap 中元空间的类加载信息 jmap -clstats 28893
。
这里可以看到有哪些类加载器,分别加载了哪些类。
当然,最重要的功能是生成 dump 文件了:
这里几个参数说明一下:
- live 表示只转存堆中活动对象,如果没有制定,则存所有对象。
- format=b 表示以 hprof 二进制格式转存。
生成的文件,可以使用 idea 打开查看。
❝
小技巧:获取 heapdump 还有哪些方式?
- 使用 kill -3 吓一吓 JVM,就能拿到堆转储快照。
- -XX:HeapDumpOnOutOfMemoryError 参数可以让虚拟机在内存溢出时自动生成堆转储快照。
这个工具目前已经废弃了。
松哥这里就简单说下这个工具的作用以及替代品。
前面 jmap 会生成 heapdump 文件,这是一个二进制文件,那么我们通过什么工具去分析这个文件呢?比如可以使用 Eclipse 的 MAT,当然现在 IDEA 也可以直接打开这个文件分析,这块成熟的工具还是非常多的。
那么 jhat 其实也是一个这样的工具,jhat 内置了一个微型的 Web 服务器,让开发者可以在浏览器中查看相关数据。但是因为 jhat 功能比较简陋,所以目前已经废弃了。
jstack 可以查看 JVM 当前时刻的线程快照。
如果某个线程执行比较耗时,或者发生死锁,发生死循环等,那么就可以通过这个指令来查看各个线程的调用堆栈,检查到底发生了什么问题。
-l
表示出了堆栈之外,也把锁的相关信息显示出来。
不过这个命令松哥并不常用。因为 Thread 中有一个 getAllStackTraces 方法,我们通过 jstack 命令获取到的信息基本上通过这个方法都能获取到,所以可以自己结合这个方法写监控页面查看相关信息。