深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第四章知识点问答(37题)

目录

  • Q1 工具矩阵与一行用途速查
  • Q2 jcmd 常用子命令与范式
  • Q3 jps 用法与注意事项
  • Q4 jmap 选项(-F/-dump/-histo/-heap)与风险
  • Q5 jstat -gcutil 各列含义(含 M vs P、CCS)
  • Q6 用 jstat 判断内存问题:采样节奏与误区
  • Q7 Thread.print/jstack:高 CPU/死锁/IO 阻塞判读
  • Q8 top -H + Thread.print SOP 与 TID→nid 映射
  • Q9 直接内存(DirectByteBuffer)生命周期与释放误区
  • Q10 采样侵入性与频率:哪些命令会 STW
  • Q11 120 秒快速体检脚本(基线)
  • Q12 堆外/本地内存三步闭环
  • Q13 DirectByteBuffer 疑似泄漏:确认→止血→修复
  • Q14 仅用 JFR/JMC 2–3 分钟捕捉 CPU/分配热点
  • Q15 高 CPU + 频繁 Young GC:三段式排查
  • Q16 专治短命对象暴增的 Checklist
  • Q17 GC 日志(JDK8/11+)开启与“是否 GC 绑定”判读
  • Q18 Code Cache 逼近 & 容器注意点(扩展)
  • Q19 hs_err_pid 崩溃日志快速判读
  • Q20 OOM 取证与快速处置
  • Q21 GC 选型与参数基线:G1 vs ZGC
  • Q22 线程 Dump 状态判读与 TID→nid 映射
  • Q23 Heap Dump 精确分析:最小流程
  • Q24 ClassLoader 泄漏定位与修复
  • Q25 统一日志量化 5 分钟 GC 占比
  • Q26 Safepoint/TtS 量化与处置
  • Q27 NMT(原生内存)三步法与替代方案
  • Q28 JFR 预设与低开销取证
  • Q29 TLAB / TLAB 外分配 监控与优化
  • Q30 对象年龄分布 / TenuringDistribution 判读
  • Q31 VM.flags/VM.command_line 与可热改项
  • Q32 统一日志选择器速查与窗口聚合
  • Q33 Compiler.directives JIT 应急绕路
  • Q34 JIT/CodeCache 体检与编译队列
  • Q35 综合演练:2 分钟从症状到证据
  • Q36 年龄分布 + 统一日志识别早晋升/撑爆
  • Q37 收官:早晋升 + Outside TLAB 联合诊断

Q1|工具矩阵与一行用途速查

  • jcmd:统一诊断入口;线程/GC/堆/参数/JFR/NMT 等一站式命令。
  • jstat:轻量采样 GC/类加载/编译等运行时统计(低侵入)。
  • jstack / jcmd Thread.print:导出线程快照,查死锁/高 CPU/卡顿热点栈。
  • jmap:堆转储(dump)与类直方图(histo);线上慎用 dump。
  • jinfo / VM.flags / VM.command_line:查看/少量可热改的 JVM 标志与启动参数。
  • jps:列出本机 Java 进程(PID、MainClass),定位目标进程。
  • jhsdb:基于 SA 的离线剖析(进程挂死/无法附加时)。
  • JFR / JMC:低开销事件级录制与分析(飞行记录器 + 控制台)。
  • (可选)JConsole / VisualVM:图形化监控与基础分析。

Q2|jcmd 常用子命令与范式

jcmd <PID> Thread.print                    # 线程快照(死锁/热点栈)
jcmd <PID> GC.heap_info                    # 堆概览(各代 used/committed)
jcmd <PID> GC.class_histogram              # 类直方图(低频、会进安全点)
jcmd <PID> GC.heap_dump filename=/path.hprof  # 堆转储(低峰执行)
jcmd <PID> GC.run                          # 触发一次 GC(仅买时间)
jcmd <PID> VM.flags                        # 查看标志;VM.command_line 启动参数
jcmd <PID> VM.system_properties            # 系统属性(时区/编码等)
jcmd <PID> JFR.start|JFR.check|JFR.dump|JFR.stop   # 录制/导出/停止 JFR
jcmd <PID> VM.native_memory baseline|summary|summary.diff  # NMT(三步法,需启动开启)
jcmd <PID> VM.log what=...                 # 统一日志在线开启/关闭

