JVM监控java.lang.management简介

举报
yoshine 发表于 2020/04/30 09:41:36 2020/04/30
【摘要】 java.lang.management是JDK自带的一套工具库。通过工厂类ManagementFactory对外提供一套管理接口,提供JVM和JVM所运行操作系统的各种信息,例如:内存使用情况、GC情况和操作系统版本等。基于以上信息,可以辅助我们对定位问题或者性能调优提供数据支撑。Management同时允许从本地和远程(JMX)对正在运行的JVM进行监视和管理。 MXBean简...

java.lang.management是JDK自带的一套工具库。通过工厂类ManagementFactory对外提供一套管理接口,提供JVM和JVM所运行操作系统的各种信息,例如:内存使用情况、GC情况和操作系统版本等。基于以上信息,可以辅助我们对定位问题或者性能调优提供数据支撑。Management同时允许从本地和远程(JMX)对正在运行的JVM进行监视和管理。




MXBean简介


     
根据ManagementFactory的javadoc,可以看出它提供了大量的工厂方法,使得我们可以通过调用这些方法来获取JVM各组件相关的JavaBean,通过这些JavaBean就可以获取到对应组件的运行状态数据。而这些JavaBean是各个服务启动时自动注册好的,运行数据也是提前计算好的,所以我们并不会因为获取相关数据,而导致额外消耗资源进行计算。

      这些JavaBean被称之为MBean或者MXBean。MBean和MXBean两者的区别在于:MXBean是一个特殊的MBean,它的数据类型被限制为开放类型,基本上是原始类型、字符串及其组合。由于这些限制,MXBean可以在不加载类的情况下使用,这使得它们甚至可以与非Java客户机进行互操作。
这个特性对于我们进行通用封装是十分重要的。




使用案例1:


      JVM
频繁的FullGC或者Java服务发生了OOM,但是JVM进程并没有自动退出。这种场景下,仅仅在操作系统层面对JVM进程进行监控是不能及时发现相关问题的。可以使用MemoryMXBean和GarbageCollectorMXBean来获取相关信息;并结合其他监控或者定时执行机制,实现告警等功能。

实现代码:

public class MemoryMXBeanTest {

    private static volatile MemoryMXBean memoryMBean;

    private static volatile List<GarbageCollectorMXBean> garbageCollectorMXBeanList;

 

    /**

     *  获取memoryMBean

     */

    private static void initMemoryMXBean() {

        synchronized (MemoryMXBeanTest.class) {

            if (memoryMBean == null) {

                try {

                    memoryMBean = AccessController.doPrivileged(

                            new PrivilegedExceptionAction<MemoryMXBean>() {

                                @Override

                                public MemoryMXBean run() throws DrsException {

                                    return ManagementFactory.getMemoryMXBean();

                                }

                            });

                } catch (Exception exp) {

                    log.error("", exp);

                }

            }

        }

    }

 

    /**

     * 获取GarbageCollectorMXBean

     */

    private static void initGarbageCollectorMXBean() {

        synchronized (MemoryMXBeanTest.class) {

            if (garbageCollectorMXBeanList == null) {

                try {

                    garbageCollectorMXBeanList = AccessController.doPrivileged(

                            new PrivilegedExceptionAction<List<GarbageCollectorMXBean>>() {

                                @Override

                                public List<GarbageCollectorMXBean> run() throws DrsException {

                                    return ManagementFactory.getGarbageCollectorMXBeans();

                                }

                            });

                } catch (Exception exp) {

                    log.error("", exp);

                }

            }

        }

    }

 

    public static MemoryMXBean getMemoryMBean() {

        if (memoryMBean == null) {

            initMemoryMXBean();

        }

        return memoryMBean;

    }

 

    public static List<GarbageCollectorMXBean> getGCMXBeanList() {

        if (garbageCollectorMXBeanList == null) {

            initGarbageCollectorMXBean();

        }

        return garbageCollectorMXBeanList;

    }

 

