1. VisualVM 用户界面概览
1.1 主界面布局
┌─────────────────────────────────────────────────────────────────────────────┐
│ 菜单栏 (Menu Bar) │
├─────────────────────────────────────────────────────────────────────────────┤
│ 工具栏 (Toolbar) │
├─────────────────┬───────────────────────────────────────────────────────────┤
│ │ │
│ 应用程序树 │ │
│ (Applications │ 主视图区域 │
│ Tree) │ (Main View Area) │
│ │ │
│ ┌─────────────┐│ ┌─────────────────────────────────────────────────────┐ │
│ │ Local ││ │ │ │
│ │ ├─ PID 1234││ │ 监控/分析视图 │ │
│ │ ├─ PID 5678││ │ (Monitoring/Profiling Views) │ │
│ │ └─ PID 9012││ │ │ │
│ │ ││ │ │ │
│ │ Remote ││ │ │ │
│ │ └─ server1 ││ │ │ │
│ │ ││ │ │ │
│ │ Snapshots ││ │ │ │
│ │ ├─ heap1 ││ │ │ │
│ │ └─ thread1 ││ │ │ │
│ └─────────────┘│ └─────────────────────────────────────────────────────┘ │
├─────────────────┴───────────────────────────────────────────────────────────┤
│ 状态栏 (Status Bar) │
└─────────────────────────────────────────────────────────────────────────────┘
1.2 界面组件详解
1.2.1 菜单栏 (Menu Bar)
File 菜单: - Load:加载快照文件 - Export:导出数据 - Exit:退出应用程序
Edit 菜单: - Find:查找功能 - Copy:复制数据 - Select All:全选
View 菜单: - Applications Window:显示/隐藏应用程序窗口 - Toolbars:工具栏设置 - Status Bar:状态栏显示
Tools 菜单: - Options:全局设置 - Plugins:插件管理 - Platform:平台工具
Window 菜单: - Reset Windows:重置窗口布局 - Configure Window:配置窗口
Help 菜单: - Contents:帮助文档 - About:关于信息
1.2.2 工具栏 (Toolbar)
[🏠] [🔄] [📊] [🔍] [⚙️] [❓]
Home Refresh Monitor Search Settings Help
- Home:返回主页
- Refresh:刷新当前视图
- Monitor:快速监控
- Search:搜索功能
- Settings:快速设置
- Help:帮助信息
1.2.3 应用程序树 (Applications Tree)
Local 节点: - 显示本地运行的 Java 进程 - 自动发现和刷新 - 显示进程 PID 和主类名
Remote 节点: - 远程 JVM 连接 - 需要手动添加 - 支持 JMX 连接
Snapshots 节点: - 保存的快照文件 - 堆转储文件 - 线程转储文件 - 性能分析结果
1.3 主视图区域
主视图区域采用标签页设计,支持多个视图同时打开:
┌─────────────────────────────────────────────────────────────┐
│ [Overview] [Monitor] [Threads] [Sampler] [Profiler] [MBeans]│
├─────────────────────────────────────────────────────────────┤
│ │
│ 当前选中的视图内容 │
│ │
└─────────────────────────────────────────────────────────────┘
2. 应用程序连接和管理
2.1 本地应用程序连接
2.1.1 自动发现
VisualVM 会自动发现本地运行的 Java 应用程序:
# 启动一个测试应用程序
java -cp . TestApplication
# VisualVM 会在 Local 节点下显示这个进程
# 格式:进程名 (PID)
# 例如:TestApplication (12345)
2.1.2 连接到应用程序
双击连接:
- 在应用程序树中双击进程
- 自动打开 Overview 标签页
右键菜单:
右键点击进程 → 选择操作 ├── Open ├── Thread Dump ├── Heap Dump ├── Profile ├── Properties └── Kill Application
2.1.3 应用程序信息查看
// 创建一个示例应用程序用于演示
public class MonitoringDemo {
private static final int THREAD_COUNT = 5;
private static final int MEMORY_SIZE = 1000000;
public static void main(String[] args) throws InterruptedException {
System.out.println("Monitoring Demo Started");
// 创建多个线程
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new WorkerTask("Worker-" + i)).start();
}
// 主线程持续运行
while (true) {
// 分配一些内存
allocateMemory();
Thread.sleep(5000);
}
}
private static void allocateMemory() {
// 创建一些对象来观察内存使用
java.util.List<String> list = new java.util.ArrayList<>();
for (int i = 0; i < MEMORY_SIZE; i++) {
list.add("Data-" + i);
}
// 模拟一些处理时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
static class WorkerTask implements Runnable {
private final String name;
public WorkerTask(String name) {
this.name = name;
}
@Override
public void run() {
while (true) {
try {
// 模拟工作负载
doWork();
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void doWork() {
// 模拟 CPU 密集型工作
double result = 0;
for (int i = 0; i < 100000; i++) {
result += Math.sqrt(i) * Math.sin(i);
}
System.out.println(name + " completed work: " + result);
}
}
}
2.2 远程应用程序连接
2.2.1 配置远程 JVM
启动远程应用程序:
# 基本 JMX 配置
java -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Djava.rmi.server.hostname=192.168.1.100 \
YourApplication
# 带认证的配置
java -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.ssl=true \
-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password \
-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access \
YourApplication
创建认证文件:
# jmxremote.password
monitorRole password123
controlRole password456
# jmxremote.access
monitorRole readonly
controlRole readwrite
# 设置文件权限
chmod 600 jmxremote.password
chmod 644 jmxremote.access
2.2.2 在 VisualVM 中添加远程连接
添加远程主机:
右键点击 "Remote" → "Add Remote Host" 输入主机名或 IP 地址:192.168.1.100
添加 JMX 连接:
右键点击远程主机 → "Add JMX Connection" 连接字符串:service:jmx:rmi:///jndi/rmi://192.168.1.100:9999/jmxrmi 用户名:monitorRole 密码:password123
高级连接配置: “`bash
自定义连接脚本
#!/bin/bash
remote-connect.sh
HOST=”$1” PORT=“$2” USER=“$3” PASS=“$4”
if [[ -z $HOST || -z $PORT ]]; then
echo “Usage: $0
CONNECTION_URL=“service:jmx:rmi:///jndi/rmi://$HOST:$PORT/jmxrmi”
echo “Connecting to: $CONNECTION_URL”
if [[ -n $USER && -n $PASS ]]; then echo “Using authentication: $USER” fi
# 可以在这里添加更多的连接逻辑
### 2.3 应用程序管理
#### 2.3.1 应用程序属性查看
右键点击应用程序 → Properties
显示信息包括: ├── 基本信息 │ ├── 进程 ID (PID) │ ├── 主类名 │ ├── 启动时间 │ └── 运行时间 ├── JVM 信息 │ ├── Java 版本 │ ├── JVM 供应商 │ ├── JVM 参数 │ └── 类路径 ├── 系统属性 │ ├── 操作系统信息 │ ├── 用户信息 │ └── 环境变量 └── JVM 参数 ├── 堆内存设置 ├── 垃圾收集器 └── 其他 JVM 选项
#### 2.3.2 应用程序控制
**生成转储文件**:
```bash
# 通过 VisualVM 界面
右键点击应用程序 → Thread Dump # 生成线程转储
右键点击应用程序 → Heap Dump # 生成堆转储
# 通过命令行(备用方法)
jstack <PID> > thread_dump.txt
jmap -dump:format=b,file=heap_dump.hprof <PID>
终止应用程序:
右键点击应用程序 → Kill Application
注意:这会强制终止进程,可能导致数据丢失
3. Overview 标签页详解
3.1 Overview 界面布局
┌─────────────────────────────────────────────────────────────┐
│ Overview │
├─────────────────────────────────────────────────────────────┤
│ 应用程序信息 (Application Information) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ PID: 12345 Started: 2024-01-15 10:30 │ │
│ │ Host: localhost Arguments: -Xmx2g -server │ │
│ │ Main class: com.example.App JVM: OpenJDK 11.0.2 │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ JVM 参数 (JVM Arguments) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ -Xms512m -Xmx2g -XX:+UseG1GC -server │ │
│ │ -Dfile.encoding=UTF-8 -Djava.awt.headless=true │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 系统属性 (System Properties) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ java.version: 11.0.2 │ │
│ │ os.name: Linux │ │
│ │ user.dir: /home/user/project │ │
│ │ [Show All Properties...] │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
3.2 应用程序信息解读
3.2.1 基本信息
进程信息: - PID:进程标识符,用于系统级操作 - Host:运行主机,本地显示为 localhost - Started:应用程序启动时间 - Main class:主类名,应用程序入口点
JVM 信息: - JVM 版本:Java 虚拟机版本和供应商 - JVM 模式:Client 或 Server 模式 - 架构:32位或64位
3.2.2 JVM 参数分析
内存参数:
-Xms512m # 初始堆大小
-Xmx2g # 最大堆大小
-XX:NewRatio=3 # 新生代与老年代比例
-XX:MaxMetaspaceSize=256m # 元空间最大大小
垃圾收集器参数:
-XX:+UseG1GC # 使用 G1 垃圾收集器
-XX:+UseParallelGC # 使用并行垃圾收集器
-XX:+UseConcMarkSweepGC # 使用 CMS 垃圾收集器
-XX:MaxGCPauseMillis=200 # 最大 GC 暂停时间
调试和监控参数:
-XX:+PrintGC # 打印 GC 信息
-XX:+PrintGCDetails # 打印详细 GC 信息
-XX:+HeapDumpOnOutOfMemoryError # OOM 时生成堆转储
-Dcom.sun.management.jmxremote # 启用 JMX 远程监控
3.3 系统属性查看
3.3.1 重要系统属性
Java 相关属性:
java.version=11.0.2
java.vendor=Eclipse Adoptium
java.home=/usr/lib/jvm/java-11-openjdk
java.class.path=/app/lib/*:/app/classes
java.library.path=/usr/lib/x86_64-linux-gnu
操作系统属性:
os.name=Linux
os.version=5.4.0-74-generic
os.arch=amd64
file.separator=/
path.separator=:
line.separator=\n
用户和目录属性:
user.name=appuser
user.home=/home/appuser
user.dir=/app
user.timezone=Asia/Shanghai
file.encoding=UTF-8
3.3.2 自定义属性查看
// 在应用程序中设置自定义属性
public class PropertyDemo {
public static void main(String[] args) {
// 设置自定义系统属性
System.setProperty("app.name", "MyApplication");
System.setProperty("app.version", "1.0.0");
System.setProperty("app.environment", "production");
System.setProperty("app.config.file", "/etc/myapp/config.properties");
// 打印所有系统属性
System.getProperties().forEach((key, value) -> {
if (key.toString().startsWith("app.")) {
System.out.println(key + "=" + value);
}
});
// 保持应用程序运行
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4. Monitor 标签页详解
4.1 Monitor 界面布局
┌─────────────────────────────────────────────────────────────┐
│ Monitor │
├─────────────────────────────────────────────────────────────┤
│ CPU 使用率图表 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CPU Usage (%) │ │
│ │ 100 ┤ │ │
│ │ 80 ┤ ██ │ │
│ │ 60 ┤ ████ │ │
│ │ 40 ┤ ██████ │ │
│ │ 20 ┤████████ │ │
│ │ 0 └────────────────────────────────────────────────── │ │
│ │ 0 1 2 3 4 5 6 7 8 9 10 │ │
│ │ Time (minutes) │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 内存使用图表 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Memory Usage (MB) │ │
│ │2048 ┤ │ │
│ │1536 ┤ │ │
│ │1024 ┤ ████████████████████████████████████████████ │ │
│ │ 512 ┤ ████████████████████████████████████████████████ │ │
│ │ 0 └────────────────────────────────────────────────── │ │
│ │ 0 1 2 3 4 5 6 7 8 9 10 │ │
│ │ Heap: 1024MB Used: 512MB Max: 2048MB │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 类和线程信息 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Classes: 8,432 loaded, 0 unloaded │ │
│ │ Threads: 25 live, 28 peak │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.2 CPU 监控
4.2.1 CPU 使用率指标
CPU 使用率类型: - Process CPU:当前进程的 CPU 使用率 - System CPU:整个系统的 CPU 使用率 - GC CPU:垃圾收集占用的 CPU 时间
CPU 使用率计算:
// 获取 CPU 使用率的示例代码
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
public class CPUMonitor {
private static final OperatingSystemMXBean osBean =
ManagementFactory.getOperatingSystemMXBean();
public static void main(String[] args) throws InterruptedException {
while (true) {
// 获取系统 CPU 使用率
double systemCpuLoad = osBean.getSystemCpuLoad();
// 获取进程 CPU 使用率
double processCpuLoad = osBean.getProcessCpuLoad();
System.out.printf("System CPU: %.2f%%, Process CPU: %.2f%%\n",
systemCpuLoad * 100, processCpuLoad * 100);
Thread.sleep(5000);
}
}
}
4.2.2 CPU 使用率分析
正常 CPU 使用模式: - 稳定型:CPU 使用率相对稳定,波动较小 - 周期型:CPU 使用率呈现周期性变化 - 突发型:偶尔出现 CPU 使用率峰值
异常 CPU 使用模式: - 持续高使用率:可能存在死循环或计算密集型操作 - 频繁波动:可能存在资源竞争或线程调度问题 - 异常峰值:可能存在性能瓶颈或算法问题
4.3 内存监控
4.3.1 内存区域详解
堆内存 (Heap Memory):
堆内存结构:
┌─────────────────────────────────────────────────────────┐
│ Java Heap │
├─────────────────────────────────────────────────────────┤
│ 新生代 (Young Generation) │
│ ┌─────────────┬─────────────┬─────────────────────────┐ │
│ │ Eden │ Survivor S0 │ Survivor S1 │ │
│ │ Space │ Space │ Space │ │
│ └─────────────┴─────────────┴─────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 老年代 (Old Generation / Tenured Space) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Old Generation │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
非堆内存 (Non-Heap Memory): - Metaspace:类元数据存储区域(Java 8+) - Code Cache:编译后的本地代码缓存 - Compressed Class Space:压缩类指针空间
4.3.2 内存使用指标
关键指标:
// 内存监控示例
import java.lang.management.*;
import java.util.List;
public class MemoryMonitor {
private static final MemoryMXBean memoryBean =
ManagementFactory.getMemoryMXBean();
private static final List<MemoryPoolMXBean> memoryPools =
ManagementFactory.getMemoryPoolMXBeans();
public static void printMemoryInfo() {
// 堆内存信息
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("=== Heap Memory ===");
System.out.println("Used: " + formatBytes(heapUsage.getUsed()));
System.out.println("Committed: " + formatBytes(heapUsage.getCommitted()));
System.out.println("Max: " + formatBytes(heapUsage.getMax()));
System.out.println("Usage: " +
String.format("%.2f%%",
(double) heapUsage.getUsed() / heapUsage.getMax() * 100));
// 非堆内存信息
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("\n=== Non-Heap Memory ===");
System.out.println("Used: " + formatBytes(nonHeapUsage.getUsed()));
System.out.println("Committed: " + formatBytes(nonHeapUsage.getCommitted()));
// 各内存池详细信息
System.out.println("\n=== Memory Pools ===");
for (MemoryPoolMXBean pool : memoryPools) {
MemoryUsage usage = pool.getUsage();
System.out.printf("%s: %s / %s (%.2f%%)\n",
pool.getName(),
formatBytes(usage.getUsed()),
formatBytes(usage.getMax()),
usage.getMax() > 0 ?
(double) usage.getUsed() / usage.getMax() * 100 : 0);
}
}
private static String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0);
if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024));
return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
}
public static void main(String[] args) throws InterruptedException {
while (true) {
printMemoryInfo();
System.out.println("\n" + "=".repeat(50) + "\n");
Thread.sleep(10000);
}
}
}
4.3.3 内存使用模式分析
正常内存使用模式: - 锯齿型:内存使用逐渐增长,GC 后下降 - 稳定型:内存使用相对稳定,在一定范围内波动 - 阶梯型:内存使用呈阶梯式增长,每次 GC 后有所下降
异常内存使用模式: - 持续增长:可能存在内存泄漏 - 频繁 GC:可能存在内存不足或对象创建过于频繁 - 突然下降:可能发生了 Full GC 或应用程序重启
4.4 类和线程监控
4.4.1 类加载监控
类加载指标: - Loaded Classes:当前已加载的类数量 - Total Loaded:总共加载过的类数量 - Unloaded Classes:已卸载的类数量
// 类加载监控示例
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
public class ClassLoadingMonitor {
private static final ClassLoadingMXBean classLoadingBean =
ManagementFactory.getClassLoadingMXBean();
public static void printClassLoadingInfo() {
System.out.println("=== Class Loading Information ===");
System.out.println("Loaded Classes: " + classLoadingBean.getLoadedClassCount());
System.out.println("Total Loaded: " + classLoadingBean.getTotalLoadedClassCount());
System.out.println("Unloaded Classes: " + classLoadingBean.getUnloadedClassCount());
// 计算类加载率
long totalLoaded = classLoadingBean.getTotalLoadedClassCount();
long unloaded = classLoadingBean.getUnloadedClassCount();
double retentionRate = (double) (totalLoaded - unloaded) / totalLoaded * 100;
System.out.printf("Class Retention Rate: %.2f%%\n", retentionRate);
}
public static void main(String[] args) throws InterruptedException {
while (true) {
printClassLoadingInfo();
System.out.println();
Thread.sleep(5000);
}
}
}
4.4.2 线程监控
线程指标: - Live Threads:当前活跃线程数 - Peak Threads:历史最大线程数 - Daemon Threads:守护线程数 - Total Started:总共启动过的线程数
// 线程监控示例
import java.lang.management.ThreadMXBean;
import java.lang.management.ManagementFactory;
public class ThreadMonitor {
private static final ThreadMXBean threadBean =
ManagementFactory.getThreadMXBean();
public static void printThreadInfo() {
System.out.println("=== Thread Information ===");
System.out.println("Live Threads: " + threadBean.getThreadCount());
System.out.println("Peak Threads: " + threadBean.getPeakThreadCount());
System.out.println("Daemon Threads: " + threadBean.getDaemonThreadCount());
System.out.println("Total Started: " + threadBean.getTotalStartedThreadCount());
// 获取所有线程信息
long[] threadIds = threadBean.getAllThreadIds();
System.out.println("\n=== Thread Details ===");
for (long threadId : threadIds) {
ThreadInfo threadInfo = threadBean.getThreadInfo(threadId);
if (threadInfo != null) {
System.out.printf("Thread [%d]: %s - %s\n",
threadId,
threadInfo.getThreadName(),
threadInfo.getThreadState());
}
}
}
public static void main(String[] args) throws InterruptedException {
while (true) {
printThreadInfo();
System.out.println("\n" + "=".repeat(50) + "\n");
Thread.sleep(10000);
}
}
}
5. 实时监控最佳实践
5.1 监控策略
5.1.1 监控频率设置
# 在 VisualVM 中设置监控频率
# Tools → Options → Profiler → Settings
# 推荐设置:
# - 开发环境:1-2 秒
# - 测试环境:5-10 秒
# - 生产环境:30-60 秒(如果允许)
5.1.2 监控数据保存
// 自动保存监控数据的脚本
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.lang.management.*;
public class MonitoringDataSaver {
private static final String DATA_DIR = "monitoring_data";
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
public static void saveMonitoringSnapshot() {
try {
File dataDir = new File(DATA_DIR);
if (!dataDir.exists()) {
dataDir.mkdirs();
}
String timestamp = LocalDateTime.now().format(FORMATTER);
String filename = String.format("%s/monitoring_%s.txt", DATA_DIR, timestamp);
try (PrintWriter writer = new PrintWriter(new FileWriter(filename))) {
writer.println("=== Monitoring Snapshot ===");
writer.println("Timestamp: " + LocalDateTime.now());
writer.println();
// 保存内存信息
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
writer.println("Heap Memory:");
writer.println(" Used: " + heapUsage.getUsed());
writer.println(" Max: " + heapUsage.getMax());
writer.println(" Usage: " +
String.format("%.2f%%",
(double) heapUsage.getUsed() / heapUsage.getMax() * 100));
writer.println();
// 保存线程信息
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
writer.println("Thread Information:");
writer.println(" Live: " + threadBean.getThreadCount());
writer.println(" Peak: " + threadBean.getPeakThreadCount());
writer.println(" Daemon: " + threadBean.getDaemonThreadCount());
writer.println();
// 保存类加载信息
ClassLoadingMXBean classBean = ManagementFactory.getClassLoadingMXBean();
writer.println("Class Loading:");
writer.println(" Loaded: " + classBean.getLoadedClassCount());
writer.println(" Total: " + classBean.getTotalLoadedClassCount());
writer.println(" Unloaded: " + classBean.getUnloadedClassCount());
}
System.out.println("Monitoring data saved to: " + filename);
} catch (IOException e) {
System.err.println("Failed to save monitoring data: " + e.getMessage());
}
}
public static void main(String[] args) throws InterruptedException {
// 每分钟保存一次监控数据
while (true) {
saveMonitoringSnapshot();
Thread.sleep(60000); // 60 seconds
}
}
}
5.2 性能影响最小化
5.2.1 监控开销控制
// 轻量级监控实现
public class LightweightMonitor {
private static final long MONITOR_INTERVAL = 30000; // 30 seconds
private static volatile boolean monitoring = true;
public static void startMonitoring() {
Thread monitorThread = new Thread(() -> {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
while (monitoring) {
try {
// 只收集关键指标
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
double heapUsagePercent =
(double) heapUsage.getUsed() / heapUsage.getMax() * 100;
int threadCount = threadBean.getThreadCount();
// 简单的阈值检查
if (heapUsagePercent > 80) {
System.out.println("WARNING: High heap usage: " +
String.format("%.2f%%", heapUsagePercent));
}
if (threadCount > 100) {
System.out.println("WARNING: High thread count: " + threadCount);
}
Thread.sleep(MONITOR_INTERVAL);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
monitorThread.setDaemon(true);
monitorThread.setName("LightweightMonitor");
monitorThread.start();
}
public static void stopMonitoring() {
monitoring = false;
}
}
5.3 监控告警
5.3.1 简单告警系统
// 监控告警系统
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.List;
import java.util.ArrayList;
public class MonitoringAlertSystem {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
private final List<AlertRule> alertRules = new ArrayList<>();
public static class AlertRule {
private final String name;
private final Supplier<Double> valueSupplier;
private final double threshold;
private final String operator; // ">", "<", ">=", "<="
private boolean alerted = false;
public AlertRule(String name, Supplier<Double> valueSupplier,
String operator, double threshold) {
this.name = name;
this.valueSupplier = valueSupplier;
this.operator = operator;
this.threshold = threshold;
}
public boolean check() {
double value = valueSupplier.get();
boolean shouldAlert = false;
switch (operator) {
case ">":
shouldAlert = value > threshold;
break;
case "<":
shouldAlert = value < threshold;
break;
case ">=":
shouldAlert = value >= threshold;
break;
case "<=":
shouldAlert = value <= threshold;
break;
}
if (shouldAlert && !alerted) {
System.out.printf("ALERT: %s = %.2f %s %.2f\n",
name, value, operator, threshold);
alerted = true;
return true;
} else if (!shouldAlert && alerted) {
System.out.printf("RESOLVED: %s = %.2f\n", name, value);
alerted = false;
}
return false;
}
}
public void addAlertRule(AlertRule rule) {
alertRules.add(rule);
}
public void startMonitoring() {
scheduler.scheduleAtFixedRate(() -> {
for (AlertRule rule : alertRules) {
rule.check();
}
}, 0, 30, TimeUnit.SECONDS);
}
public void shutdown() {
scheduler.shutdown();
}
public static void main(String[] args) throws InterruptedException {
MonitoringAlertSystem alertSystem = new MonitoringAlertSystem();
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
// 添加告警规则
alertSystem.addAlertRule(new AlertRule(
"Heap Usage %",
() -> {
MemoryUsage usage = memoryBean.getHeapMemoryUsage();
return (double) usage.getUsed() / usage.getMax() * 100;
},
">",
80.0
));
alertSystem.addAlertRule(new AlertRule(
"Thread Count",
() -> (double) threadBean.getThreadCount(),
">",
50.0
));
alertSystem.startMonitoring();
// 保持程序运行
Thread.sleep(Long.MAX_VALUE);
}
}
6. 本章总结
6.1 关键要点
用户界面掌握
- 熟悉 VisualVM 的界面布局和组件
- 理解各个标签页的功能和用途
- 掌握应用程序连接和管理方法
基本监控技能
- CPU 使用率监控和分析
- 内存使用监控和模式识别
- 类加载和线程状态监控
- 实时数据的解读和分析
监控最佳实践
- 合理设置监控频率
- 最小化性能影响
- 建立告警机制
- 数据保存和分析
6.2 实践建议
循序渐进
- 从简单的本地应用开始
- 逐步尝试远程监控
- 深入理解各项指标含义
结合实际
- 在实际项目中应用监控
- 建立性能基线
- 识别性能瓶颈
持续学习
- 关注新版本功能
- 学习高级监控技巧
- 分享监控经验
6.3 下一步学习
在下一章中,我们将学习: - 线程分析和死锁检测 - 线程转储的生成和分析 - 线程状态的深入理解 - 并发问题的诊断技巧
通过本章的学习,您已经掌握了 VisualVM 的基本使用方法和监控技能,为深入的性能分析打下了坚实的基础。