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 连接到应用程序

  1. 双击连接

    • 在应用程序树中双击进程
    • 自动打开 Overview 标签页
  2. 右键菜单

    右键点击进程 → 选择操作
    ├── 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 中添加远程连接

  1. 添加远程主机

    右键点击 "Remote" → "Add Remote Host"
    输入主机名或 IP 地址:192.168.1.100
    
  2. 添加 JMX 连接

    右键点击远程主机 → "Add JMX Connection"
    连接字符串:service:jmx:rmi:///jndi/rmi://192.168.1.100:9999/jmxrmi
    用户名:monitorRole
    密码:password123
    
  3. 高级连接配置: “`bash

    自定义连接脚本

    #!/bin/bash

    remote-connect.sh

HOST=”$1” PORT=“$2” USER=“$3” PASS=“$4”

if [[ -z $HOST || -z $PORT ]]; then echo “Usage: $0 [username] [password]” exit 1 fi

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 关键要点

  1. 用户界面掌握

    • 熟悉 VisualVM 的界面布局和组件
    • 理解各个标签页的功能和用途
    • 掌握应用程序连接和管理方法
  2. 基本监控技能

    • CPU 使用率监控和分析
    • 内存使用监控和模式识别
    • 类加载和线程状态监控
    • 实时数据的解读和分析
  3. 监控最佳实践

    • 合理设置监控频率
    • 最小化性能影响
    • 建立告警机制
    • 数据保存和分析

6.2 实践建议

  1. 循序渐进

    • 从简单的本地应用开始
    • 逐步尝试远程监控
    • 深入理解各项指标含义
  2. 结合实际

    • 在实际项目中应用监控
    • 建立性能基线
    • 识别性能瓶颈
  3. 持续学习

    • 关注新版本功能
    • 学习高级监控技巧
    • 分享监控经验

6.3 下一步学习

在下一章中,我们将学习: - 线程分析和死锁检测 - 线程转储的生成和分析 - 线程状态的深入理解 - 并发问题的诊断技巧

通过本章的学习,您已经掌握了 VisualVM 的基本使用方法和监控技能,为深入的性能分析打下了坚实的基础。