范式:低侵入优先class_histogram/heap_dump 低频、在低峰;JFR 2–3 分钟快速取证。

Q3|jps 用法与注意事项

  • 用法:jps -l 显示主类/全限定名;jps -v 带 JVM 参数;jps -m 带 main 参数。
  • 注意:jps 仅列进程,不显示内存/类加载数据;跨容器/权限受限环境可能不全。

Q4|jmap 选项与风险(-F/-dump/-histo/-heap)

  • -dump:format=b,file=…[,live]:生成堆 dump;live 仅活对象(耗时更长)。风险:长 STW/IO 大,线上需低峰+充足磁盘。
  • -histo[:live]:按类统计 #instances/#bytes会进安全点,低频使用(≥20s 一次)。
  • -heap:打印堆与 GC 概览;
  • -F强制附加(进程卡死时),风险更大,仅兜底使用。

Q5|jstat -gcutil 各列含义(含 M vs P、CCS)

  • S0/S1:Survivor0/1 使用率(%);
  • E:Eden 使用率(%);O:Old 使用率(%);
  • MMetaspace 使用率(JDK8 之后取代旧版 P PermGen);
  • CCS:Compressed Class Space 使用率(%);
  • YGC/YGCT:Young GC 次数 / 累计耗时(秒);
  • FGC/FGCT:Full GC 次数 / 累计耗时(秒);
  • GCT:GC 总耗时(秒)。

判读要点:看 斜率(ΔGCT/Δt)、是否回落(E/S 清空频率、O 是否上行)与 YGC/FGC 的节奏。

Q6|用 jstat 判断内存问题:采样节奏与误区

  • 节奏:常用 jstat -gcutil <PID> 1000 60(1s×60);高压可到 500ms;避免过短造成噪声。

  • 判据

    • 频繁 Young GCYGC 快涨、E/S 高频充满→清空、YGCT 斜率高;
    • 老年代不回落:多次(F)GC 后 O 仍上行;
    • GC 绑定:5 分钟窗口 GC 占比 ≥30%(结合统一日志计算)。
  • 误区:仅靠 jstat 无法看到堆外;也看不到分配来源/类型(需 JFR/直方图)。

Q7|Thread.print/jstack:高 CPU/死锁/IO 阻塞判读

  • 高 CPU:多次采样(相隔 1–2s)同一批 RUNNABLE 栈稳定在计算/序列化/正则 → 计算热点。
  • 伪 RUNNABLE:socketRead0/epollWait 等 I/O 原生调用阻塞但显示 RUNNABLE。
  • 死锁:输出末尾 Deadlock 区段或大量 waiting to lock <0x…>
  • 快捷映射:top -H -p <PID> 取 TID → printf '0x%x\n' <tid> → 在线程 dump 中搜 nid=0x…

Q8|top -H + Thread.print SOP 与 TID→nid 映射

# ① 找高CPU线程
top -H -p <PID> | head
# ② TID 十进制→十六进制
printf '0x%x\n' <tid>
# ③ 抓两次线程栈对比
jcmd <PID> Thread.print > /tmp/th1.txt ; sleep 2 ; jcmd <PID> Thread.print > /tmp/th2.txt
# ④ 定位相应 nid 的栈段
sed -n '/nid=0x<hex>/,/^$/p' /tmp/th2.txt

看点:计算热点/锁竞争/IO 阻塞;配合 JFR Hot Methods/Locks 进一步确认。

Q9|直接内存(DirectByteBuffer)生命周期与释放误区

  • 不归属线程:Direct 内存由包装对象/引用计数(如 Netty)管理,非“哪个线程创建就属于谁”。
  • 释放机制:对象不可达后经 Cleaner 在线程中调用 freeMemory,需等 GC/引用处理;线程结束≠释放。
  • 池化延迟:池化/线程缓存会延迟归还;可能不立刻还给 OS。
  • 当场措施:限流降并发;(Netty)trim 线程缓存;GC.run 仅回收已不可达;JMX 观测 BufferPool: direct

