第2章:用户界面与基本监控
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;}@Overridepublic 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
-
高级连接配置:
# 自定义连接脚本 #!/bin/bash # remote-connect.shHOST="$1" PORT="$2" USER="$3" PASS="$4"if [[ -z $HOST || -z $PORT ]]; thenecho "Usage: $0 <host> <port> [username] [password]"exit 1 fiCONNECTION_URL="service:jmx:rmi:///jndi/rmi://$HOST:$PORT/jmxrmi"echo "Connecting to: $CONNECTION_URL"if [[ -n $USER && -n $PASS ]]; thenecho "Using authentication: $USER" fi# 可以在这里添加更多的连接逻辑
2.3 应用程序管理
2.3.1 应用程序属性查看
右键点击应用程序 → Properties显示信息包括:
├── 基本信息
│ ├── 进程 ID (PID)
│ ├── 主类名
│ ├── 启动时间
│ └── 运行时间
├── JVM 信息
│ ├── Java 版本
│ ├── JVM 供应商
│ ├── JVM 参数
│ └── 类路径
├── 系统属性
│ ├── 操作系统信息
│ ├── 用户信息
│ └── 环境变量
└── JVM 参数├── 堆内存设置├── 垃圾收集器└── 其他 JVM 选项
2.3.2 应用程序控制
生成转储文件:
# 通过 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 secondsprivate 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 的基本使用方法和监控技能,为深入的性能分析打下了坚实的基础。