前言对于一个运行中的Java程序,我们想要管理和监控它的状态,比如:内存、CPU使用率、线程数、垃圾回收等,那么使用JMX是一个非常优雅的解决方案。你可能听说过JConsole、VisualVM等性能调优工具,却不知道这两兄弟底层都依赖它。本文将带你走进Java的管理扩展:JMX。JMX不仅是Java管理系统的标准和规范;它也是一个接口和一个“框架”。有标准和规范可以让开发者定制和开发自己的扩展功能,而JDK作为一个“框架”,帮助我们实现了常用的功能,尤其是JVM本身的监控和管理。所属专栏【方向盘】-JavaEE相关下载【本专栏源码】:https://github.com/yourbatman/FXP-java-ee【技术专栏源码大本营】:https://github.com/yourbatman/tech-column-learning【女娲刀-Initializr项目】访问地址:http://152.136.106.14:8761【程序员网盘】公益上线,注册送1G超小容量,助你一臂之力减法练习:https://wangpan.yourbatman.cn【Java开发包(Mac)】:https://wangpan.yourbatman.cn/s/rEH0提取码:javakit版本协议JavaEE:6,7,8JakartaEE:8,9.9.1正文JMXJMX(JavaManagementExtensions,即Java管理扩展)是一个为应用程序、设备和系统嵌入管理功能的框架。我们可以使用jmx来监控和管理程序的运行状态。JMX是一套嵌入在JavaEE(嵌入到JRE中)的标准代理和服务,这意味着只要遵循这个接口标准,我们的应用程序就可以被管理和监控。为了规范管理和监控,Java平台使用JMX作为管理和监控的标准接口。任何程序只要按照JMX规范访问这个接口,就可以获得所有的管理和监控信息。常用的运维监控工具,如Zabbix、Nagios,通过JMX获取的信息,对JVM本身进行监控。JMX是一个标准接口,不仅可以用来管理JVM,还可以用来管理应用程序本身。这是官方的JMX架构图:从图中可以看出,JMX技术分为三层:设备/资源层:这些托管资源是MBean/MXBeans代理层:MBeanServer是代理层的核心组件,MBeans它们都注册在这里,这样它就可以作为一个统一的代理对外提供功能服务。代理层实际上是一个独立的Java线程远程管理层:可以通过多种不同的方式访问JMX技术,并且通过给定的协议访问每个适配器。MBeanServer中注册的所有MBean,如Html协议、Http协议、JDK本身实现的RMI协议等。什么是MBeanMBean=ManagedBean。它的本质就是我们常说的JavaBean,遵循JavaBean规范,只是专门用于JMX,所以叫MBean。JMX调用所有被管理的资源MBean,这些资源都由MBeanServer管理。JVM会将自己的资源(CPU、内存等)注册到JMX中,也可以自定义MBean放入,从而实现自定义监控的能力。最后,通过公开RMI/HTTP协议提供外部访问。注意:JMX不需要安装任何额外的组件,也不需要第三方库,因为MBeanServer已经内置在JavaSE标准库中。JDK提供的MBean主要在java.lang.management和javax.management这两个包中。MBean分为四种:1.标准MBean:最常用也是最简单的一种,它没有类似于普通JavaBean的结构,不同的是管理接口是通过方法名来描述的。只要遵循一定的命名规则,就可以注册到MBeanServer中。定义一个接口。接口名称必须是xxxMBean(必须以MBean为后缀结尾)。编写接口的实现类,然后将这个实现类注册到MBeanServer中。2.DynamicMBean:它的属性和方法是在运行时定义的,这意味着它的属性和方法可以动态改变。所有动态MBean都必须实现DynamicMBean接口,然后注册它们。动态bean的辅助类主要有MBeanConstructorInfo、MBeanAttributeInfo、MBeanOperationInfo等,动态bean是妥协的产物,因为有些MBean已经存在,再转化成标准的MBean比较费力,不切实际,所以我向动态bean妥协.3.定制时几乎不使用OpenMBean:OpenMBean需要实现DynamicMBean接口。与DynamicBeans不同,它提供了更复杂的元数据数据,并且在接口中,只使用了几种预定义的通用数据类型。:OpenMBeanInfo,OpenMBeanOperationInfo,OpenMBeanConstructorInfo,OpenMBeanParameterInfo,OpenMBeanAttributeInfo4.ModelMBean:如果不能修改现有的Java类,使用它是一个不错的选择。通过实现接口javax.management.modelmbean.RequiredModelMBean,我们只需要实例化类,然后注册即可实现对资源的管理。编写ModelMBean最大的挑战是告诉ModelMBean对象托管资源的熟悉程度和方法可以暴露给代理层,ModelMBeanInfo对象描述了将暴露给代理的构造函数,属性,操作甚至监听器.画外音:一般情况下,我们只需要了解StandardMBean即可。MBean和MXBean的区别MBean和MXBean的区别主要是在接口中引用一些其他类型的类(复合类型)时,它们的表现是不同的。MBean:属性不能是复合类型/自定义类型,否则无法识别。MXBean:属性可以是自定义类型。比如heapMemoryUsage属性是在JDK自带的MemoryMXBean中定义的,它是一个复合类型。什么是MBeanServer?顾名思义:一个用于管理MBean的“服务器”。一般来说,一个JVM只有一个MBeanServer(通过ManagementFactory.getPlatformMBeanServer()API获取),用于管理JVM中所有的MBean,对外提供服务。如果需要多个MBeanServer(比如不同的域),可以通过APIMBeanServerFactory.newMBeanServer(Stringdomain)创建它们。什么是连接器和适配器?当MBean注册到MBeanServer上时,功能就可用了,这些功能可以通过协议公开。不同的协议都有相应的Connector或Adapter(这里的Connector和Adapter可以认为是同一个角色)。因此,只要有connector/adapter,就可以通过各种协议暴露功能,比如Http协议、Saop协议、RMI等。JDK只实现了基于RMI的javax.management.remote.rmi.RMIConnector,通过默认。默认情况下可以直接访问JConsole和VisualVM等工具。注:SpringBootActuator为其管理和监控端点提供了Http和RMI(JMX)两种访问方式,但其Http方式并没有实现Connector/Adaptor,甚至基于Http的操作方式也不是JMX方式(实对于Endpoint方法),不要让一些文章误导了你。既然有了Http,那么JMX还有什么意义呢?这个问题曾经困扰过我,本来也没有很想去理解JMX的意思。诚然,JMX能完成的任务都可以通过Http来完成,但有些情况下使用JMX会更方便。简单的说,Http更重,JMX更轻。Http是一种更抽象、应用范围更广、功能更强大的协议/服务,因此要做的工作也会更多。比如轻量级的方法有Get、Post、Put、Delete等。JMX是一种比Http更具体、使用范围更小、功能更弱的协议/服务。所以它的优点就是轻便,好用。JMX的特性决定了它非常适合做资源监控。因此,为了监控JVM的运行,各大监控组件和框架都会将JMX作为首选,而Http协议只是为了产品化。选修的。jmx内嵌在jdk/jre中,不需要额外导入包。JavaEE5:JavaEE8:JSR3的内容与JSR255的内容基本相同,可以认为是一样的。生存状态是高级必备。例如监控、JVM性能分析、调优、问题定位等实现(框架)无代码示例虽然demo示例是重头戏,但由于本文不是JMX专题,所以只会说明JMX在原生方式。至于在Spring、SpringBoot、commons-modeler中的使用,到此为止。直接使用JDK内置的MBean/MXBeanJDK内置了“大量”的MBean供你直接使用:ClassLoadingMXBean:Java虚拟机的类加载系统。CompilationMXBean:Java虚拟机的编译系统。MemoryMXBean:Java虚拟机的内存系统。ThreadMXBean:Java虚拟机的线程系统。RuntimeMXBean:Java虚拟机的运行时系统。OperatingSystemMXBean:运行Java虚拟机的操作系统。GarbageCollectorMXBean:Java虚拟机中的垃圾收集器。MemoryManagerMXBean:Java虚拟机中的内存管理器。MemoryPoolMXBean:Java虚拟机中的内存池。这些示例可以通过ManagementFactory获得。@Testpublicvoidtest1(){ClassLoadingMXBeanclassLoadingMXBean=ManagementFactory.getClassLoadingMXBean();ObjectNameobjectName=classLoadingMXBean.getObjectName();longtotalLoadedClassCount=classLoadingMXBean.getTotalLoadedClassCount();intloadedClassCount=classLoadingMXBean.getLoadedClassOutl();longunloadedClassCount=classLoading.getLoadingMXBean.getUnloadedSystem();("对象名称:"+对象名称);System.out.println("JVM启动加载的Class类总数(一个类被多次加载):"+totalLoadedClassCount);System.out.println("JVM当前状态加载Class类总数:"+loadedClassCount);System.out.println("JVM未加载的Class类总数:"+unloadedClassCount);}objectName:java.Loadedmultipletimes):1743JVM当前状态加载的Classes总数:1743JVM未加载的Classes总数:0);//JVM信息StringspecVendor=runtimeMXBean.getSpecVendor();StringspecName=runtimeMXBean.getSpecName();StringspecVersion=runtimeMXBean.getSpecVersion();StringbootClassPath=runtimeMXBean.getBootClassPath();StringclassPath=runtimeMXBean.getClassPath();StringlibraryPath=runtimeMXBean.getLibraryPath();System.out.println("对象名称:"+对象名称);System.out.println("运行时名称:"+name);System.out.println("当前JVM进程ID:"+name.split("@")[0]);System.out.println("虚拟机信息:"+specVendor+":"+specName+":"+specVersion);//System.out.println("bootClassPath:"+bootClassPath);//System.out.println("classPath:"+classPath);//System.out.println("libraryPath:"+libraryPath);}objectName:java.lang:type=Runtime运行时名称:9966@YourBatman-MBA.local当前JVM进程ID:9966虚拟机信息:OracleCorporation:JavaVirtualMachineSpecification:1.8RuntimeMXBean常用于获取JVM进程ID@Testpublicvoidtest3(){//JVM内存情况MemoryMXBeanmemoryMXBean=ManagementFactory.getMemoryMXBean();ObjectNameobjectName=memoryMXBean.getObjectName();MemoryUsageheapMemoryUsage=memoryMXBean.getHeapMemoryUsage();MemoryUsagenonHeapMemoryUsage=memoryMXBean.getUsageUsage();System:"+对象名称);System.out.println("已用堆内存:"+heapMemoryUsage);System.out.println("Non-heapmemoryused:"+nonHeapMemoryUsage);//操作系统的内存情况?longl=Runtime.getRuntime().totalMemory();longl1=Runtime.getRuntime().freeMemory();}objectName:java.lang:type=内存使用堆内存:init=268435456(262144K)used=24183016(23616K)committed=257425408(251392K)max=3817865216(3728384K)usednon-heapmemory:init=2555904(2496K)used=12547040(12252K)committed=13959168(13632K)max=-1(-1K)下面的操作MXBean是操作系统级信息:@Testpublicvoidtest4(){OperatingSystemMXBeanosbean=ManagementFactory.getOperatingSystemMXBean();System.out.println("操作系统架构:"+osbean.getArch());System.out.println("操作系统名称:"+osbean.getName());System.out.println("处理器数量:"+osbean.getAvailableProcessors());System.out.println("操作系统版本:"+osbean.getVersion());ThreadMXBeanthreadBean=ManagementFactory.getThreadMXBean();System.out.println("线程总数:"+threadBean.getThreadCount());//}操作系统架构:aarch64操作系统名称:MacOSX处理器数量:8操作系统版本:11.6线程总数:4自定义MBean-本地线程连接除了上述系统自带的MBean/MXBean外,更重要的事情是自定义MBean:将公共用户实体类公开为MBean/***MBean资源通过接口公开,[必须]以MBean结尾才能算作MBean**@authorYourBatman.