Q10|采样侵入性与频率

  • 可能进入安全点(有停顿)Thread.printGC.class_histogramheap_dump(最重)、jmap -F(极重)。
  • 低侵入jstat、JFR(settings=default/profile)、统一日志 VM.logGC.heap_info
  • 建议:直方图 ≥20–30s 一次;dump 选低峰;JFR 2–3 分钟短录优先。

Q11|120 秒快速体检脚本(基线)

PID=<PID>
jstat -gcutil $PID 1000 120 > /tmp/gcutil-120s.log &
jcmd  $PID Thread.print > /tmp/th1.txt ; sleep 2 ; jcmd $PID Thread.print > /tmp/th2.txt &
jcmd  $PID JFR.start name=triage settings=profile duration=180s filename=/tmp/triage.jfr dumponexit=true

读法:jstat 看 YGC/ΔGCT 与 E/S/O;Thread.print 看计算/锁/IO;JFR 后续在 JMC 看 Hot Methods & Allocation。


Q12|堆外/本地内存三步闭环

① 判断“Java 堆稳定但 RSS 上涨”

  • 堆/GC:jstat -gcutil <PID> 2000 10jcmd <PID> GC.heap_info
  • 进程 RSS:cat /proc/<PID>/status | egrep 'VmRSS|VmSwap'cat /proc/<PID>/smaps_rolluppmap -x <PID> | sort -k3 -n | tail
    → 若堆占用/GCT 斜率小而 RSS/Swap 上行,怀疑堆外。

② NMT 定位(需启动 -XX:NativeMemoryTracking=summary|detail

jcmd <PID> VM.native_memory baseline
# 等待 1–2 分钟
jcmd <PID> VM.native_memory summary.diff

关注:Thread(线程栈)、Class(Metaspace)CodeInternal/Other(常见 Direct/JNI)。

③ 未开 NMT 的替代(2+)

  • RSS vs 堆:jstat + /proc/... 侧证堆外;
  • JFR 短录看 DirectBuffer/IO/线程创建;
  • JMX BufferPooljava.nio:type=BufferPool,name=direct
  • 线程数与 ulimit -s 估算 Thread committed。

Q13|DirectByteBuffer 泄漏:确认→止血→修复

确认(2+ 证据)

  • JMX BufferPool directMemoryUsed/TotalCapacity/Count 单调上升;
  • NMT summary.diffInternal/Other 增长;
  • RSS vs 堆:堆稳而 RSS 涨。

止血(不重启)

  • 限流/降级,压低分配速率;
  • 一次性 GCjcmd <PID> GC.run(仅对不可达 DirectBuffer 有效);
  • (Netty)trim 线程本地缓存(版本相关)。

中期修复

  • 参数:合理由上限 -XX:MaxDirectMemorySize(重启);用 JFR 取证与(Netty)LeakDetector;
  • 代码:严格 release();改为堆内/分块/流式;限制最大报文。

Q14|仅用 JFR/JMC 2–3 分钟捕捉 CPU/分配热点

命令序列(JDK11+)

jcmd <PID> JFR.start name=triage settings=profile duration=180s \
  stackdepth=128 samplethreads=true filename=/tmp/triage.jfr dumponexit=true
jcmd <PID> JFR.check
jcmd <PID> JFR.dump  name=triage filename=/tmp/triage-snap.jfr
jcmd <PID> JFR.stop  name=triage

JMC 先看 5 个视图:Hot Methods / Allocation(TLAB & Outside) / GC / Locks / I/O。
处置

  • 计算热点→优化算法/缓存/拆批;
  • 分配过猛→对象复用/流式化/限流;必要时调整 Young(重启)。

Q15|高 CPU + 频繁 Young GC:三段式排查

1) 快速体检

  • top -H -p <PID>(10–20s):记高 CPU TID;
  • jstat -gcutil <PID> 1000 20:看 ΔGCT、YGC、E/S0/S1 波动;
  • jcmd <PID> Thread.print ×2(相隔 2s):定位热点线程栈。

