作为一个java后端开发,关注线上java程序的运行指标数据是非常重要的。我们会对运行在jvm上的程序的cpu、内存、GC等数据进行实时采集和监控。
而JDK提供了一些JVM检测的API,这就是有名的java.lang.management 包,包里提供了许多MXBean的接口类,可以很方便的获取到JVM的内存、GC、线程、锁、class、甚至操作系统层面的各种信息。
简介
因为平时对堆内存比较关注,这边就简单的介绍下如何使用MXBean采集jvm的内存信息。
与内存相关的MXBean有:
MemoryMXBean | Java虚拟机内存系统的管理接口。 |
MemoryManagerMXBean | 内存管理器的管理接口。 |
MemoryPoolMXBean | 内存池的管理接口。 |
关于其他MXBean的信息可查看Jdk文档:jdk文档 - java.lang.management包
MemoryMXBean
这个MXBean主要用来获取堆与非堆内存的使用情况。
常用方法:
- getHeapMemoryUsage() :返回用于对象分配的堆的当前内存使用量
- getNonHeapMemoryUsage() :返回 Java 虚拟机使用的非堆内存的当前内存使用量
调用代码示例:
// 获取MemoryMXBean
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
// 打印:堆内存使用
logger.info("heapMemoryUsage: {}", memoryMXBean.getHeapMemoryUsage().toString());
// 打印:非堆内存使用
logger.info("nonHeapMemoryUsage: {}", memoryMXBean.getNonHeapMemoryUsage().toString());
输出结果:
heapMemoryUsage: init = 268435456(262144K) used = 97256440(94976K) committed = 324534272(316928K) max = 3817865216(3728384K)
nonHeapMemoryUsage: init = 2555904(2496K) used = 38960968(38047K) committed = 39845888(38912K) max = -1(-1K)
说明:
这里 getHeapMemoryUsage() 和 getNonHeapMemoryUsage() 以及后面调用其他获取内存使用的方法返回的都是 MemoryUsage 对象。
MemoryUsage类有四个值(均以字节为单位):
- Init:java虚拟机在启动的时候向操作系统请求的初始内存容量,java虚拟机在运行的过程中可能向操作系统请求更多的内存或将内存释放给操作系统,所以init的值是不确定的。
- Used:当前已经使用的内存量。
- Committed:表示保证java虚拟机能使用的内存量,已提交的内存量可以随时间而变化(增加或减少)。Java 虚拟机可能会将内存释放给系统,committed 可以小于 init。committed 将始终大于或等于 used。
- Max:表示可以用于内存管理的最大内存量(以字节为单位)。可以不定义其值。如果定义了该值,最大内存量可能随时间而更改。已使用的内存量和已提 交的内存量将始终小于或等于 max(如果定义了 max)。如果内存分配试图增加满足以下条件的已使用内存将会失败:used > committed,即使 used <= max 仍然为 true(例如,当系统的虚拟内存不足时)。
MemoryManagerMXBean
MemoryManagerMXBean是内存管理器的管理接口。内存管理器管理 Java 虚拟机的一个或多个内存池。Java 虚拟机具有一个或多个内存管理器。
常用方法:
- getMemoryPoolNames() :返回此内存管理器管理的内存池名称
- getName() :返回表示此内存管理器的名称
调用代码示例:
// 获取所有内存管理器MXBean列表,并遍历
List<MemoryManagerMXBean> memoryManagerMXBeans = ManagementFactory.getMemoryManagerMXBeans();
for (MemoryManagerMXBean memoryManagerMXBean : memoryManagerMXBeans) {
// 获取管理器名称
String name = memoryManagerMXBean.getName();
// 获取此内存管理器管理的内存池名称
String[] memoryPoolNames = memoryManagerMXBean.getMemoryPoolNames();
logger.info("memoryManagerName: {}, memoryPoolNames: {}", name, memoryPoolNames);
}
输出结果:
memoryManagerName: CodeCacheManager, memoryPoolNames: [Code Cache]
memoryManagerName: Metaspace Manager,memoryPoolNames: [Metaspace, Compressed Class Space]
memoryManagerName: PS Scavenge, memoryPoolNames: [PS Eden Space, PS Survivor Space]
memoryManagerName: PS MarkSweep, memoryPoolNames: [PS Eden Space, PS Survivor Space, PS Old Gen]
说明:
可以看到测试jvm应用主要有4个内存管理器,分别是:
- CodeCacheManager:管理代码缓存
- Metaspace Manager:管理元空间
- PS Scavenge(多线程复制收集):管理年轻代堆内存(eden区、survivor区)
- PS MarkSweep(多线程压缩收集):管理全部堆内存(eden区、survivor区、老年代)
这里可以看出当前应用使用的垃圾回收算法及其应用的堆内存分区
MemoryPoolMXBean
MemoryPoolMXBean可以检测到堆和非堆内存的所有信息,通过getUsage()和getPeakUsage()获取信息。
常用方法:
- getName() :返回表示此内存池的名称
- getMemoryManagerNames() :返回管理此内存池的内存管理器的名称
- getUsage() :返回此内存池的内存使用量
- getPeakUsage() : 返回自 Java 虚拟机启动或峰值重置以来此内存池的峰值内存使用量
- getType() :返回此内存池的类型
调用代码示例:
// 获取所有内存池MXBean列表,并遍历
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) {
// 内存分区名
String name = memoryPoolMXBean.getName();
// 内存管理器名称
String[] memoryManagerNames = memoryPoolMXBean.getMemoryManagerNames();
// 内存分区类型
MemoryType type = memoryPoolMXBean.getType();
// 内存使用情况
MemoryUsage usage = memoryPoolMXBean.getUsage();
// 内存使用峰值情况
MemoryUsage peakUsage = memoryPoolMXBean.getPeakUsage();
// 打印
logger.info(name + ":");
logger.info(" managers: {}", memoryManagerNames);
logger.info(" type: {}", type.toString());
logger.info(" usage: {}", usage.toString());
logger.info(" peakUsage: {}", peakUsage.toString());
logger.info("");
}
输出结果:
Code Cache:
managers: CodeCacheManager
type: Non-heap memory
usage: init = 2555904(2496K) used = 6661376(6505K) committed = 6684672(6528K) max = 251658240(245760K)
peakUsage: init = 2555904(2496K) used = 6661376(6505K) committed = 6684672(6528K) max = 251658240(245760K)
Metaspace:
managers: Metaspace Manager
type: Non-heap memory
usage: init = 0(0K) used = 28340208(27675K) committed = 28966912(28288K) max = -1(-1K)
peakUsage: init = 0(0K) used = 28340208(27675K) committed = 28966912(28288K) max = -1(-1K)
Compressed Class Space:
managers: Metaspace Manager
type: Non-heap memory
usage: init = 0(0K) used = 3626448(3541K) committed = 3801088(3712K) max = 1073741824(1048576K)
peakUsage: init = 0(0K) used = 3626448(3541K) committed = 3801088(3712K) max = 1073741824(1048576K)
PS Eden Space:
managers: PS MarkSweep
type: Heap memory
usage: init = 67108864(65536K) used = 27884240(27230K) committed = 132120576(129024K) max = 1406664704(1373696K)
peakUsage: init = 67108864(65536K) used = 103284736(100864K) committed = 132120576(129024K) max = 1411383296(1378304K)
PS Survivor Space:
managers: PS MarkSweep
type: Heap memory
usage: init = 11010048(10752K) used = 10981968(10724K) committed = 11010048(10752K) max = 11010048(10752K)
peakUsage: init = 11010048(10752K) used = 10981968(10724K) committed = 11010048(10752K) max = 11010048(10752K)
PS Old Gen:
managers: PS MarkSweep
type: Heap memory
usage: init = 179306496(175104K) used = 11008496(10750K) committed = 105906176(103424K) max = 2863661056(2796544K)
peakUsage: init = 179306496(175104K) used = 11008496(10750K) committed = 179306496(175104K) max = 2863661056(2796544K)
说明:
我们获取了代码缓存区、元空间、类压缩数据区、Eden区、Survior区、老年代这6个内存分区的信息,能清晰的知道当前各分区的使用情况及其峰值占用。