《Java 虚拟机原理与性能调优》目录与学习路径
《Java 虚拟机原理与性能调优》全 31 篇的目录与学习路径:10 章 30 节,从研究方法、类文件与对象布局,到 JIT、GC、内存诊断、线程与锁、启动加速和综合压测调优,给出三条按症状跳读的路线、统一实验口径、项目状态、机器口径与各卷衔接。
tag
《Java 虚拟机原理与性能调优》全 31 篇的目录与学习路径:10 章 30 节,从研究方法、类文件与对象布局,到 JIT、GC、内存诊断、线程与锁、启动加速和综合压测调优,给出三条按症状跳读的路线、统一实验口径、项目状态、机器口径与各卷衔接。
《Java 高级编程》全 34 篇,承接卷二 libraryd,深入类型系统、反射与动态代理、类加载与字节码、JMM 与 JUC、虚拟线程与结构化并发、模式匹配、JPMS 与 FFM、JMH 与 JFR:9 章 27 节加序、前言与四个附录的完整索引。
把启动、吞吐、p50/p95/p99、GC 次数与停顿、堆占用写成一份基线 JSON,用 scripts/jvm/baseline.py 采集并对比。本节真跑两次基线得到 PASS,再用 -Xint 制造回归得到 FAIL,并用 Little 定律和 p99 曲线给出 libraryd 的容量表与扩容触发条件。
报告端点 p99 40 到 57ms,团队第一反应是加 -Xms2g -Xmx2g -XX:MaxGCPauseMillis=50。本节按现象、证据、假设、改动、回归走一遍,实测三组参数与并发曲线,结论是 GC 停顿只占 p99 的 5%,真正有效的是限制单实例在途请求。
按进程、线程、堆、JFR、字节码、依赖、运行时裁剪和启动参数八层,给出 libraryd 排障会用到的 jps、jcmd、jstack、jmap、jfr、javap、jdeps、jlink、-Xlog、--enable-preview、--enable-native-access 真实命令、观察点与场景,并逐条标注是否在本机执行。
同样 5000 个请求、50 并发,ab 不带 -k 是 1909 req/s、p99 78ms,带 -k 是 10201 req/s、p99 17ms。本节用这两份真实输出讲吞吐、均值、分位、连接复用与协调缺失,并用 Little 定律和开环压测器说明为什么并发不是越高越好。
按 JDK 8/11/17/21/25 五个基线,把 records、sealed、模式匹配、虚拟线程、ScopedValue、FFM、StructuredTaskScope、JEP 491 与 JEP 472 的状态整理成矩阵,逐行标注正式、预览、移除及对 libraryd 的影响,并给出本机验证命令与常见版本误记。
JDK 25 的 AOT 缓存分 record、create、run 三阶段,缓存类链接结果和方法 profile,不是机器码。本节实测归档大小、目录 classpath 的失败、create 阶段漏传 classpath 的报错与 jar 变更后的降级行为,并把 AOT 缓存、AppCDS 与 GraalVM Native Image 的适用边界分开。
默认 CDS 已经把 JDK 核心类放进共享归档,AppCDS 只负责应用类。本节用 -Xshare:dump、ArchiveClassesAtExit、SharedArchiveFile 和 jcmd VM.cds 实测归档大小与启动差异,并给出目录 classpath 失败、相对路径失效和 -Xshare:on 硬失败的处理方式。
把第 2 章的反射与动态代理整理成可套用模板:三个运行时注解、按实现类查找注解的审计与事务拦截器、Repository<T, ID> 代理工厂、record 组件读取、MethodHandles 替代,以及 InaccessibleObjectException 与模块 opens 处理。
把 libraryd 的启动拆成进程启动、VM 初始化、类加载、main 返回和首请求五个时间点。本节用 startuptime、class+load、JFR ClassLoad 与十次重复测量,给出 Hello 与最小 HTTP 服务的真实分布,并解释 readiness 通过为什么不等于首请求就绪。
按 libraryd 的借阅计数、事件流水线与仪表盘聚合三条线,整理 volatile、synchronized、ReentrantLock、Condition、原子类、ConcurrentHashMap、六种阻塞队列、同步器、Executors、虚拟线程与 StructuredTaskScope 的关键方法、选型与常见误用,并标注 JDK 25 的预览状态。
1000 个虚拟线程只对应 2 个载体,这是怎么做到的?本节用 Thread.vthread_scheduler、JSON 线程容器和 pin 心跳实验拆开 Continuation、mount/unmount 与调度,验证 JDK 25 的 synchronized 不再 pin,而本地调用和类初始化仍会占住载体。
JFR 找到热点之后,下一步不是立刻改代码,而是算收益。本节用同一台机器对比 Loan.state 与直接罚金计算的分配采样,用 JMH 量化 synchronized 与 LongAdder 的差距,并比较批量循环与 stream,最后给出 libraryd 的「先测量、再改、再回归」决策表。
JDK 25 已经没有偏向锁。本节用 PrintFlagsFinal 确认 UseBiasedLocking 消失,用 javap 看 synchronized 方法与方法块的字节码差异,再用 JFR 记录 JavaMonitorEnter、Wait 与 Inflate,对比 synchronized 和 ReentrantLock 的真实竞争事件。
JMH 告诉你一次调用多快,JFR 告诉你时间花在哪。本节用 JDK 25 内置 Flight Recorder 记录 10 秒 libraryd 负载,执行 jfr summary、jfr view hot-methods 与 jfr print --events jdk.ExecutionSample,引用 156 个采样和 272 MB Overdue 分配,并说明火焰图边界。
jstack 看不到虚拟线程。本节用 1000 个虚拟线程加 4 个平台工作线程实测 Thread.print 与 Thread.dump_to_file,读出 JSON 里的 root、ThreadPerTaskExecutor 与 ForkJoinPool 容器,并给出死锁检测、线程状态判断和 libraryd 的转储脚本。
用 System.nanoTime 包一个循环,测出来的往往是 JIT 删除后的空操作。本节用 JMH 1.37 真跑 FinePolicyBenchmark,给出 Warmup、Measurement、Fork 配置和真实结果表,解释 JDK 25 下注解处理器默认关闭导致的 BenchmarkList 缺失,以及为什么 1.8 ns 的差异不能直接当成结论。
堆没满、进程却一直涨,问题常在 -Xmx 之外。本节用 Byte Buddy 1.18.16 生成类,在 64 MB 元空间上限下复现真实 Metaspace OOM;再用 32 MB 直接内存上限复现 Cannot reserve 报错,并给出 VM.metaspace、NMT 与插件热重载的排查路径。
FFM 能调用 C,但 native 代码里的空指针会直接带走整个 JVM,try/catch 接不住。本节用真实的 SIGSEGV 崩溃输出说明隔离边界,讨论 Arena 清理、库卸载、跨平台分发与 JNI 迁移成本,并给出 libraryd 在 native 与纯 Java 校验器之间的选择与回退代码。