2) 根因定位

  • 计算热点:RUNNABLE 栈稳定在计算/序列化;ΔGCT 小;
  • 短命对象:YGC 快增、E/S0/S1 频清空;Histogram/JFR 分配速率高。

3) 缓解→修复

  • 不重启:限流/降级;压缩本地缓存;短录 JFR;
  • 重启后:增大新生代、调 MaxTenuringThreshold;代码复用/流式。

Q16|短命对象暴增 Checklist(定位→改善→复验)

定位(低侵入)jstat 时间序列;GC.heap_info;JFR Allocation;低频 GC.class_histogram;对齐网关指标;核对 VM.flags;查看 GC 日志;排除堆外。

改善(代码):减少临时对象;集合预尺寸;避免装箱;JSON/XML 流式;分页/分块;合理缓存;日志降采样;规范 ThreadLocal。

配置(重启项):增大新生代;调 MaxTenuringThreshold;TLAB 评估;
运行期缓解:限流/降级。

复验jstat YGC/ GCT 改善;JFR 分配热点下降;直方图 Top-K 不再上升;SLO 恢复。


Q17|GC 日志(JDK8/11+)开启与判读

JDK8 最小参数

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
-Xloggc:/var/log/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
# (可选)-XX:+PrintTenuringDistribution

JDK11+ 运行期开启/导出

jcmd <PID> VM.log what=gc*,safepoint decorators=uptime,level,tags \
  filename=/var/log/gc.log filecount=5 filesize=20M
# 停止:
jcmd <PID> VM.log disable

首看 3 信号:暂停占比;频度/斜率;回收后占用是否回落(若 FGC 后不降 → 泄漏/老年代压力)。


Q18|Code Cache 逼近 & 容器注意点(扩展)

证据

  • -Xlog:codecache=info / VM.log what=codecache=info:使用率/满;
  • NMT Code committed 接近 ReservedCodeCacheSize

止血(在线)Compiler.directives_add 排除热点方法或禁 C2,放缓产出。

(扩展)容器注意点:容器感知堆百分比;PID/命名空间取证差异;时区与日志卷一致性(本点属章节外延,保留为附录)。


Q19|hs_err_pid 崩溃日志快速判读

先看 6 段:错误头部与信号;Problematic frame;Current thread(含 Stack 区间);Native/Java frames;VM Arguments;内存与 CodeCache 摘要。

归因判据

  • JVM Bug:Problematic frame 在 libjvm.so 且伴内部断言;
  • 第三方 JNI:Problematic frame 指向 .so/.dll
  • CodeCache:CodeCache is full 或使用率逼近;
  • 栈溢出:Stack 贴近保护页/SOE
  • Metaspace:使用逼近上限/日志含 Metadata GC Threshold
  • 直接内存:Direct buffer memory/mmap 失败痕迹。

当场止血:限流/降级;JIT directives 绕行;(Direct)trim/限流;暂停外部高频 ThreadDump/重定义。
重启参数:扩大 CodeCache / 栈 / Metaspace / Direct 上限;或退编译级别(C2→C1/解释)。


Q20|OOM 取证与快速处置

启动期取证参数

  • -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=...
  • -XX:+ExitOnOutOfMemoryError-XX:OnOutOfMemoryError="..."
  • -XX:ErrorFile=/var/log/app/hs_err_pid%p.log
  • JDK8-Xloggc + 滚动;可选 JFR(Oracle JDK8)。
  • JDK11+-Xlog:gc*,safepoint:...-XX:StartFlightRecording=...

事发后 60 秒动作(低侵入优先)

  • 在线开启统一日志;jstat -gcutilGC.heap_info
  • 低频 GC.class_histogram
  • JFR.start 120s;
  • top -H + Thread.print 双采;
  • 业务限流;(极端)GC.run 或 HeapDump(低峰)。

分类型止血/修复

  • Heap space/GC overhead:限流/降缓存;增大堆/新生代;代码减分配。
  • Metaspace:停热更/修 ClassLoader;增 MaxMetaspaceSize
  • Direct:限流/trim;设 MaxDirectMemorySize;代码释放/流式化。
  • unable to create new native thread:降并发/提 ulimit -u;调小 -Xss 与线程模型优化。
  • CodeCache(可选):directives 绕行;扩大 ReservedCodeCacheSize

