作者: 一字马胡
转载标志 【2017-11-14】
更新日志
日期 | 更新内容 | 备注 |
---|---|---|
2017-11-14 | 新建文章 | 初版 |
导入
ManagementFactory是一个为我们提供各种获取JVM信息的工厂类,使用ManagementFactory可以获取大量的运行时JVM信息,比如JVM堆的使用情况,以及GC情况,线程信息等,通过这些数据项我们可以了解正在运行的JVM的情况,以便我们可以做出相应的调整。本文将基于ManagementFactory,介绍如何通过ManagementFactory获取一些运行时的JVM信息,下面首先展示了ManagementFactory的类图,可以看出它提供了大量的工厂方法,使得我们可以通过调用这些方法来获取运行时的相关Bean,通过这些Bean就可以获取到我们想要的数据:
使用ManagementFactory
上文中展示的ManagementFactory类图直观的说明了ManagementFactory提供的一些方法,可以看出我们可以获取的内容很多,下面将挑选几个具有代表性的MXBean来作为使用示例。
线程:ThreadMXBean
首先,可以通过下面的方式来获取一个ThreadMXBean:
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
下面的图片展示了ThreadMXBean支持的查询方法:
下面的代码展示了ThreadMXBean的使用方法,通过ThreadMXBean提供的方法,我们可以获取详细的运行时JVM内的线程信息:
private static Map<String, Number> collectThreadInfo() { final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); Map<String, Number> map = new LinkedHashMap<String, Number>(); map.put("jvm.thread.count", threadBean.getThreadCount()); map.put("jvm.thread.daemon.count", threadBean.getDaemonThreadCount()); map.put("jvm.thread.totalstarted.count", threadBean.getTotalStartedThreadCount()); ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadBean.getAllThreadIds()); int newThreadCount = 0; int runnableThreadCount = 0; int blockedThreadCount = 0; int waitThreadCount = 0; int timeWaitThreadCount = 0; int terminatedThreadCount = 0; if (threadInfos != null) { for (ThreadInfo threadInfo : threadInfos) { if (threadInfo != null) { switch (threadInfo.getThreadState()) { case NEW: newThreadCount++; break; case RUNNABLE: runnableThreadCount++; break; case BLOCKED: blockedThreadCount++; break; case WAITING: waitThreadCount++; break; case TIMED_WAITING: timeWaitThreadCount++; break; case TERMINATED: terminatedThreadCount++; break; default: break; } } else { /* * If a thread of a given ID is not alive or does not exist, * the corresponding element in the returned array will, * contain null,because is mut exist ,so the thread is terminated */ terminatedThreadCount++; } } } map.put("jvm.thread.new.count", newThreadCount); map.put("jvm.thread.runnable.count", runnableThreadCount); map.put("jvm.thread.blocked.count", blockedThreadCount); map.put("jvm.thread.waiting.count", waitThreadCount); map.put("jvm.thread.time_waiting.count", timeWaitThreadCount); map.put("jvm.thread.terminated.count", terminatedThreadCount); long[] ids = threadBean.findDeadlockedThreads(); map.put("jvm.thread.deadlock.count", ids == null ? 0 : ids.length); return map; }
使用上面的代码可以获取当前JVM内的线程数量,并且可以计算出每种状态下的线程数量,更多数据可以参考上面展示的ThreadMXBean提供查询接口图。
内存相关MxBean
关于JVM内存相关的数据就比较丰富了,你可以参考文章浅谈JVM中的垃圾回收来初步了解JVM的内存模型,之后你应该也可以根据该文章了解到HotSpot JVM的实现中的内存模型,阅读完该文章之后,就应该知道年轻代、老年代、永久代等相关概念,下面的类是获取这些内存信息的根据类,其中包含了详细的JVM运行时内存信息,甚至包括了堆外内存信息。
class MemoryInformation { // usedMemory 是heap使用内存 (eden+survivor+old) private final long m_usedMemory; // maxMemory 是heap最大内存 private final long m_maxMemory; // usedOldGen "Old Gen"使用内存 private final long m_usedOldGen; // maxOldGen "Old Gen"最大内存 private final long m_maxOldGen; // usedPermGen "Perm Gen"使用内存 private final long m_usedPermGen; // maxPermGen "Perm Gen"最大内存 private final long m_maxPermGen; // usedEdenSpace "Eden Space"使用内存 private final long m_usedEdenSpace; // maxEdenSpace "Eden Space"最大内存 private final long m_maxEdenSpace; // usedSurvivorSpace "Survivor Space"使用内存 private final long m_usedSurvivorSpace; // maxSurvivorSpace "Survivor Space"最大内存 private final long m_maxSurvivorSpace; private final long m_usedNonHeapMemory; private final long m_maxNonHeapMemory; private MBeanServer m_mbeanServer = ManagementFactory.getPlatformMBeanServer(); private static final String DIRECT_BUFFER_MBEAN = "java.nio:type=BufferPool,name=direct"; private static final String MAPPED_BUFFER_MBEAN = "java.nio:type=BufferPool,name=mapped"; public MemoryInformation() { m_usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); m_maxMemory = Runtime.getRuntime().maxMemory(); final MemoryPoolMXBean permGenMemoryPool = getPermGenMemoryPool(); if (permGenMemoryPool != null) { final MemoryUsage usage = permGenMemoryPool.getUsage(); m_usedPermGen = usage.getUsed(); m_maxPermGen = usage.getMax(); } else { m_usedPermGen = 0; m_maxPermGen = 0; } final MemoryPoolMXBean oldGenMemoryPool = getOldGenMemoryPool(); if (oldGenMemoryPool != null) { final MemoryUsage usage = oldGenMemoryPool.getUsage(); m_usedOldGen = usage.getUsed(); m_maxOldGen = usage.getMax(); } else { m_usedOldGen = 0; m_maxOldGen = 0; } final MemoryPoolMXBean edenSpaceMemoryPool = getEdenSpacePool(); if (edenSpaceMemoryPool != null) { final MemoryUsage usage = edenSpaceMemoryPool.getUsage(); m_usedEdenSpace = usage.getUsed(); m_maxEdenSpace = usage.getMax(); } else { m_usedEdenSpace = 0; m_maxEdenSpace = 0; } final MemoryPoolMXBean survivorSpacePool = getSurvivorSpaceMemoryPool(); if (survivorSpacePool != null) { final MemoryUsage usage = survivorSpacePool.getUsage(); m_usedSurvivorSpace = usage.getUsed(); m_maxSurvivorSpace = usage.getMax(); } else { m_usedSurvivorSpace = 0; m_maxSurvivorSpace = 0; } final MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage(); m_usedNonHeapMemory = nonHeapMemoryUsage.getUsed(); m_maxNonHeapMemory = nonHeapMemoryUsage.getMax(); } public long getMaxEdenSpace() { return m_maxEdenSpace; } public long getMaxMemory() { return m_maxMemory; } public long getMaxNonHeapMemory() { return m_maxNonHeapMemory; } public long getMaxOldGen() { return m_maxOldGen; } public long getMaxPermGen() { return m_maxPermGen; } public long getMaxSurvivorSpace() { return m_maxSurvivorSpace; } private MemoryPoolMXBean getEdenSpacePool() { for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) { if (memoryPool.getName().endsWith("Eden Space")) { return memoryPool; } } return null; } private MemoryPoolMXBean getOldGenMemoryPool() { for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) { if (memoryPool.getName().endsWith("Old Gen")) { return memoryPool; } } return null; } private MemoryPoolMXBean getPermGenMemoryPool() { for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) { if (memoryPool.getName().endsWith("Perm Gen")) { return memoryPool; } } return null; } private MemoryPoolMXBean getSurvivorSpaceMemoryPool() { for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) { if (memoryPool.getName().endsWith("Survivor Space")) { return memoryPool; } } return null; } public long getUsedDirectBufferSize() { long directBufferSize = 0; try { ObjectName directPool = new ObjectName(DIRECT_BUFFER_MBEAN); directBufferSize = (Long) m_mbeanServer.getAttribute(directPool, "MemoryUsed"); } catch (Exception e) { e.printStackTrace(); } return directBufferSize; } public long getUsedEdenSpace() { return m_usedEdenSpace; } public double getUsedEdenSpacePercentage() { if (m_usedEdenSpace > 0 && m_maxEdenSpace > 0) { return 100d * m_usedEdenSpace / m_maxEdenSpace; } return 0d; } public long getUsedMappedSize() { long mappedBufferSize = 0; try { ObjectName directPool = new ObjectName(MAPPED_BUFFER_MBEAN); mappedBufferSize = (Long) m_mbeanServer.getAttribute(directPool, "MemoryUsed"); } catch (Exception e) { e.printStackTrace(); } return mappedBufferSize; } public long getUsedMemory() { return m_usedMemory; } public double getUsedMemoryPercentage() { return 100d * m_usedMemory / m_maxMemory; } public long getUsedNonHeapMemory() { return m_usedNonHeapMemory; } public double getUsedNonHeapPercentage() { if (m_usedNonHeapMemory > 0 && m_maxNonHeapMemory > 0) { return 100d * m_usedNonHeapMemory / m_maxNonHeapMemory; } return 0d; } public long getUsedOldGen() { return m_usedOldGen; } public double getUsedOldGenPercentage() { if (m_usedOldGen > 0 && m_maxOldGen > 0) { return 100d * m_usedOldGen / m_maxOldGen; } return 0d; } public long getUsedPermGen() { return m_usedPermGen; } public double getUsedPermGenPercentage() { if (m_usedPermGen > 0 && m_maxPermGen > 0) { return 100d * m_usedPermGen / m_maxPermGen; } return 0d; } public long getUsedSurvivorSpace() { return m_usedSurvivorSpace; } public double getUsedSurvivorSpacePercentage() { if (m_usedSurvivorSpace > 0 && m_maxSurvivorSpace > 0) { return 100d * m_usedSurvivorSpace / m_maxSurvivorSpace; } return 0d; } @Override public String toString() { return getClass().getSimpleName() + "[usedMemory=" + getUsedMemory() + ", maxMemory=" + getMaxMemory() + ']'; } }
虽然代码较多,但是都是可以直接运行的,参考价值非常大,如果在工作或者其他地方需要获取这些信息,可以直接参考就可以了。
GC:GarbageCollectorMXBean
同样,你应该首先阅读文章浅谈JVM中的垃圾回收来了解关于GC的一些基础内容,并且知道有哪些GC,以及设置参数,关于JVM的参数设置这部分内容,将会新建一个【JVM参数解析系列】,下面的代码可以获取关于JVM运行时GC相关的数据信息,在统计GC相关信息的时候使用了GarbageCollectorMXBean:
class GarbageCollectorInfo { private long m_lastGcCount = 0; private long m_lastGcTime = 0; private long m_lastFullgcTime = 0; private long m_lastFullgcCount = 0; private long m_lastYounggcTime = 0; private long m_lastYounggcCount = 0; public long getM_lastGcCount() { return this.m_lastGcCount; } public long getM_lastGcTime() { return this.m_lastGcTime; } public long getM_lastFullgcTime() { return this.m_lastFullgcTime; } public long getM_lastFullgcCount() { return this.m_lastFullgcCount; } public long getM_lastYounggcTime() { return this.m_lastYounggcTime; } public long getM_lastYounggcCount() { return this.m_lastYounggcCount; } private Set<String> younggcAlgorithm = new LinkedHashSet<String>() { { add("Copy"); add("ParNew"); add("PS Scavenge"); add("G1 Young Generation"); } }; private Set<String> oldgcAlgorithm = new LinkedHashSet<String>() { { add("MarkSweepCompact"); add("PS MarkSweep"); add("ConcurrentMarkSweep"); add("G1 Old Generation"); } }; private Map<String, Number> collectGC() { long gcCount = 0; long gcTime = 0; long oldGCount = 0; long oldGcTime = 0; long youngGcCount = 0; long youngGcTime = 0; Map<String, Number> map = new LinkedHashMap<>(); for (final GarbageCollectorMXBean garbageCollector : ManagementFactory.getGarbageCollectorMXBeans()) { gcTime += garbageCollector.getCollectionTime(); gcCount += garbageCollector.getCollectionCount(); String gcAlgorithm = garbageCollector.getName(); if (younggcAlgorithm.contains(gcAlgorithm)) { youngGcTime += garbageCollector.getCollectionTime(); youngGcCount += garbageCollector.getCollectionCount(); } else if (oldgcAlgorithm.contains(gcAlgorithm)) { oldGcTime += garbageCollector.getCollectionTime(); oldGCount += garbageCollector.getCollectionCount(); } } // // GC实时统计信息 // map.put("jvm.gc.count", gcCount - m_lastGcCount); map.put("jvm.gc.time", gcTime - m_lastGcTime); final long fullGcCount = oldGCount - m_lastFullgcCount; map.put("jvm.fullgc.count", fullGcCount); map.put("jvm.fullgc.time", oldGcTime - m_lastFullgcTime); map.put("jvm.younggc.count", youngGcCount - m_lastYounggcCount); map.put("jvm.younggc.time", youngGcTime - m_lastYounggcTime); if (youngGcCount > m_lastYounggcCount) { map.put("jvm.younggc.meantime", (youngGcTime - m_lastYounggcTime) / (youngGcCount - m_lastYounggcCount)); } else { map.put("jvm.younggc.meantime", 0); } // // GC增量统计信息 // m_lastGcCount = gcCount; m_lastGcTime = gcTime; m_lastYounggcCount = youngGcCount; m_lastYounggcTime = youngGcTime; m_lastFullgcCount = oldGCount; m_lastFullgcTime = oldGcTime; return map; } }
类加载器:ClassLoadingMXBean
使用ClassLoadingMXBean可以获取当前JVM的类加载信息,下面的代码展示了ClassLoadingMXBean的使用方法:
private static Map<String, Number> collectClassLoadingInfo() { ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); Map<String, Number> map = new LinkedHashMap<String, Number>(); map.put("jvm.classloading.loaded.count", classLoadingMXBean.getLoadedClassCount()); map.put("jvm.classloading.totalloaded.count", classLoadingMXBean.getTotalLoadedClassCount()); map.put("jvm.classloading.unloaded.count", classLoadingMXBean.getUnloadedClassCount()); return map; }
结语
本文包含了大量的代码,但是这些代码都是可执行的代码,执行这些代码可以快速直观的获取到JVM运行时的一些关键数据,根据这些数据我们就可以初步了解正在运行的JVM的一些信息,有时候就可以根据这些信息来优化我们的项目,比如是否有太多的线程在空闲状态,或者是否内存占用量很大,或者是否频繁发生Full GC(以此来调整我们的JVM启动参数)。这些数据对于维护和优化项目代码都是非常有价值的,本文试图分析与总结java的ManagementFactory的用法,从文章开篇的图片可以看出ManagementFactory提供了非常丰富的获取JVM运行时数据接口,而本文仅仅挑选了其中比较有代表性的MXBean,关于其他的MXBean的相关用法可以直接参考jdk源码,本文没有涉及到的那些MXBean将在未来合适的时候补充进来,或者在其他的文章中进行分析总结。