一 什么是JMX
JMX(英语:Java Management Extensions,即Java管理扩展)是Java平台上为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
以上是维基百科的介绍
JMX即Java Management Extensions(Java管理扩展),是Java SE的一部分,在Java2的时候加入到Java SE平台中,但Java5才正式发布。
JMX提供了一个非常简单的途径去管理应用程序的资源,这里的所说的资源包括内存资源,磁盘资源等,而且因为JMX相关技术是动态的,所以可以在应用程序运行时监控和管理资源。
想必大家都听说过 jconsole或jvisualvm,它们就是用jmx实现的。
二 MBean
具体可参阅 oracle官网
MBean是类似于JavaBeans组件的托管Java对象,它遵循JMX规范中阐明的设计模式。
MBean可以代表设备,应用程序或任何需要管理的资源。MBean公开了一个包含以下内容的管理接口:
一组可读或可写的属性,或两者兼而有之。
一组可调用的操作。
自我描述。
在MBean实例的整个生命周期中,管理界面都不会更改。当某些预定义事件发生时,MBeans也可以发出通知。
JMX规范定义了五种类型的MBean:
-
Standard MBeans
-
Dynamic MBeans
-
Open MBeans
-
Model MBeans
-
MXBeans
下面介绍了一种特殊的MBean类型,称为MXBeans。
一个的MXBean是一种MBean的只引用一组预定义的数据类型。
这样,您可以确保您的MBean可被任何客户端(包括远程客户端)使用,而无需客户端有权访问代表MBean类型的特定于模型的类。MXBean提供了一种方便的方法来将相关值捆绑在一起,而无需将客户端特别配置为处理捆绑。
以与标准MBean相同的方式,通过编写称为的Java接口SomethingMXBean和实现该接口的Java类来定义MXBean 。
但是,与标准MBean不同,MXBean不需要调用Java类Something。接口中的每个方法都定义MXBean中的属性或操作。注释@MXBean也可以用于注释Java接口,而不是要求接口名称后跟MXBean后缀。
软件包中的Java 2 Platform Standard Edition(J2SE)5.0软件中存在MXBean java.lang.management。
包里提供了许多MXBean的接口类,可以很方便的获取到JVM的内存、GC、线程、锁、class、甚至操作系统层面的各种信息,本文就简单的介绍 一种利用JMX对JAVA进程进行CPU、堆内存使用的监控。
咱们经常使用jsconsole中的(概述/内存/线程/类/vm概要)就是通过java.lang.management中的定义的MXBean 获取到的。·
三 获取MXBean
获取MXBean有本地调用和jmxrmi(远程调用)两种方式,通过ManagementFactory(java.lang.management.ManagementFactory)工厂类获取。
ManagementFactory类是用于获取Java平台的受管Bean的工厂类。 该类由静态方法组成,每个方法返回一个或多个表示Java虚拟机组件的管理接口的平台MXBeans 。
1 本地调用
本地调用自能获取当前程序所运行的jvm所加载的MXBean信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//获取内存
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
//获取编译信息
CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean();
//获取系统信息
OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
//获取运行时信息
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
//获取线程信息
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
//获取类加载信息
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
//获取内存池的管理(如堆,元空间)信息
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
//获取gc信息
List<GarbageCollectorMXBean> garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans();
2 jmxrmi远程调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49//远程服务器地址
final String HOSTNAME = "172.36.0.231";
//远程服务器端口
final int PORT = 60001;
//1.构建JMX API连接器服务器的初始化地址
String serviceURL = "service:jmx:rmi:///jndi/rmi://" + HOSTNAME + ":" + PORT + "/jmxrmi";
JMXServiceURL jmxServiceURL = new JMXServiceURL(serviceURL);
//2.初始化客户端JMX API连接器,创建与给定地址的连接器服务器的连接。
JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, null);
//3.启动管理bean服务器连接,获取远程MBean服务器的 MBeanServerConnection对象
//MBeanServerConnection 此接口表示与MBean服务器通信的方式,无论是本地还是远程。 代表本地MBean服务器的MBeanServer接口扩展了此接口。
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
//4.拿到MBeanServerConnection后就可以通过它来获取对应的MXBean
//获取内存
MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy
(mBeanServerConnection, ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class);
//获取编译信息
CompilationMXBean compilationMXBean = ManagementFactory.newPlatformMXBeanProxy
(mBeanServerConnection, ManagementFactory.COMPILATION_MXBEAN_NAME, CompilationMXBean.class);
//获取系统信息
OperatingSystemMXBean systemMXBean = ManagementFactory.newPlatformMXBeanProxy(
mBeanServerConnection,
ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME,
com.sun.management.OperatingSystemMXBean.class
);
//获取运行时信息
RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy(
mBeanServerConnection,
ManagementFactory.RUNTIME_MXBEAN_NAME,
RuntimeMXBean.class
);
//获取线程信息
ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy(
mBeanServerConnection,
ManagementFactory.THREAD_MXBEAN_NAME,
ThreadMXBean.class
);
//获取类加载信息
ClassLoadingMXBean classLoading = ManagementFactory.newPlatformMXBeanProxy(
mBeanServerConnection,
ManagementFactory.CLASS_LOADING_MXBEAN_NAME,
ClassLoadingMXBean.class
);
//获取内存信息
List<MemoryPoolMXBean> memoryPool = ManagementFactory.getPlatformMXBeans(mBeanServerConnection, MemoryPoolMXBean.class);
//获取gc信息
List<GarbageCollectorMXBean> garbageCollectors = ManagementFactory.getPlatformMXBeans(mBeanServerConnection, GarbageCollectorMXBean.class);
四 MXBean使用
这个基本就是些api的调用,除了 获取cpu使用情况/gc垃圾回收活动情况需要少量计算,其他信息基本都可以拿到。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251package com.example.demo.jmx;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.CompilationMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import com.sun.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class HeapMain {
private static NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
private static NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
public static void main(String[] args) throws InterruptedException {
//运行时情况
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
//操作系统情况
com.sun.management.OperatingSystemMXBean os = ManagementFactory.getPlatformMXBean(com.sun.management.OperatingSystemMXBean.class);
//线程使用情况
ThreadMXBean threads = ManagementFactory.getThreadMXBean();
//堆内存使用情况
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
//非堆内存使用情况
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
//类加载情况
ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
//内存池对象
List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
//编译器和编译情况
CompilationMXBean cm = ManagementFactory.getCompilationMXBean();
//获取GC对象
List<GarbageCollectorMXBean> gcmList = ManagementFactory.getGarbageCollectorMXBeans();
//运行时情况
System.out.printf("jvm.name (JVM名称-版本号-供应商):%s | version: %s | vendor: %s %n", runtime.getVmName(), runtime.getVmVersion(), runtime.getVmVendor());
System.out.printf("jvm.spec.name (JVM规范名称-版本号-供应商):%s | version: %s | vendor: %s %n", runtime.getSpecName(), runtime.getSpecVersion(), runtime.getSpecVendor());
System.out.printf("jvm.java.version (JVM JAVA版本):%s%n", System.getProperty("java.version"));
System.out.printf("jvm.start.time (Java虚拟机的启动时间):%s%n", toDuration(runtime.getStartTime()));
System.out.printf("jvm.uptime (Java虚拟机的正常运行时间):%s%n", runtime.getInputArguments());
System.out.printf("jvm.uptime (Java虚拟机的正常运行时间):%s%n", toDuration(runtime.getUptime()));
System.out.printf("jvm.getInputArguments (JVM 启动参数):%s%n", runtime.getInputArguments());
System.out.printf("jvm.getSystemProperties (获取系统属性):%s%n", runtime.getSystemProperties());
//获取cpu使用情况/gc垃圾回收活动情况
Thread thread = new Thread(() -> showCpu(runtime, os, gcmList));
thread.start();
System.out.println("------------------------------------------------------------------------------------------------------");
//编译情况
System.out.printf("compilation.name(编译器名称):%s%n",cm.getName());
System.out.printf("compilation.total.time(编译器耗时):%d毫秒%n",cm.getTotalCompilationTime());
boolean isSupport=cm.isCompilationTimeMonitoringSupported();
if(isSupport){
System.out.println("支持即时编译器编译监控");
}else{
System.out.println("不支持即时编译器编译监控");
}
System.out.printf("------------------------------------------------------------------------------------------------------");
//JVM 线程情况
System.out.printf("jvm.threads.total.count (总线程数(守护+非守护)):%d%n", threads.getThreadCount());
System.out.printf("jvm.threads.daemon.count (守护进程线程数):%d%n", threads.getDaemonThreadCount());
System.out.printf("jvm.threads.peak.count (峰值线程数):%d%n", threads.getPeakThreadCount());
System.out.printf("jvm.threads.total.start.count(Java虚拟机启动后创建并启动的线程总数):%d%n", threads.getTotalStartedThreadCount());
for(Long threadId : threads.getAllThreadIds()) {
System.out.printf("threadId: %d | threadName: %s%n", threadId, threads.getThreadInfo(threadId).getThreadName());
}
System.out.println("------------------------------------------------------------------------------------------------------");
//获取GC信息
for (GarbageCollectorMXBean collectorMXBean : gcmList) {
System.out.printf("collectorMXBean.getCollectionCount(%s 垃圾回收器执行次数):%d%n",collectorMXBean.getName(), collectorMXBean.getCollectionCount());
System.out.printf("collectorMXBean.getCollectionTime(%s 垃圾回收器执行时间):%d%n",collectorMXBean.getName(), collectorMXBean.getCollectionTime());
}
System.out.println("------------------------------------------------------------------------------------------------------");
//堆内存情况
System.out.printf("jvm.heap.init (初始化堆内存):%s %n", bytesToMB(heapMemoryUsage.getInit()));
System.out.printf("jvm.heap.used (已使用堆内存):%s %n", bytesToMB(heapMemoryUsage.getUsed()));
System.out.printf("jvm.heap.committed (可使用堆内存):%s %n", bytesToMB(heapMemoryUsage.getCommitted()));
System.out.printf("jvm.heap.max (最大堆内存):%s %n", bytesToMB(heapMemoryUsage.getMax()));
System.out.println("------------------------------------------------------------------------------------------------------");
//非堆内存使用情况
System.out.printf("jvm.noheap.init (初始化非堆内存):%s %n", bytesToMB(nonHeapMemoryUsage.getInit()));
System.out.printf("jvm.noheap.used (已使用非堆内存):%s %n", bytesToMB(nonHeapMemoryUsage.getUsed()));
System.out.printf("jvm.noheap.committed (可使用非堆内存):%s %n", bytesToMB(nonHeapMemoryUsage.getCommitted()));
System.out.printf("jvm.noheap.max (最大非堆内存):%s %n", bytesToMB(nonHeapMemoryUsage.getMax()));
System.out.println("------------------------------------------------------------------------------------------------------");
//系统概况
System.out.printf("os.name(操作系统名称-版本号):%s %s %s %n", os.getName(), "version", os.getVersion());
System.out.printf("os.arch(操作系统内核):%s%n", os.getArch());
System.out.printf("os.cores(可用的处理器数量):%s %n", os.getAvailableProcessors());
System.out.printf("os.loadAverage(系统负载平均值):%s %n", os.getSystemLoadAverage());
System.out.println("------------------------------------------------------------------------------------------------------");
//类加载情况
System.out.printf("class.current.load.count(当前加载类数量):%s %n", cl.getLoadedClassCount());
System.out.printf("class.unload.count(未加载类数量):%s %n", cl.getUnloadedClassCount());
System.out.printf("class.total.load.count(总加载类数量):%s %n", cl.getTotalLoadedClassCount());
System.out.println("------------------------------------------------------------------------------------------------------");
for(MemoryPoolMXBean pool : pools) {
final String kind = pool.getType().name();
final MemoryUsage usage = pool.getUsage();
System.out.println("内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(pool.getName()) + ", jvm." + pool.getName() + ".init(初始化):" + bytesToMB(usage.getInit()));
System.out.println("内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(pool.getName()) + ", jvm." + pool.getName() + ".used(已使用): " + bytesToMB(usage.getUsed()));
System.out.println("内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(pool.getName()) + ", jvm." + pool.getName()+ ".committed(可使用):" + bytesToMB(usage.getCommitted()));
System.out.println("内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(pool.getName()) + ", jvm." + pool.getName() + ".max(最大):" + bytesToMB(usage.getMax()));
System.out.println("------------------------------------------------------------------------------------------------------");
}
thread.join();
}
private static void showCpu(RuntimeMXBean runtime,OperatingSystemMXBean os,List<GarbageCollectorMXBean> gcmList) {
//上一个cpu运行记录时间点
long prevUpTime = runtime.getUptime();
//当时cpu运行时间
long upTime;
//上一次cpu运行总时间
long prevProcessCpuTime = os.getProcessCpuTime();
//当前cpu运行总时间
long processCpuTime;
//上一次gc运行总时间
long prevProcessGcTime = getTotalGarbageCollectionTime(gcmList);
//当前gc运行总时间
long processGcTime;
//可用内核数量
int processorCount =os.getAvailableProcessors();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
int i =10;
while (i>0){
processCpuTime = os.getProcessCpuTime();
processGcTime = getTotalGarbageCollectionTime(gcmList);
upTime = runtime.getUptime();
long upTimeDiff = upTime - prevUpTime;
//计算cpu使用率
long processTimeDiff = processCpuTime - prevProcessCpuTime;
//processTimeDiff 取到得是纳秒数 1ms = 1000000ns
double cpuDetail = processTimeDiff * 100.0 /1000000/ processorCount / upTimeDiff;
//计算gccpu使用率
long processGcTimeDiff = processGcTime - prevProcessGcTime;
double gcDetail = processGcTimeDiff * 100.0 /1000000/ processorCount / upTimeDiff;
System.out.printf("cpu使用率:%s ,gc使用率:%s %n",cpuDetail,gcDetail);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
prevProcessCpuTime = processCpuTime;
prevUpTime = upTime;
prevProcessGcTime =processGcTime;
i--;
}
}
/**
* 垃圾回收期总耗时
*/
protected static long getTotalGarbageCollectionTime(List<GarbageCollectorMXBean> gcmList) {
long total = -1L;
for (GarbageCollectorMXBean collectorMXBean : gcmList) {
total+=collectorMXBean.getCollectionTime();
}
return total;
}
protected static String getKindName(String kind) {
if("NON_HEAP".equals(kind)) {
return "NON_HEAP(非堆内存)";
}else {
return "HEAP(堆内存)";
}
}
protected static String getPoolName(String poolName) {
switch (poolName) {
case "Code Cache":
return poolName +"(代码缓存区)";
case "Metaspace":
return poolName +"(元空间)";
case "Compressed Class Space":
return poolName +"(类指针压缩空间)";
case "PS Eden Space":
return poolName +"(伊甸园区)";
case "PS Survivor Space":
return poolName +"(幸存者区)";
case "PS Old Gen":
return poolName +"(老年代)";
default:
return poolName;
}
}
protected static String bytesToMB(long bytes) {
return fmtI.format((long)(bytes / 1024 / 1024)) + " MB";
}
protected static String printSizeInKb(double size) {
return fmtI.format((long) (size / 1024)) + " kbytes";
}
protected static String toDuration(double uptime) {
uptime /= 1000;
if (uptime < 60) {
return fmtD.format(uptime) + " seconds";
}
uptime /= 60;
if (uptime < 60) {
long minutes = (long) uptime;
String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
return s;
}
uptime /= 60;
if (uptime < 24) {
long hours = (long) uptime;
long minutes = (long) ((uptime - hours) * 60);
String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
if (minutes != 0) {
s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
}
return s;
}
uptime /= 24;
long days = (long) uptime;
long hours = (long) ((uptime - days) * 24);
String s = fmtI.format(days) + (days > 1 ? " days" : " day");
if (hours != 0) {
s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
}
return s;
}
}
五 自己来实现一个 jconsole或jvisualvm
掌握了api后,掌握一点前端技能,或者swing,JavaFXML可视化技能,基本就可以来自己实现一个 jconsole或jvisualvm。
下面是我用vue(近年来很火的前端框架)+echarts(前端可视化库)+springBoot实现的一个简陋的jvisualvm,感兴趣的可与去看看 源码
TODO
对 jmx 进行封装,实现一个内存等基本信息的管理工具。
参考资料
https://blog.csdn.net/AndCo/article/details/107412285
https://github.com/AndsGo/JmsWeb