Java ManagementFactory解析

作者: 一字马胡
转载标志 【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将在未来合适的时候补充进来,或者在其他的文章中进行分析总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容