Q21|GC 选型与参数基线:G1 vs ZGC

选型要点(≥6):堆大小;暂停目标;吞吐 vs 延迟;JDK 成熟度;碎片/压实;内存弹性(SoftMaxHeapSize/ZUncommit);容器配额;调参复杂度。

G1 基线(容器)

-XX:+UseContainerSupport
-XX:InitialRAMPercentage=40  -XX:MaxRAMPercentage=70
-XX:+UseG1GC  -XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=30
-XX:G1NewSizePercent=20      -XX:G1MaxNewSizePercent=60
-XX:G1ReservePercent=20      -XX:+ParallelRefProcEnabled
-XX:ActiveProcessorCount=<核数>
-Xlog:gc*,safepoint:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=20M
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app
-XX:ErrorFile=/var/log/app/hs_err_pid%p.log

ZGC 基线(JDK17+)

-XX:+UseZGC  -XX:+ZUncommit
-XX:SoftMaxHeapSize=<size/%>  -XX:ZUncommitDelay=300s
-XX:InitialRAMPercentage=40  -XX:MaxRAMPercentage=70
-XX:ActiveProcessorCount=<核数>
-Xlog:gc*,safepoint:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=20M
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app
-XX:ErrorFile=/var/log/app/hs_err_pid%p.log

上线验收(ZGC):Pause P99 ≤10–20ms;无明显 Allocation Stall;GC 线程 CPU <5–10%,RSS 随闲忙回落。


Q22|线程 Dump 状态判读与 TID→nid 映射

状态特征(各2个)

  • RUNNABLE:计算热点方法;或 I/O 原生调用伪 RUNNABLE。
  • BLOCKED:waiting to lockReentrantLock/AQS.acquire
  • WAITING/TIMED_WAITING:Object.wait/Condition.awaitThread.sleep
  • PARKED:LockSupport.parkForkJoinPool.awaitWork
  • NATIVE:read0/write0/epollWait/accept0、JNI/Unsafe.copyMemory

根因与处置:CPU 热点优化/限流;锁竞争缩临界区/分段锁;队列/背压;I/O 设超时与熔断。

TID→nid 映射top -H -p <PID>printf '0x%x\n' <tid> → 在 Thread.printnid=0x...


Q23|Heap Dump 精确分析:最小流程

何时抓:FGC 后 Old 不回落;Histogram Top-K 3 次上升;Metaspace 高位;选低峰(会 STW/大 IO)。

怎么抓

jcmd <PID> GC.heap_dump filename=/data/dump/app-$(date +%F-%H%M).hprof
# 兜底:jmap -dump:format=b,file=... <PID> (-F 谨慎)

看什么:Dominator Tree(最大保留)/ Path to GC Roots(强引用链)/ Leak Suspects。

修复与复验:移除静态强引用/缩缓存/ThreadLocal remove/流式化;jstat 斜率改善、Histogram 不再上行、SLO 恢复。


Q24|ClassLoader 泄漏定位与修复

线上信号

  • 堆:*ClassLoader Retained 大、强引用链指向静态/线程/ThreadLocal;
  • NMT/统计:Class(Metaspace) 上涨;jstat -class Loaded↑/Unloaded↓;
  • 现象:Metadata GC Threshold 频繁 / OOME: Metaspace。

MAT/JMC 三步:找 Loader → 看到根路径/支配关系 → 落到具体字段/集合/重复类。

修复URLClassLoader#close() 与停后台线程;断开静态回指与缓存隔离;控制动态生成类/代理;ThreadLocal 规范。增大 MaxMetaspaceSize 仅买时间。


Q25|统一日志量化 5 分钟 GC 占比

开启-Xlog:gc*,safepoint:...:uptime,level,tagsjcmd VM.log what=gc*,safepoint ...

awk 思路:累计最近 300s 内 Pause ... <xx>ms 为 GC 秒数,ratio=gc_sec/300;可同时统计事件次数、Safepoint。

