在Java或者Spring Boot日常开发过程中,因为Java的自动垃圾回收机制,我们可能很少关注JVM的信息,面临性能问题时,我们可能也会更多的借助JProfiler等第三方工具进行问题分析。
比如我们需要做一个程序运行健康监控功能,我们可能就需要获取Jvm的信息,下面我们就看一下如何在Java中获取Jvm信息。
首先我们定义两个公共方法,分别是用来将毫秒转换成时分秒,另外一个方法是将时间戳转换成时分秒毫秒。
/**
* 毫秒转换成时分秒
*
* @param uptime 毫秒
* @return 时分秒
*/
protected static String toDuration(double uptime) {
NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
uptime /= 1000;
if (uptime < 60) {
return fmtD.format(uptime) + " 秒";
}
uptime /= 60;
if (uptime < 60) {
long minutes = (long) uptime;
return fmtI.format(minutes) + "分";
}
uptime /= 60;
if (uptime < 24) {
long hours = (long) uptime;
long minutes = (long)((uptime - hours) * 60);
String s = fmtI.format(hours) + "时";
if (minutes != 0) {
s += " " + fmtI.format(minutes) + "分";
}
return s;
}
uptime /= 24;
long days = (long) uptime;
long hours = (long)((uptime - days) * 24);
String s = fmtI.format(days) + "天";
if (hours != 0) {
s += " " + fmtI.format(hours) + "时";
}
return s;
}
/**
* 时间戳钻日期
*
* @param timeStamp 时间戳
* @return 日期
*/
protected static String timeStamp2Date(long timeStamp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
return simpleDateFormat.format(timeStamp);
}
可以获取jvm的名称、jvm版本、jvm制造商、java版本、jvm启动时间、jvm运行时间等信息
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
System.out.printf("jvm名称:%s %n", runtimeMXBean.getVmName());
System.out.printf("jvm版本:%s %n", runtimeMXBean.getVmVersion());
System.out.printf("jvm制造商:%s %n", runtimeMXBean.getVmVendor());
System.out.printf("java版本:%s %n", System.getProperty("java.version"));
System.out.printf("jvm启动时间:%s %n", timeStamp2Date(runtimeMXBean.getStartTime()));
System.out.printf("jvm运行时间:%s %n", toDuration(runtimeMXBean.getUptime()));
可以获取编译器名称、编译耗时及是否支持及时编译器编译监控。
CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean();
System.out.printf("编译器名称:%s %n", compilationMXBean.getName());
System.out.printf("编译耗时:%s %n", toDuration(compilationMXBean.getTotalCompilationTime()));
System.out.printf("是否支持及时编译器编译监控:%s %n", compilationMXBean.isCompilationTimeMonitoringSupported());
可以获取总线程数、守护进程线程数、峰值线程数、Java虚拟机启动后创建并启动的线程总数、是否支持测量当前线程的CPU时间等信息。
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
System.out.printf("总线程数(守护+非守护):%s %n", threadMXBean.getThreadCount());
System.out.printf("守护进程线程数:%s %n", threadMXBean.getDaemonThreadCount());
System.out.printf("峰值线程数:%s %n", threadMXBean.getPeakThreadCount());
System.out.printf("Java虚拟机启动后创建并启动的线程总数:%s %n", threadMXBean.getTotalStartedThreadCount());
System.out.printf("是否支持测量当前线程的CPU时间:%s %n", threadMXBean.isCurrentThreadCpuTimeSupported());
System.out.printf("当前线程的总CPU时间:%s %n", threadMXBean.getCurrentThreadCpuTime());
System.out.printf("当前线程在用户模式中执行的CPU时间:%s %n", threadMXBean.getCurrentThreadUserTime());
可以获取初始化堆内存、已使用堆内存、可使用堆内存、最大堆内存等信息
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
System.out.printf("初始化堆内存:%sm %n", heapMemoryUsage.getInit() / 1024 / 1024);
System.out.printf("已使用堆内存:%sm %n", heapMemoryUsage.getUsed() / 1024 / 1024);
System.out.printf("可使用堆内存:%sm %n", heapMemoryUsage.getCommitted() / 1024 / 1024);
System.out.printf("最大堆内存:%sm %n", heapMemoryUsage.getMax() / 1024 / 1024);
为了方便查询数据,我们可以手工编译、运行代码
javac JvmInfo.java
java -Xmx20m JvmInfo
也可以获取非堆内存,也就是永久代的信息。需要注意,JDK1.7之后已经废弃了永久代的概念,使用元空间进行替代
MemoryUsage noHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
System.out.printf("初始化非堆内存:%sm %n", noHeapMemoryUsage.getInit() / 1024 / 1024);
System.out.printf("已使用非堆内存:%sm %n", noHeapMemoryUsage.getUsed() / 1024 / 1024);
System.out.printf("可使用非堆内存:%sm %n", noHeapMemoryUsage.getCommitted() / 1024 / 1024);
System.out.printf("最大非堆内存:%sm %n", noHeapMemoryUsage.getMax() / 1024 / 1024);
可以获取操作系统名称、操作系统版本、处理器数量、系统平均负载、电脑架构等信息
OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
System.out.printf("操作系统名称:%s %n", operatingSystemMXBean.getName());
System.out.printf("操作系统版本:%s %n", operatingSystemMXBean.getVersion());
System.out.printf("可用处理器数量%s %n", operatingSystemMXBean.getAvailableProcessors());
System.out.printf("系统平均负载:%f %n", operatingSystemMXBean.getSystemLoadAverage());
System.out.printf("电脑架构:%s %n", operatingSystemMXBean.getArch());
可获取当前加载类数量、未加载类数量、总加载类数量
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
System.out.printf("当前加载类数量%s %n", classLoadingMXBean.getLoadedClassCount());
System.out.printf("未加载类数量%s %n", classLoadingMXBean.getUnloadedClassCount());
System.out.printf("总加载类数量%s %n", classLoadingMXBean.getTotalLoadedClassCount());
也可以获取内存池对象
public static void main(String[] args) {
List < MemoryPoolMXBean > memoryPoolMXBeanList = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean memoryMXBean: memoryPoolMXBeanList) {
final String kind = memoryMXBean.getType().name();
final MemoryUsage usage = memoryMXBean.getUsage();
System.out.println(
"内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(memoryMXBean.getName()) + ", jvm." +
memoryMXBean.getName() + ".init(初始化):" + bytesToMB(usage.getInit()));
System.out.println(
"内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(memoryMXBean.getName()) + ", jvm." +
memoryMXBean.getName() + ".used(已使用): " + bytesToMB(usage.getUsed()));
System.out.println(
"内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(memoryMXBean.getName()) + ", jvm." +
memoryMXBean.getName() + ".committed(可使用):" + bytesToMB(usage.getCommitted()));
System.out.println(
"内存模型: " + getKindName(kind) + ", 内存空间名称: " + getPoolName(memoryMXBean.getName()) + ", jvm." +
memoryMXBean.getName() + ".max(最大):" + bytesToMB(usage.getMax()));
}
}
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 (bytes / 1024 / 1024) + " MB";
}
评论 (0)