JVM指标说明

本文介绍了JVM内存、GC、线程、类、文件句柄等指标的采集原理。

内存相关指标

ARMS探针中存在一个定时任务,通过定期调用JDK的接口 java.lang.management.ManagementFactory#getPlatformMXBeans(java.lang.Class<T>)并传入 java.lang.management.MemoryPoolMXBean.class参数获得JVM内存相关指标。

该方法会返回一个列表,列表中每一个对象反映了JVM中不同内存预期使用情况。下面的代码片段简单地演示了如何使用该方法。

public static void printCurrentJVMMemoryUsage() {
    List<MemoryPoolMXBean> beans = ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class);
    for (MemoryPoolMXBean bean : beans) {
        System.out.printf("area=%s\tname=%s\tinitial=%d\tmax=%d\tcommited=%d\tuse=%d\n", bean.getType().name(),
                bean.getName(), bean.getUsage().getInit(), bean.getUsage().getMax(), bean.getUsage().getCommitted(), bean.getUsage().getUsed());
    }
}

该代码片段在不同JVM版本中的输出有所不同,一个可能的输出结果如下所示,包含了每一个内存区域的类型,名称以及初始大小、最大大小、已提交量和已使用量。

area=NON_HEAP	name=Code Cache	initial=2555904	max=134217728	commited=3211264	use=3139712
area=NON_HEAP	name=Metaspace	initial=0	max=-1	commited=8388608	use=7950584
area=NON_HEAP	name=Compressed Class Space	initial=0	max=1073741824	commited=1048576	use=919408
area=HEAP	name=PS Eden Space	initial=67108864	max=1409286144	commited=67108864	use=39057856
area=HEAP	name=PS Survivor Space	initial=11010048	max=11010048	commited=11010048	use=0
area=HEAP	name=PS Old Gen	initial=179306496	max=2863661056	commited=179306496	use=0

ARMS探针定期采集这些数据,采集到后不做任何额外加工直接上报并记录在下述指标中。

指标名

指标含义

arms_jvm_mem_init_bytes

内存初始大小

arms_jvm_mem_max_bytes

内存最大大小

arms_jvm_mem_committed_bytes

内存已提交大小

arms_jvm_mem_used_bytes

内存已使用大小

其中两个关键维度:

维度名称

维度含义

维度举例

area

内存区域类型

heap、nonheap

id

内存区域名称

eden、survivor、total

JVM各个内存区域说明请参见JVM监控内存详情说明

GC相关指标

ARMS探针中存在一个定时任务,通过定期调用JDK的接口 java.lang.management.ManagementFactory#getPlatformMXBeans(java.lang.Class<T>)并传入 java.lang.management.GarbageCollectorMXBean参数获得JVM GC相关指标。

该方法会返回一个列表,列表中每一个对象反映了JVM中不同垃圾收集器的运行情况。下面的代码片段简单地演示了如何使用该方法。

public static void printGC() {
    List<GarbageCollectorMXBean> beans = ManagementFactory.getPlatformMXBeans(GarbageCollectorMXBean.class);
    for (GarbageCollectorMXBean  bean : beans) {
        System.out.printf("name=%s\tgcCount=%d\tgcTime=%d\n", bean.getName(),
                bean.getCollectionCount(), bean.getCollectionTime());
    }
}

该代码片段在不同JVM版本中的输出有所不同,一个可能的输出结果如下所示,包含了每一个垃圾回收器的名称、GC次数以及GC耗时。

name=PS Scavenge	gcCount=0	gcTime=0
name=PS MarkSweep	gcCount=0	gcTime=0

需要注意的是,因为该数据记录的是JVM启动以来的累计值,ARMS探针在定期采集到这些数据后会减去上一次采集的值得到当前周期(默认15秒)内的GC次数和GC耗时,然后上报并记录在下述指标中。

指标名

指标含义

arms_jvm_gc_delta

当前周期GC次数

arms_jvm_gc_seconds_delta

当前周期GC耗时

考虑到实际的垃圾回收器数量较多,为降低用户理解成本, ARMS探针会将实际的垃圾回收器名称映射为YoungOld并记录在下述维度中。

维度名称

维度含义

维度举例

gen

GC类型

young、old

具体的映射关系如下:

垃圾回收器名称

Gen取值

Copy

young

G1 Young Gen

young

G1 Old Gen

old

G1 Young Generation

young

G1 Old Generation

old

ParNew

young

ConMarkSweep

old

ConcurrentMarkSweep

old

PS Scavenge

young

PS MarkSweep

old

PS Serial

young

MarkSweepCompact

old

由于在最新的ZGC中,整体GC的设计有较大变动,不再适用之前简单的YoungGC、FullGC这种分类方式,ZGC单独映射如下:

垃圾回收器名称

Gen取值

ZGC Cycles

cycles

ZGC Pauses

pauses

ZGC Minor Cycles

young_cycles

ZGC Minor Pauses

young_pauses

ZGC Major Cycles

old_cycles

ZGC Major Pauses

old_pauses

需要注意,在ARMS控制台上会展示一个叫做线程栈使用的图表,如下图所示,该指标并非在ARMS探针侧采集,是通过当前存活线程数 × 1MB计算所得。JVM中默认会为每个线程分配1MB的空间用于存储栈帧中的局部变量表、操作数栈、动态链接和方法返回地址等信息。