非 GC 绑定时的初判jstat -gcutil <PID> 1000 60(ΔGCT≈0);Thread.print ×2 判 CPU/锁。


Q26|Safepoint/TtS 量化与处置

方法 A(快照差分)

jcmd <PID> VM.print_safepoint_statistics -verbose > /tmp/sp1.txt
sleep 300
jcmd <PID> VM.print_safepoint_statistics -verbose > /tmp/sp2.txt
# 计算 ΔTtS/ΔStopped 占比;聚合 VM Operation Top-N

方法 B(-Xlog:safepoint):窗口聚合 stoppedtime to safepoint

高 TtS 三类根因:JNI/系统调用卡住;长计算循环缺轮询点;GC Locker/JNI Critical 区域。对应:限流/缩短 native 段/加入轮询点;必要时 GuaranteedSafepointInterval(治标)。


Q27|NMT(原生内存)三步法与替代方案

开关与开销-XX:NativeMemoryTracking=summary|detail;summary ~1–2% 开销、detail 更高,不宜长开。

三步baseline → 等待 → summary.diff;看 Thread / Class(Metaspace) / Code / Internal(Other) 等类目。

典型场景:Thread 涨→下调并发/排查泄漏;Class 涨→停热更/查 Loader 泄漏;Internal/Other 涨→Direct/JNI,限流+trim。

若未开 NMT:RSS vs 堆对比;JMX BufferPool/线程数/CodeCache 侧证(有局限)。


Q28|JFR 预设与低开销取证

预设default(长期巡检,~0.5–1%)/profile(短时热点,~1–3%)/continuous(有的发行版提供,接近 default)。

短时取证(180s)JFR.start name=triage settings=profile duration=180s stackdepth=128 samplethreads=true filename=/tmp/triage.jfr

低扰动巡检(≥30m)settings=default stackdepth=64 samplethreads=false maxage=30m maxsize=256m,周期 dump,控制磁盘占用。

上线验收:GC Pause P99 增加 <5ms/5%;CPU 增幅 <2%,QPS/P99 变化 ❤️–5%;Allocation/锁等待无显著上扬。


Q29|TLAB / TLAB 外分配 监控与优化

概念:TLAB 内 = 线程本地快分配;Outside TLAB = 慢路径(对象大/剩余不足/逃逸)。

观察:JFR 的 Allocation in/outside TLAB;或 VM.log what=gc+tlab=debug;JDK8 可 -XX:+PrintTLAB(重启)。

Outside 高的原因→动作

  • 对象过大 → 分块/流式/复用缓冲;
  • TLAB 不匹配 → 依赖 ResizeTLAB,必要时评估 Young/MinTLABSize(重启);
  • 跨线程发布/逃逸 → 在消费线程内创建与复用;
  • 临时对象过多 → 复用 Builder/数组、预尺寸集合。

快速判定:JFR 180s;Outside 字节占比 >20–30% 且 YGC 频繁 → 倾向代码整改或 TLAB/Young 调优。


Q30|对象年龄分布 / TenuringDistribution 判读

开启:JDK8:-XX:+PrintTenuringDistribution(配 GC 日志);JDK11+:-Xlog:gc+age=trace 或运行期 VM.log 开启。

早晋升new threshold ≤ 2 频现;原因:Survivor 小/阈值低/存活率高;动作:扩大新生代/Survivor、适度提高 MaxTenuringThreshold(压测)。

Survivor 撑爆:desired survivor 远小于实际存活、甚至 promotion failure;原因:瞬时存活峰值高/Outside 多;动作:扩大新生代/Survivor,代码侧削峰。

体检脚本:1 分钟每 5s 抽取年龄日志;配 jstat 观察 YGC/ΔGCT 斜率,判定是否需要调阈值/比例。


Q31|VM.flags/VM.command_line 与可热改项

查看VM.command_line(原始参数)、VM.flags(标志与可写属性)、VM.system_properties

可在线操作:统一日志 VM.log、JFR、编译指令 Compiler.directives_*、极少数 manageable 标志(如 MinHeapFreeRatio)。

不可在线改-Xms/-Xmx、收集器选择、大多数堆布局、Metaspace/Direct/CodeCache 上限、NMT 开关等。


