检测虚拟机当前的状态总是 Java 开放人员所关心的,也正是因为如此,出现了大量的 profiler 工具来检测当前的虚拟机状态。从 Java SE 5 之后,在 JDK 中,我们有了一些 Java 的虚拟机检测 API,即 java.lang.management
包。Management 包里面包括了许多 MXBean 的接口类和 LockInfo、MemoryUsage、MonitorInfo 和 ThreadInfo 等类。从名字可以看出,该包提供了虚拟机内存分配、垃圾收集(GC)情况、操作系统层、线程调度和共享锁,甚至编译情况的检测机制。这样一来,Java 的开发人员就可以很简单地为自己做一些轻量级的系统检测,来确定当前程序的各种状态,以便随时调整。
要获得这些信息,我们首先通过 java.lang.management.ManagementFactory
这个工厂类来获得一系列的 MXBean。包括:
- ClassLoadingMXBean
ClassLoadMXBean 包括一些类的装载信息,比如有多少类已经装载 / 卸载(unloaded),虚拟机类装载的 verbose 选项(即命令行中的 Java – verbose:class 选项)是否打开,还可以帮助用户打开 / 关闭该选项。
- CompilationMXBean
CompilationMXBean 帮助用户了解当前的编译器和编译情况,该 mxbean 提供的信息不多。
- GarbageCollectorMXBean
相对于开放人员对 GC 的关注程度来说,该 mxbean 提供的信息十分有限,仅仅提供了 GC 的次数和 GC 花费总时间的近似值。但是这个包中还提供了三个的内存管理检测类:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
- MemoryManagerMXBean
这个类相对简单,提供了内存管理类和内存池(memory pool)的名字信息。
- MemoryMXBean
这个类提供了整个虚拟机中内存的使用情况,包括 Java 堆(heap)和非 Java 堆所占用的内存,提供当前等待 finalize 的对象数量,它甚至可以做 gc(实际上是调用 System.gc)。
- MemoryPoolMXBean
该信息提供了大量的信息。在 JVM 中,可能有几个内存池,因此有对应的内存池信息,因此,在工厂类中,getMemoryPoolMXBean() 得到是一个 MemoryPoolMXBean 的 list。每一个 MemoryPoolMXBean 都包含了该内存池的详细信息,如是否可用、当前已使用内存 / 最大使用内存值、以及设置最大内存值等等。
- MemoryManagerMXBean
- OperatingSystemMXBean
该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。
- RuntimeMXBean
运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。
- ThreadMXBean
在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个线程的所有信息。
我们知道,management 和底层虚拟机的关系是非常紧密的。其实,有一些的是直接依靠虚拟机提供的公开 API 实现的,比如 JVMTI;而另外一些则不然,很大一块都是由虚拟机底层提供某些不公开的 API / Native Code 提供的。这样的设计方式,保证了 management 包可以提供足够的信息,并且使这些信息的提供又有足够的效率;也使 management 包和底层的联系非常紧密。
package com.easyway.space.commons.systems;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.CompilationMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryManagerMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
import java.lang.management.MonitorInfo;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
/**
* 系统监控工具
* management 和底层虚拟机的关系是非常紧密的。其实,有一些的是直接依靠虚拟机提供的公
* 开 API 实现的,比如 JVMTI;而另外一些则不然,很大一块都是由虚拟机底层提供某些不公开
* 的 API / Native Code 提供的。这样的设计方式,保证了 management 包可以提供足够的信
* 息,并且使这些信息的提供又有足够的效率;也使 management 包和底层的联系非常紧密。
* @author longgangbai
*
*/
public class SystemUtils {
/**
*ClassLoadingMXBean ClassLoadMXBean 包括一些类的装载信息,
* 比如有多少类已经装载 / 卸载(unloaded),
* 虚拟机类装载的 verbose 选项(即命令行中的 Java – verbose:class 选项)是否打开,
* 还可以帮助用户打开 / 关闭该选项。
*/
public static void getVMClassInformation(){
ClassLoadingMXBean classLoadMXBean=ManagementFactory.getClassLoadingMXBean();
int loadClazzCount=classLoadMXBean.getLoadedClassCount();
System.out.println("加载类的数量:"+loadClazzCount);
long hasloadClazzCount=classLoadMXBean.getTotalLoadedClassCount();
System.out.println("已经加载类的数量:"+hasloadClazzCount);
long hasUnloadClazzCount=classLoadMXBean.getUnloadedClassCount();
System.out.println("尚未加载类的数量:"+hasUnloadClazzCount);
boolean isVerbose=classLoadMXBean.isVerbose();
System.out.println("是否开始加载类信息:"+isVerbose);
//CompilationMXBean 帮助用户了解当前的编译器和编译情况,该 mxbean 提供的信息不多。
CompilationMXBean compilationMXBean=ManagementFactory.getCompilationMXBean();
String jitName=compilationMXBean.getName();
System.out.println("即时编译的名称:"+jitName);
long totalCompileTime=compilationMXBean.getTotalCompilationTime();
System.out.println("总的编译时间:"+totalCompileTime+"/s");
boolean isSupport=compilationMXBean.isCompilationTimeMonitoringSupported();
if(isSupport){
System.out.println("支持即时编译器编译监控");
}else{
System.out.println("不支持即时编译器编译监控");
}
List<GarbageCollectorMXBean> gcMXBeanList=ManagementFactory.getGarbageCollectorMXBeans();
//相对于开放人员对 GC 的关注程度来说,该 mxbean 提供的信息十分有限,仅仅提供了 GC 的次数和 GC 花费总时间的近似值。
for (GarbageCollectorMXBean gcMXBean : gcMXBeanList) {
//内存池名称
String[] poolNames=gcMXBean.getMemoryPoolNames();
for (String poolName : poolNames) {
System.out.println("poolNames="+poolName);
}
}
//提供了内存管理类和内存池(memory pool)的名字信息。
List<MemoryManagerMXBean> memoryMgrMXBeanList=ManagementFactory.getMemoryManagerMXBeans();
//内存管理器的信息
for (MemoryManagerMXBean memoryManagerMXBean : memoryMgrMXBeanList) {
String[] poolNames=memoryManagerMXBean.getMemoryPoolNames();
for (String poolName : poolNames) {
System.out.println("poolNames="+poolName);
}
}
//内存信息
MemoryMXBean memoryMXBean=ManagementFactory.getMemoryMXBean();
//java堆得使用情况信息
MemoryUsage heapMemoryUsage=memoryMXBean.getHeapMemoryUsage();
long usaged=heapMemoryUsage.getUsed();
System.out.println("java 内存堆使用内存:"+usaged);
long maxUsage=heapMemoryUsage.getMax();
System.out.println("java 内存堆最大使用内存:"+maxUsage);
long initUsage=heapMemoryUsage.getInit();
System.out.println("java 内存堆初始化时占用内存:"+initUsage);
List<MemoryPoolMXBean> memoryPoolMXBeanList=ManagementFactory.getMemoryPoolMXBeans();
//该信息提供了大量的信息。在 JVM 中,可能有几个内存池,因此有对应的内存池信息,因此,在工厂类中
//,getMemoryPoolMXBean() 得到是一个 MemoryPoolMXBean 的 list。每一个 MemoryPoolMXBean 都包含
//了该内存池的详细信息,如是否可用、当前已使用内存 / 最大使用内存值、以及设置最大内存值等等。
for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeanList) {
//内存池的名称
String poolName=memoryPoolMXBean.getName();
//内存管理器的名称
String[] memoryMgrNames=memoryPoolMXBean.getMemoryManagerNames();
for (String mgrName : memoryMgrNames) {
System.out.println("内存管理器的名称:"+mgrName);
}
//java JVM最近内存的使用情况
MemoryUsage memoryUsage=memoryPoolMXBean.getCollectionUsage();
System.out.println("内存池的收集器内存使用率:"+memoryUsage.getUsed()/memoryUsage.getMax()+"%");
memoryPoolMXBean.getCollectionUsageThreshold();
memoryPoolMXBean.getCollectionUsageThresholdCount();
MemoryType memoryType=memoryPoolMXBean.getType();
System.out.println("内存的信息:"+memoryType.name());
MemoryUsage memoryUage=memoryPoolMXBean.getUsage();
System.out.println("内存池的内存使用率:"+memoryUage.getUsed()/memoryUage.getMax()+"%");
memoryPoolMXBean.getUsageThreshold();
System.out.println();
memoryPoolMXBean.getUsageThresholdCount();
System.out.println();
}
//该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。
OperatingSystemMXBean operateSystemMBean=ManagementFactory.getOperatingSystemMXBean();
String operateName=operateSystemMBean.getName();
System.out.println("操作系统的名称:"+operateName);
int processListCount=operateSystemMBean.getAvailableProcessors();
System.out.println("操作系统的进程数:"+processListCount);
String osArchName=operateSystemMBean.getArch();//System.getProperty("os.arch");
System.out.println("操作系统的架构:"+osArchName);
double loadAverage=operateSystemMBean.getSystemLoadAverage();
System.out.println("操作系统的负载均衡信息:"+loadAverage);
String versionName=operateSystemMBean.getVersion();//System.getProperty("os.version");
System.out.println("操作系统的版本号码:"+versionName);
//运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。
RuntimeMXBean runtimeMXBean=ManagementFactory.getRuntimeMXBean();
String vmName=runtimeMXBean.getVmName();
System.out.println("虚拟机的名称:"+vmName);
String vmVersion=runtimeMXBean.getVmVersion();
System.out.println("虚拟机的版本:"+vmVersion);
Map<String, String> sysMaps=runtimeMXBean.getSystemProperties();
Set<Entry<String,String>> keyLists=sysMaps.entrySet();
for (Entry<String, String> entry : keyLists) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
String vmVendor=runtimeMXBean.getVmVendor();
System.out.println("系统的供应商的名称:"+vmVendor);
//类加载器的路径
String clazzPath=runtimeMXBean.getClassPath();//System.getProperty("java.class.path")}
System.out.println("操作系统的类加载器的名称:"+clazzPath);
List<String> argsList=runtimeMXBean.getInputArguments();
System.out.println("操作系统的参数信息");
for (String args : argsList) {
System.out.println(" "+args);
}
String libPath=runtimeMXBean.getLibraryPath();// System.getProperty("java.library.path")
System.out.println("java 的类库路径:"+libPath );
String specVersion=runtimeMXBean.getManagementSpecVersion();
System.out.println("实施运行Java虚拟机管理接口规范 的版本"+specVersion);
String specName=runtimeMXBean.getSpecName();
System.out.println("规范的名称:"+specName);
String specVender=runtimeMXBean.getSpecVendor();
System.out.println("规范管理接口供应商 :"+specVender);
long startTime=runtimeMXBean.getStartTime();
System.out.println("java 虚拟机的开始启动的时间:"+startTime);
runtimeMXBean.getSpecVersion();
System.out.println("规范接口版本::"+operateName);
String bottClassPath =runtimeMXBean.getBootClassPath();
System.out.println("操作系统的bootstrap 的classloader:"+bottClassPath);
//在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。
//ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线
//程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个
//线程的所有信息。
//线程的信息
ThreadMXBean threadMXBean=ManagementFactory.getThreadMXBean();
//所有的线程的编号
long[] threadIds=threadMXBean.getAllThreadIds();
for (long threadId : threadIds) {
//线程的信息
ThreadInfo threadInfo=threadMXBean.getThreadInfo(threadId);
//线程被阻塞的数量
threadInfo.getBlockedCount();
//被锁定线程的监控信息
MonitorInfo[] monitorInfos=threadInfo.getLockedMonitors();
for (MonitorInfo monitorInfo : monitorInfos) {
int depth=monitorInfo.getLockedStackDepth();
System.out.println("锁定的程度:"+depth);
}
//异步锁定的信息
LockInfo[] lockinfos=threadInfo.getLockedSynchronizers();
//锁定的信息
for (LockInfo lockInfo : lockinfos) {
System.out.println("锁定类的名称:"+lockInfo.getClassName());
}
//线程的名称
String threadName=threadInfo.getThreadName();
System.out.println("线程的名称:"+threadName);
Thread.State state=threadInfo.getThreadState();
System.out.println("线程的信息:"+state.name());
}
long cpuTime=threadMXBean.getCurrentThreadCpuTime();
long curentTime=threadMXBean.getCurrentThreadUserTime();
long threadCount=threadMXBean.getDaemonThreadCount();
long peakliveThreadCount=threadMXBean.getPeakThreadCount();
long threadCounts=threadMXBean.getThreadCount();
System.out.println("当前处于live状态的线程总的数量:"+threadCounts);
long totalThreadCount=threadMXBean.getTotalStartedThreadCount();
System.out.println("JVM 启动之后,总的自动线程数量:"+totalThreadCount);
}
/**
*
* @return
*/
public static final String getHeapMemoryUsage() {
ClassLoadingMXBean mxbean;
return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()
.getUsed()
+ "/"
+ ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()
.getMax();
}
/**
* 当前使用内存的百分比
* @return
*/
public static final String getHeapMemoryUsagePercent() {
return (((double) ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage().getUsed() / (double) ManagementFactory
.getMemoryMXBean().getHeapMemoryUsage().getMax()) * 100L)
+ "%";
}
/**
* 当前非java堆占用的百分比
* @return
*/
public static final String getNonHeapMemoryUsage() {
return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()
.getUsed()
+ "/"
+ ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()
.getMax();
}
/**
*
* @return
*/
public static final String getNonHeapMemoryUsagePercent() {
return (((double) ManagementFactory.getMemoryMXBean()
.getNonHeapMemoryUsage().getUsed() / (double) ManagementFactory
.getMemoryMXBean().getNonHeapMemoryUsage().getMax()) * 100)
+ "%";
}
/**
* 获取线程数
* @return
*/
public static final String getThreadCount() {
return "" + ManagementFactory.getThreadMXBean().getThreadCount();
}
}
Jsp页面:
<%@taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@page import="java.lang.management.ManagementFactory"%>
<%@page import="java.lang.management.MemoryUsage"%>
<%@page import="java.lang.management.ClassLoadingMXBean"%>
<%@page import="java.lang.management.CompilationMXBean"%>
<%@page import="java.lang.management.MemoryMXBean"%>
<%@page import="java.lang.management.RuntimeMXBean"%>
<%@page import="java.lang.management.ThreadMXBean"%>
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
double oneMB =1024*1024;
String color = "blue";
MemoryMXBean memoryMXBean=ManagementFactory.getMemoryMXBean();
ClassLoadingMXBean classLoadMXBean=ManagementFactory.getClassLoadingMXBean();
out.println("<label style=\"color: "+color+"\">===加载类的情况====</label><br />");
out.println("加载类的总数量:"+classLoadMXBean.getTotalLoadedClassCount()+"个;<br />");
out.println("已加载类的数量:"+classLoadMXBean.getLoadedClassCount()+"个;<br />");
out.println("未加载类的数量:"+classLoadMXBean.getUnloadedClassCount()+"个;<br />");
out.println("是否开始加载类信息:"+classLoadMXBean.isVerbose()+";<br /><br />");
//CompilationMXBean 帮助用户了解当前的编译器和编译情况,该 mxbean 提供的信息不多。
out.println("<label style=\"color: "+color+"\">===编译情况====</label><br />");
CompilationMXBean compilationMXBean=ManagementFactory.getCompilationMXBean();
out.println("即时编译名称:"+compilationMXBean.getName()+";<br />");
out.println("总的编译时间:"+compilationMXBean.getTotalCompilationTime()+"s;<br />");
boolean isSupport=compilationMXBean.isCompilationTimeMonitoringSupported();
if(isSupport){
out.println("是否支持监控:"+"支持即时编译器编译监控;<br /><br />");
}else{
out.println("是否支持监控:"+"不支持即时编译器编译监控;<br /><br />");
}
//堆得使用情况信息
MemoryUsage heapMemoryUsage=memoryMXBean.getHeapMemoryUsage();
out.println("<label style=\"color: "+color+"\">===堆内存使用情况====</label><br />");
out.println("当前使用内存:"+heapMemoryUsage.getUsed()/oneMB+"M;<br />");
out.println("最大使用内存:"+heapMemoryUsage.getMax()/oneMB+"M;<br />");
out.println("初始化时内存:"+heapMemoryUsage.getInit()/oneMB+"M;<br /><br />");
//java 非堆内存的使用情况
out.println("<label style=\"color: "+color+"\">===非堆内存的使用情况====</label><br />");
MemoryMXBean memeoryMX = ManagementFactory.getMemoryMXBean();
out.println("当前使用内存:"+memeoryMX.getNonHeapMemoryUsage().getUsed()/oneMB+"M;<br />");
out.println("最大使用内存:"+memeoryMX.getNonHeapMemoryUsage().getMax()/oneMB+"M;<br />");
out.println("初始化时内存:"+memeoryMX.getNonHeapMemoryUsage().getInit()/oneMB+"M;<br /><br />");
//线程的信息
ThreadMXBean threadMXBean=ManagementFactory.getThreadMXBean();
out.println("<label style=\"color: "+color+"\">===线程的信息====</label><br />");
out.println("当前处于live状态的线程总的数量:"+threadMXBean.getThreadCount()+"个;<br />");
out.println("JVM启动之后 ,总的自动线程数量:"+threadMXBean.getTotalStartedThreadCount()+"个;<br /><br />");
out.println("<label style=\"color: "+color+"\">===服务器信息====</label><br />");
RuntimeMXBean runtimeMX = ManagementFactory.getRuntimeMXBean();
request.setAttribute("startTime", new Date(runtimeMX.getStartTime()));
out.println("服务启动时间:"+new Date(runtimeMX.getStartTime())+";<br /><br />");
%>