第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 连接到应用程序
  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. 高级连接配置

    # 自定义连接脚本
    #!/bin/bash
    # remote-connect.sh
    HOST="$1"
    PORT="$2"
    USER="$3"
    PASS="$4"
    if [[ -z $HOST || -z $PORT ]];
    then
    echo "Usage: $0 <host> <port> [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 应用程序控制

生成转储文件

# 通过 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 的基本使用方法和监控技能,为深入的性能分析打下了坚实的基础。

posted @ 2025-09-08 22:49  yjbjingcha  阅读(9)  评论(0)    收藏  举报