Q32|统一日志选择器速查与窗口聚合

常用选择器gc*=infogc+phases=debuggc+heap=infogc+age=tracegc+tlab=debuggc+metaspace=infosafepoint=infocodecache=info

运行期开启/关闭jcmd <PID> VM.log what=gc*,safepoint ... filename=...;或 output=stdout 即席;关闭 VM.log disable

5 分钟窗口聚合:用 uptime 装饰器统计 Pause 总时长/次数、GC 占比、new threshold 最小值(awk/grep)。

阈值与动作:暂停占比 ≥30% 或 5 分钟内 Full GC ≥2 次 → 限流/暂停重任务;必要时一次性 GC.run(低峰)。


Q33|Compiler.directives JIT 应急绕路

操作

jcmd <PID> Compiler.directives_add '[{"match":"com.foo.Bar::hot","Exclude":true}]'
jcmd <PID> Compiler.directives_add '[{"match":"com.foo.*","C1":true,"C2":false}]'
jcmd <PID> Compiler.directives_print
jcmd <PID> Compiler.directives_remove <ID>  # 或 Compiler.directives_clear

效果:被 Exclude 的方法退回解释执行;禁 C2 则最多到 C1,缓解 CodeCache/编译崩溃路径。

触发条件:CodeCache 逼近/队列堆积;疑似 JIT 相关崩溃(hs_err/CompileTask)。

回滚与验证directives_remove/clear;开 -Xlog:compilation=debug/VM.log 验证;看 Compiler.queuecodecache=info 曲线回落。


Q34|JIT/CodeCache 体检与编译队列

体检命令Compiler.queue 看队列;-Xlog:compilation=debug 看洪峰;-Xlog:codecache=info 看使用率/满告警。

不重启止血:directives 排除热点或禁 C2(性能下降);业务限流(SLA 受限)。

重启修复:增大 ReservedCodeCacheSize;降低编译强度(TieredStopAtLevel/-UseC2)。

为何放大延迟:编译线程争用 CPU + 安装/去优化触发安全点与指令缓存失效,热点退回解释,尾延迟上扬。


Q35|综合演练:2 分钟从症状到证据

采集脚本(120s)

PID=<PID>
jstat -gcutil $PID 1000 120 > /tmp/gcutil-120s.log &
jcmd  $PID Thread.print > /tmp/th1.txt ; sleep 2 ; jcmd $PID Thread.print > /tmp/th2.txt &
jcmd  $PID JFR.start name=triage settings=profile duration=180s filename=/tmp/triage.jfr dumponexit=true

判定

  • 计算热点:ΔGCT 小;RUNNABLE 栈稳定在计算;JFR Hot Methods 高、Allocation 不夸张。
  • 短命对象:YGC 快增、E/S 频清空、YGCT 斜率高;JFR Allocation/TLAB 热。
    动作:限流/降级;收缩缓存;重启后增新生代/适度提晋升阈值,代码复用/流式。

Q36|统一日志 + 年龄分布 → 识别“早晋升 / Survivor 撑爆”

开启jcmd <PID> VM.log what=gc+age=trace decorators=uptime,level,tags filename=/var/log/gc-age.log;关闭 VM.log disable

统计:最近 5 分钟 new threshold 最小值;判定“早晋升”阈值:≤2 多次出现。

Survivor 撑爆:不重启:限流/降批;一次性 GC.run(低峰,取证为主)。重启:增新生代/Survivor;适度提 MaxTenuringThreshold(压测)。


Q37|收官:2 分钟识别“早晋升”与“TLAB 外分配”

采集(120s)jstat -gcutil + VM.log gc+age=trace + JFR.start 180s

早晋升信号:年龄日志 new threshold ≤ 2jstatO 随 YGC 上行、E/S 频清零。

Outside TLAB >30% 时

  • 代码:分块/流式/复用缓冲;在消费线程内创建并复用对象(减少逃逸)。
  • 参数(重启):适度增大新生代/Survivor(G1 G1NewSizePercent/G1MaxNewSizePercent;评估 MinTLABSize)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值