    private Map<String, Object> getMemStatus() {

        Map<String, Object> memStatus = new HashMap<String, Object>();

        // 堆内存

        memStatus.put("Heap mem Committed", getMemoryMBean().getHeapMemoryUsage().getCommitted());

        memStatus.put("Heap mem Init", getMemoryMBean().getHeapMemoryUsage().getInit());

        memStatus.put("Heap mem Max", getMemoryMBean().getHeapMemoryUsage().getMax());

        memStatus.put("Heap mem Used", getMemoryMBean().getHeapMemoryUsage().getUsed());

        // 堆内存使用率

        memStatus.put("Heap mem Used Rato",

                (getMemoryMBean().getHeapMemoryUsage().getUsed() * 100 / getMemoryMBean().getHeapMemoryUsage().getMax()) + "%");

 

        // 堆外内存

        memStatus.put("Non-Heap mem Committed", getMemoryMBean().getNonHeapMemoryUsage().getCommitted());

        memStatus.put("Non-Heap mem Init", getMemoryMBean().getNonHeapMemoryUsage().getInit());

        memStatus.put("Non-Heap mem Used", getMemoryMBean().getNonHeapMemoryUsage().getUsed());

        return memStatus;

    }

 

    private Map<String, Object> getGcStatus() {

        Map<String, Object> gcStatus = new HashMap<String, Object>();

        if (!CollectionUtils.isEmpty(getGCMXBeanList())) {

            // 不同的垃圾回收器

            for (GarbageCollectorMXBean gcMXBean : getGCMXBeanList()) {

                gcStatus.put(gcMXBean.getName() + "-" + Arrays.toString(gcMXBean.getMemoryPoolNames()) + "-count", gcMXBean.getCollectionCount());

                gcStatus.put(gcMXBean.getName() + "-" + Arrays.toString(gcMXBean.getMemoryPoolNames()) + "-time", gcMXBean.getCollectionTime());

            }

        }

        return gcStatus;

    }

 

    public void print() {

        log.info("jvm mem status: {}", GsonUtils.toJson(getMemStatus()));

 

        log.info("jvm gc status: {}", GsonUtils.toJson(getGcStatus()));

    }

 

}

运行结果:

2020-03-09 08:24:30,001 jvm mem status: {"Non-Heap mem Used":201168408,"Heap mem Used":710231248,"Heap mem Used Rato":"48%","Heap mem Committed":1467482112,"Non-Heap mem Init":2555904,"Non-Heap mem Committed":207945728,"Heap mem Init":1474297856,"Heap mem Max":1467482112}

2020-03-09 08:24:30,001 jvm gc status: {"PS Scavenge-[PS Eden Space, PS Survivor Space]-count":210,"PS MarkSweep-[PS Eden Space, PS Survivor Space, PS Old Gen]-time":842,"PS MarkSweep-[PS Eden Space, PS Survivor Space, PS Old Gen]-count":4,"PS Scavenge-[PS Eden Space, PS Survivor Space]-time":2213}



使用案例2


     
有业务功能大量并发,同时占用了大量数据库连接;从而导致连接池中可用连接长时间无空闲的话,就会导致业务持久化操作异常。下面就以使用Druid数据库连接池为例,通过Druid自带的MXbean获取连接池相关信息。
PS:
      1、多数Java连接池都会注册MXbean并记录重要监控数据,具体实现和数据线略有差别;
      2、druid自带monitor;需要配置额外的数据库进行存储,可参见:https://github.com/alibaba/druid/wiki/druid-monitor%E8%AE%BE%E8%AE%A1

      通过JMX连接JVM,可以看到DruidDataSource下有两个数据源,他们的ObjectName为:com.alibaba.druid:type=DruidDataSource,id=XXX,每个数据源的MXBean中各有一组Attribute记录相关监控项。


实现代码:

public class DruidMXBeanTest {

 

    private static volatile MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

 

    private static void initMBeanServer() {

        synchronized (DruidMXBeanTest.class) {

            if (mBeanServer == null) {

                try {

                    mBeanServer = AccessController.doPrivileged(

                            new PrivilegedExceptionAction<MBeanServer>() {

                                @Override

                                public MBeanServer run() throws DrsException {

                                    return ManagementFactory.getPlatformMBeanServer();

                                }

                            });

                } catch (Exception exp) {

                    log.error("", exp);

                }

            }

        }

    }

 

