Java监控和管理
Java监控和管理API
Java Standard Edition(Java SE)平台提供的监控和管理技术 - JMX(Java Management Extensions) 技术。
Java SE 中包含了用于监控和管理的(java.lang.management)API,通过这些 API 可以实现应用程序的自我监控,此 API 主要提供了以下信息的访问:
- 类加载相关。
- JVM 相关,例如运行时间、系统环境变量、用户输入参数。
- 线程相关,例如线程状态,线程的统计信息、线程的堆栈等。
- 内存使用情况。
- GC 情况。
- 死锁检测。
- 操作系统信息。
Java 8的java.lang.management模块:
类名 | 描述 |
---|---|
ClassLoadingMXBean | 用于 Java 虚拟机的类加载系统的管理接口。 |
CompilationMXBean | 用于 Java 虚拟机的编译系统的管理接口。 |
GarbageCollectorMXBean | 用于 Java 虚拟机的垃圾回收的管理接口。 |
MemoryManagerMXBean | 内存管理器的管理接口。 |
MemoryMXBean | Java 虚拟机的内存系统的管理接口。 |
MemoryPoolMXBean | 内存池的管理接口。 |
OperatingSystemMXBean | 用于操作系统的管理接口,Java 虚拟机在此操作系统上运行。 |
RuntimeMXBean | Java 虚拟机的运行时系统的管理接口。 |
ThreadMXBean | Java 虚拟机线程系统的管理接口。 |
Java虚拟机的监控
上面说到 Java SE 中已经内置了开箱即用的监控和管理功能,通过这些功能可以实现程序的自我监测,Java 默认已经实现了对 Java 虚拟机相关信息的监测。
下面通过一个简单的示例,演示如何通过监控管理 API 获取系统信息、编译器信息、内存信息以及垃圾收集器信息。
public static void main(String[] args) {
showJvmInfo();
showMemoryInfo();
showSystem();
showClassLoading();
showCompilation();
showThread();
showGarbageCollector();
showMemoryManager();
showMemoryPool();
}
/**
* Java 虚拟机的运行时系统
*/
public static void showJvmInfo() {
RuntimeMXBean rtMxBean = ManagementFactory.getRuntimeMXBean();
System.out.println("Java 虚拟机的运行时系统(RuntimeMXBean):");
System.out.println("jvm vendor:" + rtMxBean.getVmVendor());
System.out.println("jvm name:" + rtMxBean.getVmName());
System.out.println("jvm version:" + rtMxBean.getVmVersion());
System.out.println("jvm bootClassPath:" + rtMxBean.getBootClassPath());
System.out.println("jvm start time:" + rtMxBean.getStartTime());
System.out.println("\n");
}
/**
* Java 虚拟机的内存系统
*/
public static void showMemoryInfo() {
MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memoryMxBean.getHeapMemoryUsage();
System.out.println("Java 虚拟机的内存系统(MemoryMXBean):");
System.out.println("Heap " + heap.toString());
System.out.println(
"Heap" +
" init:" + heap.getInit() + byte2Mb(heap.getInit()) +
" used:" + byte2Mb(heap.getUsed()) +
" committed:" + byte2Mb(heap.getCommitted()) +
" max:" + byte2Mb(heap.getMax()));
System.out.println("\n");
}
private static String byte2Mb(long source) {
return "(" + source / 1024 / 1024 + "mb)";
}
/**
* Java 虚拟机在其上运行的操作系统
*/
public static void showSystem() {
OperatingSystemMXBean operatingSystemMxBean = ManagementFactory.getOperatingSystemMXBean();
System.out.println("Java 虚拟机在其上运行的操作系统(OperatingSystemMXBean):");
System.out.println("Architecture(操作系统架构): " + operatingSystemMxBean.getArch());
System.out.println("Processors(Java虚拟机可用的处理器数): " + operatingSystemMxBean.getAvailableProcessors());
System.out.println("System name(操作系统名称): " + operatingSystemMxBean.getName());
System.out.println("System version(操作系统版本): " + operatingSystemMxBean.getVersion());
System.out.println("Last minute load(最后一分钟的系统负载平均值): " + operatingSystemMxBean.getSystemLoadAverage());
System.out.println("\n");
}
/**
* Java 虚拟机的类加载系统
*/
public static void showClassLoading() {
ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
System.out.println("Java 虚拟机的类加载系统(ClassLoadingMXBean):");
System.out.println("TotalLoadedClassCount(加载的类总数): " + classLoadingMxBean.getTotalLoadedClassCount());
System.out.println("LoadedClassCount(当前加载的类的数量)" + classLoadingMxBean.getLoadedClassCount());
System.out.println("UnloadedClassCount(卸载类的总数):" + classLoadingMxBean.getUnloadedClassCount());
System.out.println("\n");
}
/**
* Java 虚拟机的编译系统
*/
public static void showCompilation() {
CompilationMXBean compilationMxBean = ManagementFactory.getCompilationMXBean();
System.out.println("Java 虚拟机的编译系统(CompilationMXBean):");
System.out.println("TotalCompilationTime(编译时间(毫秒)):" + compilationMxBean.getTotalCompilationTime());
System.out.println("name(JIT编译器的名称):" + compilationMxBean.getName());
System.out.println("\n");
}
/**
* Java 虚拟机的线程系统
*/
public static void showThread() {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
System.out.println("Java 虚拟机的线程系统(ThreadMXBean):");
System.out.println("ThreadCount(当前活动线程数)" + threadMxBean.getThreadCount());
System.out.println("PeakThreadCount(峰值实时线程计数)" + threadMxBean.getPeakThreadCount());
System.out.println("TotalStartedThreadCount(启动的线程总数)" + threadMxBean.getTotalStartedThreadCount());
System.out.println("DaemonThreadCount(当前活动后台进程线程数)" + threadMxBean.getDaemonThreadCount());
System.out.println("isSynchronizerUsageSupported(虚拟机是否支持监视可下载同步器的使用情况)" + threadMxBean.isSynchronizerUsageSupported());
System.out.println("AllThreadIds(所有活动线程ID):" + JSON.toJSONString(threadMxBean.getAllThreadIds()));
System.out.println("CurrentThreadUserTime(当前线程在用户模式下执行的CPU时间(以纳秒为单位))" + threadMxBean.getCurrentThreadUserTime());
for (ThreadInfo threadInfo : threadMxBean.getThreadInfo(threadMxBean.getAllThreadIds(), 1)) {
System.out.print(threadInfo.getThreadId() + threadInfo.toString());
}
System.out.println("\n");
}
/**
* Java 虚拟机中的垃圾回收器。
*/
public static void showGarbageCollector() {
List<GarbageCollectorMXBean> collectorMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
System.out.println("Java 虚拟机中的垃圾回收器(GarbageCollectorMXBean):");
for (GarbageCollectorMXBean collectorMxBean : collectorMxBeans) {
System.out.println("name(垃圾收集器名称):" + collectorMxBean.getName());
System.out.println("--CollectionCount:" + collectorMxBean.getCollectionCount());
System.out.println("--CollectionTime" + collectorMxBean.getCollectionTime());
System.out.println("\n");
}
System.out.println("\n");
}
/**
* Java 虚拟机中的内存管理器
*/
public static void showMemoryManager() {
List<MemoryManagerMXBean> memoryManagerMxBeans = ManagementFactory.getMemoryManagerMXBeans();
System.out.println("Java 虚拟机中的内存管理器(MemoryManagerMXBean):");
for (MemoryManagerMXBean managerMxBean : memoryManagerMxBeans) {
System.out.println("name(内存管理器名称):" + managerMxBean.getName());
System.out.println("--MemoryPoolNames:" + String.join(",", managerMxBean.getMemoryPoolNames()));
System.out.println("\n");
}
System.out.println("\n");
}
/**
* Java 虚拟机中的内存池
*/
public static void showMemoryPool() {
List<MemoryPoolMXBean> memoryPoolMxBeans = ManagementFactory.getMemoryPoolMXBeans();
System.out.println("Java 虚拟机中的内存池(MemoryPoolMXBean):");
for (MemoryPoolMXBean memoryPoolMxBean : memoryPoolMxBeans) {
System.out.println("name:" + memoryPoolMxBean.getName());
System.out.println("--CollectionUsage:" + memoryPoolMxBean.getCollectionUsage());
System.out.println("--type:" + memoryPoolMxBean.getType());
System.out.println("\n");
}
System.out.println("\n");
}
}
输出:
Java 虚拟机的运行时系统(RuntimeMXBean):
jvm vendor:Oracle Corporation
jvm name:Java HotSpot(TM) 64-Bit Server VM
jvm version:25.221-b11
jvm bootClassPath:F:\opt\Java\jdk8\jre\lib\resources.jar;F:\opt\Java\jdk8\jre\lib\rt.jar;F:\opt\Java\jdk8\jre\lib\sunrsasign.jar;F:\opt\Java\jdk8\jre\lib\jsse.jar;F:\opt\Java\jdk8\jre\lib\jce.jar;F:\opt\Java\jdk8\jre\lib\charsets.jar;F:\opt\Java\jdk8\jre\lib\jfr.jar;F:\opt\Java\jdk8\jre\classes
jvm start time:1673181328952
Java 虚拟机的内存系统(MemoryMXBean):
Heap init = 268435456(262144K) used = 4028824(3934K) committed = 257425408(251392K) max = 3791650816(3702784K)
Heap init:268435456(256mb) used:(3mb) committed:(245mb) max:(3616mb)
Java 虚拟机在其上运行的操作系统(OperatingSystemMXBean):
Architecture(操作系统架构): amd64
Processors(Java虚拟机可用的处理器数): 8
System name(操作系统名称): Windows 10
System version(操作系统版本): 10.0
Last minute load(最后一分钟的系统负载平均值): -1.0
Java 虚拟机的类加载系统(ClassLoadingMXBean):
TotalLoadedClassCount(加载的类总数): 507
LoadedClassCount(当前加载的类的数量)507
UnloadedClassCount(卸载类的总数):0
Java 虚拟机的编译系统(CompilationMXBean):
TotalCompilationTime(编译时间(毫秒)):8
name(JIT编译器的名称):HotSpot 64-Bit Tiered Compilers
Java 虚拟机的线程系统(ThreadMXBean):
ThreadCount(当前活动线程数)5
PeakThreadCount(峰值实时线程计数)5
TotalStartedThreadCount(启动的线程总数)5
DaemonThreadCount(当前活动后台进程线程数)4
isSynchronizerUsageSupported(虚拟机是否支持监视可下载同步器的使用情况)true
AllThreadIds(所有活动线程ID):[5,4,3,2,1]
CurrentThreadUserTime(当前线程在用户模式下执行的CPU时间(以纳秒为单位))234375000
5"Attach Listener" Id=5 RUNNABLE
4"Signal Dispatcher" Id=4 RUNNABLE
3"Finalizer" Id=3 WAITING on java.lang.ref.ReferenceQueue$Lock@17c68925
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.ReferenceQueue$Lock@17c68925
2"Reference Handler" Id=2 WAITING on java.lang.ref.Reference$Lock@7e0ea639
at java.lang.Object.wait(Native Method)
- waiting on java.lang.ref.Reference$Lock@7e0ea639
1"main" Id=1 RUNNABLE
at sun.management.ThreadImpl.getThreadInfo1(Native Method)
Java 虚拟机中的垃圾回收器(GarbageCollectorMXBean):
name(垃圾收集器名称):PS Scavenge
--CollectionCount:0
--CollectionTime0
name(垃圾收集器名称):PS MarkSweep
--CollectionCount:0
--CollectionTime0
Java 虚拟机中的内存管理器(MemoryManagerMXBean):
name(内存管理器名称):CodeCacheManager
--MemoryPoolNames:Code Cache
name(内存管理器名称):Metaspace Manager
--MemoryPoolNames:Metaspace,Compressed Class Space
name(内存管理器名称):PS Scavenge
--MemoryPoolNames:PS Eden Space,PS Survivor Space
name(内存管理器名称):PS MarkSweep
--MemoryPoolNames:PS Eden Space,PS Survivor Space,PS Old Gen
Java 虚拟机中的内存池(MemoryPoolMXBean):
name:Code Cache
--CollectionUsage:null
--type:Non-heap memory
name:Metaspace
--CollectionUsage:null
--type:Non-heap memory
name:Compressed Class Space
--CollectionUsage:null
--type:Non-heap memory
name:PS Eden Space
--CollectionUsage:init = 67108864(65536K) used = 0(0K) committed = 0(0K) max = 1399848960(1367040K)
--type:Heap memory
name:PS Survivor Space
--CollectionUsage:init = 11010048(10752K) used = 0(0K) committed = 0(0K) max = 11010048(10752K)
--type:Heap memory
name:PS Old Gen
--CollectionUsage:init = 179306496(175104K) used = 0(0K) committed = 0(0K) max = 2843738112(2777088K)
--type:Heap memory
Disconnected from the target VM, address: '127.0.0.1:12687', transport: 'socket'
Process finished with exit code 0
Java监控和管理工具
JMX 技术中提到 JMX 不仅提供了监控和管理的 API ,还提供了用于网络远程管理的服务,可以使用 JMX 相关监控管理工具,通过网络远程连接到正在运行 Java 虚拟机,监控其运行状态。
JMC(Java Mission Control)
Java Mission Control使您能够监视和管理Java应用程序,而不会引入通常与这些类型的工具相关联的性能开销。
它使用为Java虚拟机(JVM)的常规自适应动态优化收集的数据。除了最小化性能开销之外,这种方法还消除了观察器效应的问题,当监视工具改变系统的执行特性时会发生这种问题。
Java Mission Control由客户端应用程序(JMC客户端)和在其上运行的许多插件组成:
-
JVM Browser显示正在运行的Java应用程序及其JVM。每个JVM实例都称为JVM连接。
-
JMX Console连接到正在运行的JVM,实时收集和显示其特征,并允许您通过Managed Beans(MBean)更改某些运行时属性。您还可以创建触发某些事件的规则(例如,如果应用程序的CPU使用率达到90%,则发送电子邮件)。
-
Java Flight Recorder(JFR)收集并保存详细的性能特征,以进行历史分析和分析。它可以用作独立的性能监视和分析工具,但是当用作JMC客户端的插件时,它会在逻辑分组的表,图表和拨号中显示诊断信息。它使您可以选择专注于问题所需的时间范围和详细程度。
-
Java Mission Control插件使用Java Management Extensions(JMX)代理连接到JVM。
启动JMC
双击JAVA_HOME\bin\jmc.exe 文件启动JMC
连接远程程序添加配置:
-Dcom.sun.management.jmxremote.port=7001
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=your ip
JMC功能介绍
MBean浏览器中查看类属性信息:
内存栏目查看GC、各个内存区域使用情况:
线程状态、死锁、堆栈信息:
诊断命令,查询vm信息等:
JFR(Java Flight Recorder)
黑匣子是用于记录飞机飞行和性能参数的仪器。在飞机出问题后,用于定位问题原因。JFR 就是 Java 的黑匣子。
JFR 是 Java Flight Record (Java飞行记录) 的缩写,是 JVM 内置的基于事件的JDK监控记录框架。这个起名就是参考了黑匣子对于飞机的作用,将Java进程比喻成飞机飞行。顾名思义,这个记录主要用于问题定位和持续监控。
如果是利用默认配置启动这个记录,性能非常高效,对于业务影响很小,因为这个框架本来就是用来长期在线上部署的框架。这个记录可以输出成二进制文件,用户可以指定最大记录时间,或者最大记录大小,供用户在需要的时候输出成文件进行事后分析。
JFR在Java应用运行时收集对应发生的事件,主要有三种类型的事件提供给JFR收集:
- 即时事件:一旦事件发生会立即进行数据记录
- 持续事件:如果持续时间超过指定阈值则进行数据记录
- 简单事件:用于记录应用所在系统的活跃指标(例如CPU,内存等)
开启JFR记录
通过启动参数配置并且启用 JFR,也可以通过启动参数在 JVM 进程启动的时候就启动 JFR,或者是利用 jcmd 工具,动态启用或者关闭 JFR。
JDK 8中的-XX:+FlightRecorder
java -XX:StartFlightRecording=disk=true,dumponexit=true,filename=recording.jfr,maxsize=1024m,maxage=1d,settings=profile,path-to-gc-roots=true test.Main
配置key | 默认值 | 说明 |
---|---|---|
delay | 0 | 延迟多久后启动 JFR 记录,支持带单位配置, 例如 delay=60s(秒), delay=20m(分钟), delay=1h(小时), delay=1d(天),不带单位就是秒, 0就是没有延迟直接开始记录。一般为了避免框架初始化等影响,我们会延迟 1 分钟开始记录(例如Spring cloud应用,可以看下日志中应用启动耗时,来决定下这个时间)。 |
disk | true | 是否写入磁盘,这个就是上文提到的, global buffer 满了之后,是直接丢弃还是写入磁盘文件。 |
dumponexit | false | 程序退出时,是否要dump出 .jfr文件 |
duration | 0 | JFR 记录持续时间,同样支持单位配置,不带单位就是秒,0代表不限制持续时间,一直记录。 |
filename | 启动目录/hotspot-pid-26732-id-1-2020_03_12_10_07_22.jfr,pid 后面就是 pid, id 后面是第几个 JFR 记录,可以启动多个 JFR 记录。最后就是时间。 | dump的输出文件 |
name | 无 | 记录名称,由于可以启动多个 JFR 记录,这个名称用于区分,否则只能看到一个记录 id,不好区分。 |
maxage | 0 | 这个参数只有在 disk 为 true 的情况下才有效。最大文件记录保存时间,就是 global buffer 满了需要刷入本地临时目录下保存,这些文件最多保留多久的。也可以通过单位配置,没有单位就是秒,默认是0,就是不限制 |
maxsize | 250MB | 这个参数只有在 disk 为 true 的情况下才有效。最大文件大小,支持单位配置, 不带单位是字节,m或者M代表MB,g或者G代表GB。设置为0代表不限制大小**。虽然官网说默认就是0,但是实际用的时候,不设置会有提示**: No limit specified, using maxsize=250MB as default. 注意,这个配置不能小于后面将会提到的 maxchunksize 这个参数。 |
path-to-gc-roots | false | 是否记录GC根节点到活动对象的路径,一般不打开这个,首先这个在我个人定位问题的时候,很难用到,只要你的编程习惯好。还有就是打开这个,性能损耗比较大,会导致FullGC一般是在怀疑有内存泄漏的时候热启动这种采集,并且通过产生对象堆栈无法定位的时候,动态打开即可。一般通过产生这个对象的堆栈就能定位,如果定位不到,怀疑有其他引用,例如 ThreadLocal 没有释放这样的,可以在 dump 的时候采集 gc roots |
settings | 默认是 default.jfc,这个位于 $JAVA_HOME/lib/jfr/default.jfc | 采集 Event 的详细配置,采集的每个 Event 都有自己的详细配置。另一个 JDK 自带的配置是 profile.jfc,位于 $JAVA_HOME/lib/jfr/profile.jfc 。这个配置文件里面的配置是怎么回事,我们后面会涉及。 |
感兴趣可以看看:
https://juejin.cn/post/6959405798556434440
https://zhuanlan.zhihu.com/p/87850586
JConsole
Jconsole (Java Monitoring and Management Console),一种基于JMX的可视化监视、管理工具。
JConsole 基本包括以下基本功能:概述、内存、线程、类、VM概要、MBean。
概述
内存
线程
可以看到线程列表、线程状态、线程名称、线程堆栈等信息。
类
可以看到 已加装当前类、已加载类总数、已卸载类总数。
VM
MXBean
JVisualVM
VisualVM(All-in-One Java Troubleshooting Tool);功能最强大的运行监视和故障处理程序。
功能描述:
- 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)。
- 监视应用程序的CPU、GC、堆、方法区(1.7及以前),元空间(JDK1.8及以后)以及线程的信息(jstat、jstack)。
- dump以及分析堆转储快照(jmap、jhat)。
- 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
- 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照