开篇
在es的jvm监控当中,通过JMX能够采集的指标包括JVM堆空间、JVM非堆空间、JVM新生区幸存区老年区空间、JVM的GC耗时、线程数。获取这些指标的核心方法都是通过JMX提供的接口进行实现。
在es的源码当中JvmStats的类,内部有具体的代码实现,有兴趣的可以看看。
获取堆和非堆空间
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean()获取线程数
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean()获取JVM各代内存情况
List<> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();获取JVM各代垃圾回收情况
List<> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans()
源码分析
- 1、memoryMXBean.getHeapMemoryUsage()获取堆空间占用信息。
- 2、memoryMXBean.getNonHeapMemoryUsage()获取非堆空间占用信息。
- 3、ManagementFactory.getMemoryPoolMXBeans()获取JVM各代信息。
- 4、memoryPoolMXBean.getUsage()获取JVM各代的使用情况。
- 5、memoryPoolMXBean.getPeakUsage()获取JVM各代巅峰的使用情况。
public class JvmStats implements Streamable, Serializable, ToXContent {
private static boolean enableLastGc;
private final static RuntimeMXBean runtimeMXBean;
private final static MemoryMXBean memoryMXBean;
private final static ThreadMXBean threadMXBean;
private static Method managementFactoryPlatformMXBeansMethod;
private static Method getLastGcInfoMethod;
private static Method getMemoryUsageBeforeGcMethod;
private static Method getMemoryUsageAfterGcMethod;
private static Method getStartTimeMethod;
private static Method getEndTimeMethod;
private static Method getDurationMethod;
static {
// 负责获取JVM启动事件
runtimeMXBean = ManagementFactory.getRuntimeMXBean();
// 获取JVM内存堆和非堆空间
memoryMXBean = ManagementFactory.getMemoryMXBean();
// 获取线程的数量
threadMXBean = ManagementFactory.getThreadMXBean();
try {
managementFactoryPlatformMXBeansMethod = ManagementFactory.class.getMethod("getPlatformMXBeans", Class.class);
} catch (Throwable e) {
managementFactoryPlatformMXBeansMethod = null;
}
JvmInfo info = JvmInfo.jvmInfo();
boolean enableLastGc = Booleans.parseBoolean(System.getProperty("monitor.jvm.enable_last_gc"), defaultEnableLastGc);
if (enableLastGc) {
try {
Class sunGcClass = Class.forName("com.sun.management.GarbageCollectorMXBean");
Class gcInfoClass = Class.forName("com.sun.management.GcInfo");
getLastGcInfoMethod = sunGcClass.getDeclaredMethod("getLastGcInfo");
getLastGcInfoMethod.setAccessible(true);
getMemoryUsageBeforeGcMethod = gcInfoClass.getDeclaredMethod("getMemoryUsageBeforeGc");
getMemoryUsageBeforeGcMethod.setAccessible(true);
getMemoryUsageAfterGcMethod = gcInfoClass.getDeclaredMethod("getMemoryUsageAfterGc");
getMemoryUsageAfterGcMethod.setAccessible(true);
getStartTimeMethod = gcInfoClass.getDeclaredMethod("getStartTime");
getStartTimeMethod.setAccessible(true);
getEndTimeMethod = gcInfoClass.getDeclaredMethod("getEndTime");
getEndTimeMethod.setAccessible(true);
getDurationMethod = gcInfoClass.getDeclaredMethod("getDuration");
getDurationMethod.setAccessible(true);
} catch (Throwable ex) {
enableLastGc = false;
}
}
JvmStats.enableLastGc = enableLastGc;
}
public static JvmStats jvmStats() {
// 获取系统启动时间
JvmStats stats = new JvmStats(System.currentTimeMillis(), runtimeMXBean.getUptime());
// 获取JVM的堆和非堆空间
stats.mem = new Mem();
// 获取堆空间
MemoryUsage memUsage = memoryMXBean.getHeapMemoryUsage();
stats.mem.heapUsed = memUsage.getUsed() < 0 ? 0 : memUsage.getUsed();
stats.mem.heapCommitted = memUsage.getCommitted() < 0 ? 0 : memUsage.getCommitted();
stats.mem.heapMax = memUsage.getMax() < 0 ? 0 : memUsage.getMax();
// 获取非堆空间
memUsage = memoryMXBean.getNonHeapMemoryUsage();
stats.mem.nonHeapUsed = memUsage.getUsed() < 0 ? 0 : memUsage.getUsed();
stats.mem.nonHeapCommitted = memUsage.getCommitted() < 0 ? 0 : memUsage.getCommitted();
// 获取JVM各代内存空间,List内部包含新生代、幸存代、老年代空间
List memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
List pools = new ArrayList<>();
for (int i = 0; i < memoryPoolMXBeans.size(); i++) {
try {
MemoryPoolMXBean memoryPoolMXBean = memoryPoolMXBeans.get(i);
// 获取JVM每个代的内存使用情况,最大内存使用情况
MemoryUsage usage = memoryPoolMXBean.getUsage();
MemoryUsage peakUsage = memoryPoolMXBean.getPeakUsage();
// 获取JVM代的名称
String name = GcNames.getByMemoryPoolName(memoryPoolMXBean.getName(), null);
if (name == null) { // if we can't resolve it, its not interesting.... (Per Gen, Code Cache)
continue;
}
// 统计各代内存使用情况
pools.add(new MemoryPool(name,
usage.getUsed() < 0 ? 0 : usage.getUsed(),
usage.getMax() < 0 ? 0 : usage.getMax(),
peakUsage.getUsed() < 0 ? 0 : peakUsage.getUsed(),
peakUsage.getMax() < 0 ? 0 : peakUsage.getMax()
));
} catch (OutOfMemoryError err) {
throw err; // rethrow
} catch (Throwable ex) {
}
}
stats.mem.pools = pools.toArray(new MemoryPool[pools.size()]);
// 获取线程数量
stats.threads = new Threads();
stats.threads.count = threadMXBean.getThreadCount();
stats.threads.peakCount = threadMXBean.getPeakThreadCount();
// 获取JVM的GC信息
List gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
stats.gc = new GarbageCollectors();
stats.gc.collectors = new GarbageCollector[gcMxBeans.size()];
for (int i = 0; i < stats.gc.collectors.length; i++) {
GarbageCollectorMXBean gcMxBean = gcMxBeans.get(i);
stats.gc.collectors[i] = new GarbageCollector();
stats.gc.collectors[i].name = GcNames.getByGcName(gcMxBean.getName(), gcMxBean.getName());
stats.gc.collectors[i].collectionCount = gcMxBean.getCollectionCount();
stats.gc.collectors[i].collectionTime = gcMxBean.getCollectionTime();
// 获取上一次GC信息
if (enableLastGc) {
try {
Object lastGcInfo = getLastGcInfoMethod.invoke(gcMxBean);
if (lastGcInfo != null) {
Map usageBeforeGc = (Map) getMemoryUsageBeforeGcMethod.invoke(lastGcInfo);
Map usageAfterGc = (Map) getMemoryUsageAfterGcMethod.invoke(lastGcInfo);
long startTime = (Long) getStartTimeMethod.invoke(lastGcInfo);
long endTime = (Long) getEndTimeMethod.invoke(lastGcInfo);
long duration = (Long) getDurationMethod.invoke(lastGcInfo);
long previousMemoryUsed = 0;
long memoryUsed = 0;
long memoryMax = 0;
for (Map.Entry entry : usageBeforeGc.entrySet()) {
previousMemoryUsed += entry.getValue().getUsed();
}
for (Map.Entry entry : usageAfterGc.entrySet()) {
MemoryUsage mu = entry.getValue();
memoryUsed += mu.getUsed();
memoryMax += mu.getMax();
}
stats.gc.collectors[i].lastGc = new GarbageCollector.LastGc(startTime, endTime, memoryMax, previousMemoryUsed, memoryUsed, duration);
}
} catch (Exception e) {
// e.printStackTrace();
}
}
}
return stats;
}
}
- 1、根据JVM各代的名字确定属于新生代、幸存代、老年代。
- 2、根据GC的方法名确定是新生代、老年代。
public class GcNames {
public static final String YOUNG = "young";
public static final String OLD = "old";
public static final String SURVIVOR = "survivor";
/**
* Resolves the GC type by its memory pool name ({@link java.lang.management.MemoryPoolMXBean#getName()}.
*/
public static String getByMemoryPoolName(String poolName, String defaultName) {
if ("Eden Space".equals(poolName) || "PS Eden Space".equals(poolName) || "Par Eden Space".equals(poolName) || "G1 Eden Space".equals(poolName)) {
return YOUNG;
}
if ("Survivor Space".equals(poolName) || "PS Survivor Space".equals(poolName) || "Par Survivor Space".equals(poolName) || "G1 Survivor Space".equals(poolName)) {
return SURVIVOR;
}
if ("Tenured Gen".equals(poolName) || "PS Old Gen".equals(poolName) || "CMS Old Gen".equals(poolName) || "G1 Old Gen".equals(poolName)) {
return OLD;
}
return defaultName;
}
public static String getByGcName(String gcName, String defaultName) {
if ("Copy".equals(gcName) || "PS Scavenge".equals(gcName) || "ParNew".equals(gcName) || "G1 Young Generation".equals(gcName)) {
return YOUNG;
}
if ("MarkSweepCompact".equals(gcName) || "PS MarkSweep".equals(gcName) || "ConcurrentMarkSweep".equals(gcName) || "G1 Old Generation".equals(gcName)) {
return OLD;
}
return defaultName;
}
}