Java-性能分析&监控工具

时间:2023-01-10 19:55:06

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模块:
    Java-性能分析&监控工具
类名 描述
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
Java-性能分析&监控工具
连接远程程序添加配置:

 -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浏览器中查看类属性信息:
Java-性能分析&监控工具
内存栏目查看GC、各个内存区域使用情况:
Java-性能分析&监控工具
线程状态、死锁、堆栈信息:
Java-性能分析&监控工具
诊断命令,查询vm信息等:
Java-性能分析&监控工具

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。

概述

Java-性能分析&监控工具

内存

Java-性能分析&监控工具

线程

Java-性能分析&监控工具
可以看到线程列表、线程状态、线程名称、线程堆栈等信息。

Java-性能分析&监控工具
可以看到 已加装当前类、已加载类总数、已卸载类总数。

VM

Java-性能分析&监控工具

MXBean

Java-性能分析&监控工具

JVisualVM

VisualVM(All-in-One Java Troubleshooting Tool);功能最强大的运行监视和故障处理程序。

功能描述:

  • 显示虚拟机进程以及进程的配置环境信息(jps、jinfo)。
  • 监视应用程序的CPUGC方法区(1.7及以前),元空间(JDK1.8及以后)以及线程的信息(jstat、jstack)。
  • dump以及分析堆转储快照(jmap、jhat)。
  • 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
  • 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照

Java-性能分析&监控工具
Java-性能分析&监控工具
Java-性能分析&监控工具
Java-性能分析&监控工具
Java-性能分析&监控工具
Java-性能分析&监控工具

Java Management Extensions(JMX)