本文介绍了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探针会将实际的垃圾回收器名称映射为Young和Old并记录在下述维度中。
维度名称 | 维度含义 | 维度举例 |
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的空间用于存储栈帧中的局部变量表、操作数栈、动态链接和方法返回地址等信息。
线程相关指标
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线程,会分别统计下述不同状态的线程数:
|
类相关指标
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 | 当前周期卸载类数量 |