    private static MBeanServer getMBeanServer() {

        if (mBeanServer == null) {

            initMBeanServer();

        }

        return mBeanServer;

    }

 

    public static Set<ObjectInstance> queryMBeans(String objectName) {

        try {

            if (getMBeanServer() != null) {

                return getMBeanServer().queryMBeans(new ObjectName(objectName), null);

            }

        } catch (Exception e) {

            log.error("", e);

        }

        return null;

    }

 

    public static Map<String, Object> getAttributes(String objectName, List<String> attrNameList) {

        try {

            return getAttributes(new ObjectName(objectName), attrNameList);

        } catch (Exception e) {

            log.error("", e);

        }

        return null;

    }

 

    public static Map<String, Object> getAttributes(ObjectName objectName, List<String> attrNameList) {

        Map<String, Object> result = new HashMap<String, Object>();

        if (CollectionUtils.isEmpty(attrNameList) || getMBeanServer() == null) {

            return result;

        }

        try {

            AttributeList attributeList = getMBeanServer().getAttributes(objectName, attrNameList.toArray(new String[] {}));

            for (int i = 0; i < attrNameList.size(); i++) {

                result.put(attrNameList.get(i), i < attributeList.size() ? ((Attribute) attributeList.get(i)).getValue() : "");

            }

            return result;

        } catch (Exception e) {

            log.error("", e);

        }

        return result;

    }

 

    private Map<String, Map<String, Object>> getDruidDataSourceStatus() {

        Map<String, Map<String, Object>> druidDataSourceMap = new HashMap<String, Map<String, Object>>();

        try {

            // 结果数量不固定并且ID每次随机,使用带通配符的ObjectName进行批量查询

            Set<ObjectInstance> druidDataSourceSet = queryMBeans("com.alibaba.druid:type=DruidDataSource,id=*");

            if (druidDataSourceSet != null) {

 

                // 一个数据源一组attribute

                druidDataSourceSet.stream().forEach(objectInstance -> {

                    Map<String, Object> druidDataSource = new HashMap<String, Object>();

 

                    /**

                     * 需要获取到的监控项

                     *

                     * druid更多相关监控项可以在com.alibaba.druid.pool.DruidDataSource中查看(注意使用时需要大写驼峰格式)

                     */

                    List<String> attrList = new ArrayList<String>() {

                        {

                            // 数据源用户名

                            add("Username");

                            // 最大连接池数量

                            add("MaxActive");

                            // 最小连接池数量

                            add("MinIdle");

                            // 连接池中空闲连接数

                            add("PoolingCount");

                            // 连接池中正在使用的连接数

                            add("ActiveCount");

                        }

                    };

                    druidDataSource = getAttributes(objectInstance.getObjectName(), attrList);

                    druidDataSourceMap.put(String.valueOf(druidDataSource.get("Username")), druidDataSource);

                });

            }

 

        } catch (Exception e) {

            log.error("", e);

        }

        return druidDataSourceMap;

    }

 

    public void print() {

        log.info("druid dataSource status: {}", GsonUtils.toJson(getDruidDataSourceStatus()));

    }

}

 

运行结果:

2020-03-09 08:24:30,002 druid dataSource status: {"db1":{"Username":"db1","MaxActive":20,"MinIdle":5,"ActiveCount":0,"PoolingCount":5},"db2":{"Username":"db2","MaxActive":20,"MinIdle":10,"ActiveCount":1,"PoolingCount":9}}

 


 

总结

很多开源中间件都会使用Management方式进行自定义扩展,记录相关运行状况数据;例如Kafka、logback等,都可以使用类似方式获取数据。

参考资料:

https://www.jianshu.com/p/5d854245051d
https://baike.baidu.com/item/java.lang.management/5179868?fr=aladdin
http://www.voidcn.com/article/p-cwctaeex-bsq.html

https://zimt8.com/questions/16275207/

https://docs.oracle.com/javase/tutorial/jmx/mbeans/mxbeans.html

https://docs.oracle.com/javase/8/docs/api/java/lang/management/package-summary.html

 


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。