image.png

线程相关指标

ARMS探针中存在一个定时任务,通过定期调用JDK的接口 java.lang.management.ManagementFactory#getThreadMXBean获得JVM线程相关指标。

该方法会返回一个类型为java.lang.management.ThreadMXBean的实例,该实例反映了当前JVM的线程情况。下面的代码片段简单地演示了如何使用该方法。

public static void printThreads() {
    ThreadMXBean threadMXBean = java.lang.management.ManagementFactory.getThreadMXBean();
    int threadCount = threadMXBean.getThreadCount();
    int daemonThreadCount = threadMXBean.getDaemonThreadCount();
    long totalStartedThreadCount = threadMXBean.getTotalStartedThreadCount();
    long terminatedThreadCount = totalStartedThreadCount - threadCount;
    System.out.printf("allStartedThreadCount=%d\tcurrentThreadCount=%d\tdaemonThreadCount=%d\tterminatedThreadCount=%d\n"
            , totalStartedThreadCount, threadCount, daemonThreadCount, terminatedThreadCount);
}

该代码片段在不同JVM版本中的输出有所不同,一个可能的输出结果如下所示,包含了当前JVM累计启动的线程数、存活的线程数、后台线程数以及累计的终结线程数。需要注意,除了终结线程数之外,其余几个线程数均是通过直接调用ThreadMXBean方法获得,累计的终结线程数是通过累计启动线程数减去当前存活线程数获得,代表了从JVM启动后,累计有多少线程完成执行任务而被终结回收。

allStartedThreadCount=5	currentThreadCount=5	daemonThreadCount=4	terminatedThreadCount=0

此外对于当前存活的线程数,会分别统计其处于RUNNABLE、BLOCKED、WAITING、TIMED_WAITING的线程数量。

ARMS探针会定期采集这些数据,采集后不做额外处理直接记录在下述指标中。

指标名

指标含义

arms_jvm_threads_count

内存初始大小

上文提到的不同线程通过下述关键维度区分。

维度名称

维度含义

维度举例

state

线程状态

  • live:当前存活线程数

  • daemon:守护线程数

  • terminated:终结线程数,仅 4.x 探针支持。

对于live线程,会分别统计下述不同状态的线程数:

  • new:新建状态

  • runnable:可运行状态

  • blocked:阻塞状态

  • wait:等待状态

  • timed-wait:有超时时间的的等待状态

类相关指标

ARMS探针中存在一个定时任务,通过定期调用JDK的接口 ManagementFactory.getClassLoadingMXBean()获得JVM类加载相关指标。

该方法会返回一个类型为java.lang.management.ClassLoadingMXBean的实例,该实例反映了当前JVM的线程情况。下面的代码片段简单地演示了如何使用该方法。

public static void printCurrentJVMMemoryUsage() {
    ClassLoadingMXBean classLoaderMXBean = ManagementFactory.getClassLoadingMXBean();
    long totalLoadedClassCount = classLoaderMXBean.getTotalLoadedClassCount();
    long unloadedClassCount = classLoaderMXBean.getUnloadedClassCount();
    System.out.printf("totalLoadedClassCount=%d\tunloadedClassCount=%d\n"
            , totalLoadedClassCount, unloadedClassCount);
}

该代码片段在不同JVM版本中的输出有所不同,一个可能的输出结果如下所示,包含了当前JVM累计加载的类数量和累计卸载的类数量。

totalLoadedClassCount=1355	unloadedClassCount=0

需要注意,因为该数据记录的是JVM启动以来的累计值,ARMS探针在定期采集到这些数据后会减去上一次采集的值得到当前周期(默认15秒)内的类加载数量和类卸载数量,然后上报并记录在下述指标中。

指标名

指标含义

arms_class_load_loaded

当前周期加载类数量

arms_class_load_un_loaded

当前周期卸载类数量

文件句柄相关指标

ARMS探针中存在一个定时任务,通过定期调用JDK的接口 ManagementFactory.getOperatingSystemMXBean()获得JVM类加载相关指标。

该方法会返回一个类型为java.lang.management.OperatingSystemMXBean的实例,该实例反映了当前JVM的文件句柄打开情况。下面的代码片段简单地演示了如何使用该方法。

public static void printFDUsage() {
    OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
    if (!(operatingSystemMXBean instanceof UnixOperatingSystemMXBean)) {
        return;
    }
    long openFDCount = ((UnixOperatingSystemMXBean) operatingSystemMXBean).getOpenFileDescriptorCount();
    long maxFDCount = ((UnixOperatingSystemMXBean) operatingSystemMXBean).getMaxFileDescriptorCount();
    System.out.printf("openFDCount=%d\tfdOpenRatio=%f\n"
            , openFDCount, (double) openFDCount / maxFDCount);
}

该代码片段在不同的JVM版本输出有所不同,一个可能的输出结果如下所示,结果中输出了当前JVM打开的文件句柄数和文件句柄打开率,文件句柄打开率是由当前JVM已经打开的文件句柄数除以当前JVM总的可打开文件句柄数得到。

openFDCount=184	fdOpenRatio=0.017969

ARMS 探针会定期采集这些数据,采集后不做额外处理直接记录在下述指标中。

指标名

指标含义

arms_file_desc_open_count

当前JVM打开的文件句柄数

arms_file_desc_open_ratio

当前周期卸载类数量