为了及时了解java进程运行时的内存、线程、垃圾收集以及堆空间和操作系统的内存情况,在代码中使用一个单独的线程,将相关信息记录下来,并且循环写入文件中。这样一来,即使java进程被杀掉,在java进程结束前的运行时状态信息也已经持久化到文件中了。
虽然进程启动时,已经设置了内存空间溢出时进行堆dump,但是如果此时整个操作系统的空间太小,而对空间有比较大的情况下,也容易导致堆dump失败。
总之,在缺乏完善的监控机制前提下,适时将程序的运行时信息记录到文件中,出现意外终止时,有助于帮助分析可能原因,以便为后续规避此类问题提供帮助。
package com.demo;
import com.sun.management.OperatingSystemMXBean;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadMXBean;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class JVMRuntimeInfoDemo {
private static String timeMinutes;
private static ArrayList<String> buff;
private static Long index = 1l;
public static void main(String[] args) throws InterruptedException {
LinkedList<String> queue = new LinkedList<String>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdfMinutes = new SimpleDateFormat("yyyyMMdd-HHmmss");
Long time = System.currentTimeMillis();
timeMinutes = sdfMinutes.format(time);
buff = new ArrayList<>(1024);
for (int i = 0; i < 60; i++) {
byte[] bytes = new byte[1024000];
buff.add(bytes.toString());
String dateTime = sdf.format(System.currentTimeMillis());
saveRuntimeInfo(queue);
Thread.sleep(3000);
}
}
public static void saveRuntimeInfo(LinkedList<String> queue) {
String fileName = "runtime" + timeMinutes + ".log";
String filePath = getConfFile(fileName);
try {
File confFile = new File(filePath);
if (!confFile.exists()) {
confFile.createNewFile();
}
FileOutputStream outputStream = new FileOutputStream(confFile);
String configInfo = "";
// 记录操作系统内存
configInfo = getOSMemoryInfo(configInfo);
// 记录虚拟机内存
configInfo = getJVMMemoryInfo(configInfo);
// 堆内存信息
configInfo = getHeapInfo(configInfo);
// 垃圾收集信息
configInfo = getGCInfo(configInfo);
// 线程信息
configInfo = getThreadInfo(configInfo);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateTime = sdf.format(System.currentTimeMillis());
// String poll = queue.poll();
configInfo += "\n" + index + " " + dateTime + "\n\n";
index = index + 1;
if (index >= 100) {
index = 1l;
}
System.out.println(configInfo);
queue.addLast(configInfo);
while (queue.size() > 30) {
queue.removeFirst();
}
// 遍历
for (String poll : queue) {
outputStream.write(poll.getBytes(StandardCharsets.UTF_8));
}
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取日志文件路径
public static String getConfFile(String fileName) {
URL resource1 = JVMRuntimeInfoDemo.class.getResource("/");
String path = resource1.getPath();
int endPos = path.indexOf(".jar!");
if (endPos > 0)
path = path.substring(0, endPos);
endPos = path.lastIndexOf("/");
if (path.startsWith("file:/"))
path = path.substring(6, endPos);
else if (path.startsWith("/"))
path = path.substring(1, endPos);
path = path + "/" + fileName;
return path;
}
public static String getOSMemoryInfo(String baseInfo) {
int byteToMb = 1024 * 1024;
// 操作系统级内存情况查询
OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
String os = System.getProperty("os.name");
long physicalFree = osmxb.getFreePhysicalMemorySize() / byteToMb;
long physicalTotal = osmxb.getTotalPhysicalMemorySize() / byteToMb;
long physicalUse = physicalTotal - physicalFree;
baseInfo = baseInfo + "\n======操作系统内存信息=======";
baseInfo = baseInfo + "\n操作系统的版本:" + os;
baseInfo = baseInfo + "\n操作系统内存已使用:" + physicalFree + " MB";
baseInfo = baseInfo + "\n操作系统内存剩余:" + physicalUse + " MB";
baseInfo = baseInfo + "\n操作系统总内存:" + physicalTotal + " MB";
return baseInfo;
}
public static String getJVMMemoryInfo(String baseInfo) {
// 虚拟机级内存情况查询
long vmFree = 0;
long vmUse = 0;
long vmTotal = 0;
long vmMax = 0;
int byteToMb = 1024 * 1024;
Runtime rt = Runtime.getRuntime();
vmTotal = rt.totalMemory() / byteToMb;
vmFree = rt.freeMemory() / byteToMb;
vmMax = rt.maxMemory() / byteToMb;
vmUse = vmTotal - vmFree;
baseInfo = baseInfo + "\n======JVM内存信息=======";
baseInfo += "\nJVM内存已用的空间为:" + vmUse + " MB";
baseInfo += "\n" + "JVM内存的空闲空间为:" + vmFree + " MB";
// jvm当前分配空间
baseInfo += "\n" + "JVM总内总存空间为:" + vmTotal + " MB";
// 与jvm配置有关
baseInfo += "\n" + "JVM总内存最大空间为:" + vmMax + " MB";
return baseInfo;
}
// 堆空间信息
public static String getHeapInfo(String baseInfo) {
int byteToMb = 1024 * 1024;
MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
MemoryUsage usage = memorymbean.getHeapMemoryUsage();
baseInfo = baseInfo + "\n======堆内存信息=======";
baseInfo += "\n初始堆大小:" + usage.getInit() / byteToMb + "MB";
baseInfo += "\n最大值:" + usage.getMax() / byteToMb + "MB";
baseInfo += "\n已使用:" + usage.getUsed() / byteToMb + "MB";
return baseInfo;
}
// 垃圾收集信息
public static String getGCInfo(String baseInfo) {
List<GarbageCollectorMXBean> gcmList = ManagementFactory.getGarbageCollectorMXBeans();
baseInfo = baseInfo + "\n======垃圾收集信息=======";
for (GarbageCollectorMXBean gcm : gcmList) {
baseInfo += "\n垃圾收集器名称:" + gcm.getName();
long collectionTime = gcm.getCollectionTime();
long collectionCount = gcm.getCollectionCount();
baseInfo += "\n垃圾收集次数:" + collectionCount + " 次,垃圾收集时间:" + collectionTime + "毫秒";
}
return baseInfo;
}
// 线程信息
public static String getThreadInfo(String baseInfo) {
ThreadMXBean tm = (ThreadMXBean) ManagementFactory.getThreadMXBean();
baseInfo = baseInfo + "\n======线程信息=======";
baseInfo += "\n线程数:" + tm.getThreadCount();
baseInfo += "\n最大线程数:" + tm.getPeakThreadCount();
baseInfo += "\n守护线程数:" + tm.getDaemonThreadCount();
return baseInfo;
}
}
执行程序,输出结果类似如下:
======操作系统内存信息=======
操作系统的版本:Windows 10
操作系统内存已使用:7350 MB
操作系统内存剩余:8917 MB
操作系统总内存:16267 MB
======JVM内存信息=======
JVM内存已用的空间为:9 MB
JVM内存的空闲空间为:52 MB
JVM总内总存空间为:61 MB
JVM总内存最大空间为:61 MB
======堆内存信息=======
初始堆大小:64MB
最大值:61MB
已使用:9MB
======垃圾收集信息=======
垃圾收集器名称:PS Scavenge
垃圾收集次数:2 次,垃圾收集时间:5毫秒
垃圾收集器名称:PS MarkSweep
垃圾收集次数:0 次,垃圾收集时间:0毫秒
======线程信息=======
线程数:5
最大线程数:5
守护线程数:4
31 2022-02-14 14:55:56