本文介绍桌面应用的调试技巧与性能监控方法。
jvmArgs += listOf(
"-XX:NativeMemoryTracking=detail",
)# Windows
jcmd <pid> VM.native_memory summary
# macOS/Linux
jcmd <pid> VM.native_memory summaryNative Memory Tracking:
Total: reserved=XXX MB, committed=XXX MB
- Java Heap (reserved=XXX MB, committed=XXX MB)
- Class (reserved=XXX MB, committed=XXX MB)
- Thread (reserved=XXX MB, committed=XXX MB)
- Code Stack (reserved=XXX MB, committed=XXX MB)
- GC (reserved=XXX MB, committed=XXX MB)
# 启动时启用
java -XX:StartFlightRecording:settings=default,dumponexit=true,filename=recording.jfr ...
# 动态启用
jcmd <pid> JFR.start delay=5s duration=60s filename=recording.jfr
# 查看记录
jcmd <pid> JFR.view recording.jfr| 配置 | 说明 |
|---|---|
default |
适合大多数场景 |
profile |
更详细的采样 |
continuous |
持续记录 |
# 检查状态
jcmd <pid> JFR.check
# 停止记录
jcmd <pid> JFR.stop
# 转储
jcmd <pid> JFR.dump filename=dump.jfrjvmArgs += listOf(
"-Xlog:gc*=info:file=gc.log:time,uptime:filecount=5,filesize=10M",
)jvmArgs += listOf(
"-XX:+UseG1GC",
"-XX:MaxGCPauseMillis=200",
"-XX:G1HeapRegionSize=4m",
)- GCViewer - 可视化 GC 日志
- GCEasy - 在线分析
- IBM Pattern - 详细模式
[2024-01-01T12:00:00.001+0800] GC(12) Pause Young (Normal) 45M->12M(256M) 23.456ms
jvmArgs += listOf(
// GPU 合成(推荐)
"-Dskiko.renderApi=OPENGL",
// 软件渲染(调试)
"-Dskiko.renderApi=SOFTWARE",
// 快速软件渲染
"-Dskiko.renderApi=SOFTWARE_FAST",
)- 列表滚动不流畅 - 尝试 OPENGL
- 内存占用高 - 切换 SOFTWARE 测试
- 显示异常 - 检查 GPU 驱动
项目中的配置 (
build.gradle.kts:52):
"-Dskiko.renderApi=OPENGL",# 启动 JMC
jmc &
# 连接到应用
# 自动开启 JFR 录制- CPU 占用 - 方法级别分析
- 内存分配 - 对象创建热点
- GC 分析 - 停顿时间
- 线程 - 死锁检测
# 生成 heap dump
jcmd <pid> GC.heap_dump heap.hprof
# JMX 触发
jmap -dump:file=heap.hprof,format=b <pid>| 工具 | 说明 |
|---|---|
| Eclipse MAT | 免费,功能强大 |
| VisualVM | JDK 内置 |
| YourKit | 商业工具 |
# 内存泄漏
找出持续增长的对象:
- ThreadLocal 引用
- 监听器未注销
- 静态集合积累
# 内存溢出
分析 dump:
- 大对象(数组)
- 类加载器问题
# 采样 CPU
./profiler.sh start -e cpu <pid>
./profiler.sh stop --format=html -o output.html <pid>
# 采样内存分配
./profiler.sh start -e alloc <pid>┌─────────────────────────────────────────────┐
│ main() │
│ ┌────────────────────────────────────────┐ │
│ │ process() │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │ render() │ │ │
│ │ │ ┌────────────────────────────┐ │ │ │
│ │ │ │ drawText() │ │ │ │
│ │ │ └────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────┘ │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
-
Compose 检查器
- Layout Inspector
- Recomposition 计数
-
断点技巧
- 条件断点:url.contains("api") - 日志断点:打印变量 - 方法断点:入/出口 -
性能分析
Run > Profiler > CPU Usage Run > Profiler > Memory
// 添加调试标志
System.setProperty("kotlin.compose.compiler.recompose.trace", "true")
// 查看重组日志| 工具/参数 | 用途 |
|---|---|
-XX:NativeMemoryTracking=detail |
内存追踪 |
jcmd JFR.* |
飞行记录 |
-Xlog:gc*=info |
GC 日志 |
-Dskiko.renderApi=OPENGL |
渲染配置 |
| JMC | Mission Control |
jcmd GC.heap_dump |
Heap Dump |
全部博客已完成!