年底Sun 公司发布了 Java Standard Edition (Java SE )的最终正式版代号 Mustang(野马)跟 Tiger(Java SE )相比Mustang 在性能方面有了不错的提升与 Tiger 在 API 库方面的大幅度加强相比虽然 Mustang 在 API 库方面的新特性显得不太多但是也提供了许多实用和方便的功能在脚本WebServiceXML编译器 API数据库JMX网络和 Instrumentation 方面都有不错的新特性和功能加强 本系列 文章主要介绍 Java SE 在 API 库方面的部分新特性通过一些例子和讲解帮助开发者在编程实践当中更好的运用 Java SE 提高开发效率
本文是其中的第三篇介绍了 Java 管理扩展(Java Management ExtensionJMX) 架构及其框架以及在 Java SE 中新引入的 JMX API —— javalangmanagement 包 最后作者讲述了此 API 在 Java SE 中的相关改进和对未来版本的展望
前言
在 Java 程序的运行过程中对 JVM 和系统的监测一直是 Java 开发人员在开发过程所需要的一直以来Java 开发人员必须通过一些底层的 JVM API比如 JVMPI 和 JVMTI 等才能监测 Java 程序运行过程中的 JVM 和系统的一系列情况这种方式一直以来被人所诟病因为这需要大量的 C 程序和 JNI 调用开发效率十分低下于是出现了各种不同的专门做资源管理的程序包为了解决这个问题Sun 公司也在其 Java SE 版本中正式提出了 Java 管理扩展(Java Management ExtensionsJMX)用来管理检测 Java 程序(同时 JMX 也在 JEE 中被发布)
JMX 的提出让 JDK 中开发自检测程序成为可能也提供了大量轻量级的检测 JVM 和运行中对象/线程的方式从而提高了 Java 语言自己的管理监测能力
JMX 和系统管理
管理系统(Management System)
要 了解 JMX我们就必须对当前的 IT 管理系统有一个初步的了解随着企业 IT 规模的不断增长IT 资源(IT resource)数量不断增加IT 资源的分布也越来越分散可以想象甚至对于一家只有几百台 PC 公司的 IT 管理人员来说分发一个安全补丁并且保证其在每台 PC 上的安装如果只依赖人工来完成那简直就是一场噩梦这样IT 管理系统就应运而生
然而CPU网卡存储阵列是 IT 资源OSMS OfficeOracle databaseIBM Websphere 也是 IT 资源IT 管理系统若要对这些 IT 资源进行管理就必须对这些管理对象有所了解形形色色的 IT 资源就像是说着不同语言的人Oralce 数据库表达内存紧张的方式和 Window XP 是绝然不同的 而 IT 管理系统就像建造通天塔的经理必须精通所有的语言 这几乎是一个不可能完成的任务难道 IT 管理系统是另外一个通天塔吗?当然不是!其实我们只要给每个 IT 资源配个翻译就可以了
管理系统的构架
图 管理系统构架
上图分析了管理系统的基本构架模式其中 Agent / SubAgent 起到的就是翻译的作用把 IT 资源报告的消息以管理系统能理解的方式传送出去
也许读者有会问为什么需要 Agent 和 SubAgent 两层体系呢?这里有两个现实的原因
管理系统一般是一个中央控制的控制软件而 SubAgent 直接监控一些资源往往和这些资源分布在同一物理位置当这些 SubAgent 把状态信息传输到管理系统或者传达管理系统的控制指令的时候需要提供一些网络传输的功能
管理系统的消息是有一定规范的消息的翻译本身是件复杂而枯燥的事情
一般来说管理系统会将同一物理分布或者功能类似的 SubAgent 分组成一组由一个共用的 Agent 加以管理在这个 Agent 里封装了 和 的功能
JMX 和管理系统
JMX 既是 Java 管理系统的一个标准一个规范也是一个接口一个框架图 展示了 JMX 的基本架构
图 JMX 构架
和其它的资源系统一样JMX 是管理系统和资源之间的一个接口它定义了管理系统和资源之间交互的标准javaxmanagementMBeanServer 实现了 Agent 的功能以标准的方式给出了管理系统访问 JMX 框架的接口而 javaxmanagementMBeans 实现了 SubAgent 的功能以标准的方式给出了 JMX 框架访问资源的接口而从类库的层次上看JMX 包括了核心类库 javalangmanagement 和 javaxmanagement 包javalangmanagement 包提供了基本的 VM 监控功能而 javaxmanagement 包则向用户提供了扩展功能
JMX 的基本框架
JMX 使用了 Java Bean 模式来传递信息一般说来JMX 使用有名的 MBean其内部包含了数据信息这些信息可能是应用程序配置信息模块信息系统信息统计信息等另外MBean 也可以设立可读写的属性直接操作某些函数甚至启动 MBean 可发送的 notification 等MBean 包括 StandardMXBeanDynamicModelOpen 等几种分类其中最简单是标准 MBean 和 MXBean而我们使用得最多的也是这两种MXBean 主要是 javalangmanagement 使用较多将在下一节中介绍我们先了解其他一些重要的 MBean 的种类
标准 MBean
标准 MBean 是最简单的一类 MBean与动态 Bean 不同它并不实现 javaxmanagement 包中的特殊的接口说它是标准 MBean 是因为其向外部公开其接口的方法和普通的 Java Bean 相同是通过 lexical或者说 coding convention 进行的下面我们就用一个例子来展现如何实现一个标准 MBean 来监控某个服务器 ServerImpl 状态的ServerImpl 代表了用来演示的某个 Server 的实现
package standardbeans;
public class ServerImpl {
public final long startTime;
public ServerImpl() {
startTime = SystemcurrentTimeMillis();
}
}
然后我们打算使用一个标准 MBeanServerMonitor 来监控 ServerImpl
package standardbeans;
public class ServerMonitor implements ServerMonitorMBean {
private final ServerImpl target;
public ServerMonitor(ServerImpl target){
thistarget = target;
}
public long getUpTime(){
return SystemcurrentTimeMillis() targetstartTime;
}
}
这里的 ServerMonitorBean 又是怎么回事呢?MXBean 规定了标准 MBean 也要实现一个接口所有向外界公开的方法都要在这个接口中声明否则管理系统就不能从中获得相应的信息此外该接口的名字也有一定的规范即在标准 MBean 类名之后加上MBean后缀若 MBean 的类名叫做 MBeansName 的话对应的接口就要叫做 MBeansNameMBean
对于管理系统来说这些在 MBean 中公开的方法最终会被 JMX 转化成属性(Attribute)监听(Listener)和调用(Invoke)的概念如果读者对 Java Bean 有一些了解的话不难看出public long getUpTime() 对应了 Bean 中的一个称为upTime的只读属性
下面我们就看一个模拟管理系统的例子
package standardbeans;
import javaxmanagementMBeanServer;
import javaxmanagementMBeanServerFactory;
import javaxmanagementObjectName;
public class Main {
private static ObjectName objectName ;
private static MBeanServer mBeanServer;
public static void main(String[] args) throws Exception{
init();
manage();
}
private static void init() throws Exception{
ServerImpl serverImpl = new ServerImpl();
ServerMonitor serverMonitor = new ServerMonitor(serverImpl);
mBeanServer = MBeanServerFactorycreateMBeanServer();
objectName = new ObjectName(objectName:id=ServerMonitor);
mBeanServerregisterMBean(serverMonitorobjectName);
}
private static void manage() throws Exception{
Long upTime = (Long) mBeanServergetAttribute(objectName
upTime);
Systemoutprintln(upTime);
}
}
JMX 的核心是 MBServerJava SE 已经提供了一个默认实现可以通过 MBServerFactorycreateMBeanServer() 获得每个资源监控者(MBean)一般都会有名称(ObjectName) 登记在 MBServer 内部的一个 Repository 中注意这个 ObjectName 对于每一个 MBServer 必须是唯一的只能对应于一个 MBean(读者有兴趣的话可以试着再给 mBeanServer 注册一个同名的 objectName看看会怎么样) 上述例子是在 init() 方法中完成向 MBeanServer 注册工作的
在管理过程中管理系统并不与资源或者 SubAgent 直接打交道也就是说这里不会直接引用到 MBean而是通过 MBeanServer 的 getAttribute 方法取得对应 MBean 的属性的
动态 MBean
但 是对于很多已有的 SubAgent 实现其 Coding Convention 并不符合标准 MBean 的要求重构所有这些 SubAgent 以符合标准 MBean 标准既费力也不实际JMX 中给出了动态(Dynamic) MBean 的概念MBServer 不再依据 Coding Convention 而是直接查询动态 MBean 给出的元数据(meta data)以获得 MBean 的对外接口
package dynamicbeans;
import javaxmanagement*;
import javalangreflect*;
public class ServerMonitor implements DynamicMBean {
private final ServerImpl target;
private MBeanInfo mBeanInfo;
public ServerMonitor(ServerImpl target){
thistarget = target;
}
//实现获取被管理的 ServerImpl 的 upTime
public long upTime(){
return SystemcurrentTimeMillis() targetstartTime;
}
//javaxmanagementMBeanServer 会通过查询 getAttribute(Uptime) 获得 Uptime 属性值
public Object getAttribute(String attribute) throws AttributeNotFoundException
MBeanException ReflectionException {
if(attributeequals(UpTime)){
return upTime();
}
return null;
}
//给出 ServerMonitor 的元信息
public MBeanInfo getMBeanInfo() {
if (mBeanInfo == null) {
try {
Class cls = thisgetClass();
//用反射获得 upTime 属性的读方法
Method readMethod = clsgetMethod(upTime new Class[]);
//用反射获得构造方法
Constructor constructor = clsgetConstructor(new Class[]
{ServerImplclass});
//关于 upTime 属性的元信息:名称为 UpTime只读属性(没有写方法)
MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo(
UpTime The time span since server start
readMethod null);
//关于构造函数的元信息
MBeanConstructorInfo mBeanConstructorInfo = new MBeanConstructorInfo(
Constructor for ServerMonitor constructor);
//ServerMonitor 的元信息为了简单起见在这个例子里
//没有提供 invocation 以及 listener 方面的元信息
mBeanInfo = new MBeanInfo(clsgetName()
Monitor that controls the server
new MBeanAttributeInfo[] { upTimeMBeanAttributeInfo }
new MBeanConstructorInfo[] { mBeanConstructorInfo }
null null);
} catch (Exception e) {
throw new Error(e);
}
}
return mBeanInfo;
}
public AttributeList getAttributes(String[] arg) {
return null;
}
public Object invoke(String arg Object[] arg String[] arg)
throws MBeanException
ReflectionException {
return null;
}
public void setAttribute(Attribute arg) throws AttributeNotFoundException
InvalidAttributeValueException MBeanException ReflectionException {
return;
}
public AttributeList setAttributes(AttributeList arg) {
return null;
}
}
其它动态 MBean
另外还有两类 MBeanOpen MBean 和 Model MBean实际上它们也都是动态 MBean
Open MBean 与其它动态 MBean 的唯一区别在于前者对其公开接口的参数和返回值有所限制 —— 只能是基本类型或者 javaxmanagementopenmbean 包内的 ArrayTypeCompositeTypeTarbularType 等类型这主要是考虑到管理系统的分布很可能远端管理系统甚至 MBServer 层都不具有 MBean 接口中特殊的类
Model Bean
然而普通的动态 Bean 通常缺乏一些管理系统所需要的支持比如持久化 MBean 的状态日志记录缓存等等如果让用户去一一实现这些功能确实是件枯燥无聊的工作为了减轻用户的负担JMX 提供商都会提供不同的 ModelBean 实现其中有一个接口是 Java 规范中规定所有厂商必须实现的javaxmanagementmodelmbeanRequiredModelBean 通过配置 Descriptor 信息我们可以定制这个 Model Bean 指定哪些 MBean 状态需要记入日志如何记录以及是否缓存某些属性缓存多久等等这里我们以 RequiredModelBean 为例讨论 ModelBean比如我们先来看一个例子首先是 server 端
package modelmbean;
public class Server {
private long startTime;
public Server() {}
public int start(){
startTime = SystemcurrentTimeMillis();
return ;
}
public long getUpTime(){
return SystemcurrentTimeMillis() startTime;
}
}
然后我们对它的监测如下
package modelmbean;
import javaxmanagement*;
import javaxmanagementmodelmbean*;
public class Main {
public static void main(String[] args) throws Exception{
MBeanServer mBeanServer = MBeanServerFactorycreateMBeanServer();
RequiredModelMBean serverMBean =
(RequiredModelMBean) mBeanServerinstantiate(
javaxmanagementmodelmbeanRequiredModelMBean);
ObjectName serverMBeanName =
new ObjectName(server: id=Server);
serverMBeansetModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
Server server = new Server();
serverMBeansetManagedResource(server ObjectReference);
ObjectInstance registeredServerMBean =
mBeanServerregisterMBean((Object) serverMBean serverMBeanName);
serverMBeaninvoke(startnull null);
Threadsleep();
Systemoutprintln(serverMBeangetAttribute(upTime));
Threadsleep();
Systemoutprintln(serverMBeangetAttribute(upTime));
}
private static ModelMBeanInfo getModelMBeanInfoForServer(ObjectName objectName)
throws Exception{
ModelMBeanAttributeInfo[] serverAttributes =
new ModelMBeanAttributeInfo[];
Descriptor upTime =
new DescriptorSupport(
new String[] {
name=upTime
descriptorType=attribute
displayName=Server upTime
getMethod=getUpTime
});
serverAttributes[] =
new ModelMBeanAttributeInfo(
upTime
long
Server upTime
true
false
false
upTime);
ModelMBeanOperationInfo[] serverOperations =
new ModelMBeanOperationInfo[];
Descriptor getUpTimeDesc =
new DescriptorSupport(
new String[] {
name=getUpTime
descriptorType=operation
class=modelmbeanServer
role=operation
});
MBeanParameterInfo[] getUpTimeParms = new MBeanParameterInfo[];
serverOperations[] = new ModelMBeanOperationInfo(getUpTime
get the up time of the server
getUpTimeParms
javalangLong
MBeanOperationInfoACTION
getUpTimeDesc);
Descriptor startDesc =
new DescriptorSupport(
new String[] {
name=start
descriptorType=operation
class=modelmbeanServer
role=operation
});
MBeanParameterInfo[] startParms = new MBeanParameterInfo[];
serverOperations[] = new ModelMBeanOperationInfo(start
start(): start server
startParms
javalangInteger
MBeanOperationInfoACTION
startDesc);
ModelMBeanInfo serverMMBeanInfo =
new ModelMBeanInfoSupport(
modelmbeanServer
ModelMBean for managing an Server
serverAttributes
null
serverOperations
null);
//Default strategy for the MBean
Descriptor serverDescription =
new DescriptorSupport(
new String[] {
(name= + objectName)
descriptorType=mbean
(displayName=Server)
type=modelmbeanServer
log=T
logFile=serverMXlog
currencyTimeLimit= });
serverMMBeanInfosetMBeanDescriptor(serverDescription);
return serverMMBeanInfo;
}
很明显和其它 MBean 类似使用 Model MBean 的过程也是下面几步
创建一个 MBServermBeanServe
获得管理资源用的 MBeanserverBean
给这个 MBean 一个 ObjectNameserverMBeanName
将 serverBean 以 serverMBeanName 注册到 mBeanServer 上去
唯一不同的是ModelMBean 需要额外两步:
serverMBeansetModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
serverMBeansetManagedResource(server ObjectReference);
第一步用于提供 serverMBean 的元数据主要包括以下两类
类似于普通的动态 MBean需要 MBean 的 AttributeInvocationNotification 的类型/反射信息诸如返回类型参数类型和相关的 get/set 方法等这里将不再赘述
关于缓存持久化以及日志等的策略后面我们将介绍一些这方面的信息
第二步指出了 ServerMBean 管理的对象也就是说从元数据中得到的 Method 将施加在哪个 Object 上需要指出的是 setManagedResource(Object o String type); 中第二个参数是 Object 类型可以是 ObjectReferenceHandleIOREJBHandle 或 RMIReference目前 SE 中的实现只支持 ObjectReference笔者认为后面几种类型是为了将来 JMX 管理对象扩展而设定的可能将来 Model Bean 不仅可以管理 Plain Java Object(POJO)还可能管理 Native Resource 并给诸如 EJB 和 RMI 对象的管理提供更多的特性
Model Bean 与普通动态 Bean 区别在于它的元数据类型 ModelMBeanInfo 扩展了前者的 MBeanInfo使得 ModelMBeanOperationInfoModelMBeanConstructor_Info ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一个额外的元数据javaxmanagementDescriptor 它是用来设定 Model Bean 策略的数据的存储是典型的 keyvalue 键值对不同的 Model Bean 实现以及不同的 MBeanFeatureInfo 支持不同的策略特性下面我们就以 Attribute 为例看一下 RequiredModelBean 支持的策略
首先它最重要的 Descriptor 主要是 namedisplayName 和 descriptorType其中 name 是属性名称name 要与对应 ModelMBeanAttributeInfo 的 name 相同descriptorType 必须是 attribute
另外valuedefault legalValues value 是用来设定初始值的default 指当不能从 resource 中获得该属性时的默认返回值legalValues 是一组合法的属性数据它并不用来保证 setAttribute 的数据一致性而是在 UI 系统如 JConsole 中提示用户可能的数据输入
在属性访问的 getMethod setMethod 方法上事实上所有对属性的访问都会被 delegate 给同一 MBeanInfo 中特定的 Operation getMethod/setMethod 给出了对应的 ModelMBeanOperationInfo 名称
还有一些额外的属性比如persistPolicy persistPeriod 是代表了持久化策略currencyTimeLimit lastUpdatedTimeStamp 缓存策略iterable 属性是否必须使用 iterate 来访问默认为否protocolMap 定义了与第三方系统有关的数据转换的 data modelvisibility 定义了与第三方 UI 系统有关的 MBean 如何显示的策略presentationString 也是定义了与第三方 UI 系统有关的 MBean 如何显示策略比如 presentation=servergif
事实上策略特性有两个层次的作用域整个 Model Bean 和特定的 MBeanFeature
Model Bean 的策略描述会被施加到该 Model Bean 的所有 MBeanFeature 上去除非该 MBeanFeature 重写了这个策略特性
在上面的例子里这一个语句
serverMMBeanInfosetMBeanDescriptor(serverDescription);
给整个serverMBeanInfo 设了一个策略描述 serverDescription其中用 currencyTimeLimit= 指出属性的缓存时间是 秒所以在 Main 方法中两次 serverMBeangetAttribute(upTime)之间的间隔小于 秒就会得到同样的缓存值
如果我们不想让 upTime 这个属性被缓存我们可以在它的策略描述中加入 currencyTimeLimit=:
Descriptor upTime = new DescriptorSupport(
new String[] {
name=upTime
descriptorType=attribute
displayName=Server upTime
getMethod=getUpTime
currencyTimeLimit= //不需要缓存
});
Descriptor getUpTimeDesc =
new DescriptorSupport(
new String[] {
name=getUpTime
descriptorType=operation
class=modelmbeanServer
role=operation
currencyTimeLimit= //不需要缓存
});
getUpTimeDesc 也要改动的原因是 RequiredModelBean 会把获取 upTime 属性的工作 delegate 给 getUpTime invocation只要其中一处使用 MBean 级的缓存策略就没法获得实时 upTime 数据了
虚拟机检测
JMX 与虚拟机检测
JMX 的提出为Java 虚拟机提供了 Java 层上的检测机制JSE 中新提出的 javalangmanagement 包即是 JMX 在 JDK 的一个应用它提供了大量的有用的接口通过 MBean 方式提供了对 Java 虚拟机和运行时远端的监控和检测方式来帮助用户来检测本地或者远端的虚拟机的运行情况有了 JMX 之后我们可以设计一个客户端来检测远端一个正在运行的虚拟机中的线程数线程当前的 Stack内存管理GC 所占用的时间虚拟机中的对象和当前虚拟机参数等重要的参数和运行时信息JMX 另外的一个重要功能是对配置信息的检测和再配置比如我们可以在远端查看和修改当前 JVM 的 verbose 参数以达到动态管理的目的甚至我们可以在远端指挥 JVM 做一次 GC这在下文中有详细介绍
JMX 提供的虚拟机检测 API
检测虚拟机当前的状态总是 Java 开放人员所关心的也正是因为如此出现了大量的 profiler 工具来检测当前的虚拟机状态从 Java SE 之后在 JDK 中我们有了一些 Java 的虚拟机检测 API即 javalangmanagement 包Management 包里面包括了许多 MXBean 的接口类和 LockInfoMemoryUsageMonitorInfo 和 ThreadInfo 等类从名字可以看出该包提供了虚拟机内存分配垃圾收集(GC)情况操作系统层线程调度和共享锁甚至编译情况的检测机制这样一来Java 的开发人员就可以很简单地为自己做一些轻量级的系统检测来确定当前程序的各种状态以便随时调整
要获得这些信息我们首先通过 javalangmanagementManagementFactory 这个工厂类来获得一系列的 MXBean包括
ClassLoadingMXBean
ClassLoadMXBean 包括一些类的装载信息比如有多少类已经装载/卸载(unloaded)虚拟机类装载的 verbose 选项(即命令行中的 Java –verbose:class 选项)是否打开还可以帮助用户打开/关闭该选项
CompilationMXBean
CompilationMXBean 帮助用户了解当前的编译器和编译情况该 mxbean 提供的信息不多
GarbageCollectorMXBean
相 对于开放人员对 GC 的关注程度来说该 mxbean 提供的信息十分有限仅仅提供了 GC 的次数和 GC 花费总时间的近似值但是这个包中还提供了三个的内存管理检测类MemoryManagerMXBeanMemoryMXBean 和 MemoryPoolMXBean
MemoryManagerMXBean
这个类相对简单提供了内存管理类和内存池(memory pool)的名字信息
MemoryMXBean
这个类提供了整个虚拟机中内存的使用情况包括 Java 堆(heap)和非 Java 堆所占用的内存提供当前等待 finalize 的对象数量它甚至可以做 gc(实际上是调用 Systemgc)
MemoryPoolMXBean
该 信息提供了大量的信息在 JVM 中可能有几个内存池因此有对应的内存池信息因此在工厂类中getMemoryPoolMXBean() 得到是一个 MemoryPoolMXBean 的 list每一个 MemoryPoolMXBean 都包含了该内存池的详细信息如是否可用当前已使用内存/最大使用内存值以及设置最大内存值等等
OperatingSystemMXBean
该类提供的是操作系统的简单信息如构架名称当前 CPU 数最近系统负载等
RuntimeMXBean
运行时信息包括当前虚拟机的名称提供商版本号以及 classpathbootclasspath 和系统参数等等
ThreadMXBean
在 Java 这个多线程的系统中对线程的监控是相当重要的ThreadMXBean 就是起到这个作用ThreadMXBean 可以提供的信息包括各个线程的各种状态CPU 占用情况以及整个系统中的线程状况从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象这个对象中则包含了这个线程的所有信息
javalangmanagement 和虚拟机的关系
我们知道management 和底层虚拟机的关系是非常紧密的其实有一些的是直接依靠虚拟机提供的公开 API 实现的比如 JVMTI而另外一些则不然很大一块都是由虚拟机底层提供某些不公开的 API / Native Code 提供的这样的设计方式保证了 management 包可以提供足够的信息并且使这些信息的提供又有足够的效率也使 management 包和底层的联系非常紧密
Java 中的 API 改进
Management 在 Java SE 被提出之后受到了欢迎在 Java 当中这个包提供更多的 API 来更好地提供信息
OperatingSystemMXBean getSystemLoadAverage()
Java 程序通常关注是虚拟机内部的负载内存等状况而不考虑整个系统的状况但是很多情况下Java 程序在运行过程中整个计算机系统的系统负荷情况也会对虚拟机造成一定的影响随着 Java 的发展Java 程序已经覆盖了各个行业这一点也必须得到关注在以前利用 Native 代码来检测系统负载往往是唯一的选择但是在 Java 当中JDK 自己提供了一个轻量级的系统负载检测 API即 OperatingSystemMXBeangetSystemLoadAverage()
当然这个 API 事实上仅仅返回一个对前一分钟系统负载的简单的估测它设计的主要目标是简单快速地估测当前系统负荷因此它首先保证了这个 API 的效率是非常高的也因为如此这个 API 事实上并不适用于所有的系统
锁检测
我们知道同步是 Java 语言很重要的一个特性在 Java SE 中最主要的同步机制是依靠 synchronize 关键字对某一个对象加锁实现的在 Java SE 之后的版本中concurrent 包的加入大大强化了 Java 语言的同步能力concurrent 提供了很多不同类型的锁机制可供扩展因此要更好地观测当前的虚拟机状况和不同线程的运行态去观察虚拟机中的各种锁以及线程与锁的关系是非常必要 的很可惜的是在过去的 JDK 中我们并没有非常方便的 API 以供使用一个比较直接的检测方式是查看线程的 stack trace更为强大全面(但是也更复杂并且效率低下)的方案是得到一个 VM 所有对象的快照并查找之这些策略的代价都比较大而且往往需要编写复杂的 Native 代码
JDK 里提供了一些相当简单的 API 来提供这个服务首先了解两个新类LockInfo 和 MonitorInfo 这两个类承载了锁的信息LockInfo 可以是任何的 Java 锁包括简单 Java 锁和 ncurrent 包中所使用的锁(包括 AbstractOwnableSynchronizer 和 Condition 的实现类/子类)而 MonitorInfo 是简单的 Java 对象所代表的锁要检测一个线程所拥有的锁和等待的锁首先要得到一个线程的 ThreadInfo然后可以简单地调用
getLockedMonitors()
返回一个所有当前线程已经掌握的锁对象的列表
getLockedSynchronizers()
对于使用 concurrent 包的线程返回一个该线程所掌握的ownable synchronizer(即 AbstractOwnableSynchronizer 及其子类)所组成的列表
getLockInfo()
当前线程正在等待的那个锁对象的信息就可以知道线程所有的锁信息通过这些锁信息我们很方便的可以知道当前虚拟机的所有线程的锁信息由此我们还可以推导出更多的信息
死锁检测
死锁检测一直以来是软件工程师所重视的显然一个死锁的系统永远是工程师最大的梦魇Java 程序的死锁检测也一直以来是 Java 程序员所头痛的为了解决线程间死锁问题一般都有预防(代码实现阶段)和死锁后恢复(运行时)两种方式以前 Java 程序员都重视前者因为在运行态再来检测和恢复系统是相当麻烦的缺少许多必要的信息但是对于一些比较复杂的系统采取后者或者运行时调试死锁信息也 是非常重要的由上面所说现在我们已经可以知道每一个线程所拥有和等待的锁因此要计算出当前系统中是否有死锁的线程也是可行的了当然Java 里面也提供了一个 API 来完成这个功能即
ThreadMXBeanfindDeadlockedThreads()
这个函数的功能就是检测出当前系统中已经死锁的线程当然这个功能复杂因此比较费时基本上仅仅将之用于调试以便对复杂系统线程调用的改进
未来的发展
JMX 在 Java SE / 中的功能已经相当强大但是距离 Java 程序开发人员的要求还是有一段距离因此 Sun 公司已经向 JCP 提出了 JSR (JMX API 版本)来扩充和进一步发展 JMX并希望这个 JSR 将在 Java SE 中实现在这个文档中新的 JMX 将着重于
对 management 模型的优化并提供更好的支持加入了比如 annotation 等等的新特性
对 JMX 定义的优化在进一步强化 MBean 扩充性好的优点的同时尽量改变(用户普遍认为的)MBean 很难实现的缺点
对非 Java 平台客户端的支持这将是一个令人振奋的新特性
具体的扩展可能包括
层次性的命名域(Hierarchical namespace)
新的事件服务功能
对 locales 的新支持
为 MBean 启用 annotation 服务
也可以使用用户类型的 mapping 了
可以看到JMX 的进一步发展主要关注的是可扩展性动态性和易用性等 Java 用户非常关注的方面
总结
在Java SE 出现的 JMX 在 Java SE 中有了更多的功能和扩展能力这很好地适应了 Java 语言的发展和用户的要求让 Java 的监测管理的的功能更加强大