# 《Java性能权威指南(第2版)》知识点全解 > AI生成|2026年10月28日|codebuddy|原书:斯科特·奥克斯《Java性能权威指南(第2版)》,人民邮电出版社,2022-03-01 > **我的阅读**:进度 99%|累计 14小时1分钟|开始 2023-02-23|最近 2023-03-16|读完 2023-03-16 **一句话总结**:用“先测量、再定位、后调优”的闭环,把 Java 性能问题拆到 JIT、GC、内存、线程、服务器与数据库、SE API 六层,帮你在 Java 8/11 上找到真正的瓶颈,而不是凭经验堆参数。 **适合谁**:有 Java 经验、需做线上排障、容量规划或性能面试的后端工程师、基础架构与 SRE。 **不适合谁**:零 Java 基础、只想找“万能参数”的人;本书聚焦 Java 8/11,使用 Java 17/21 新特性者需另补资料。 **前置知识**:Java 语法与集合、线程与锁、Linux 常用命令;最好了解堆/栈、类加载、GC 的粗略概念。 ## 一、全书地图 全书是三层闭环:**先建立性能观与测量方法(第1—3章),再深入 JVM 运行时(第4—9章),最后落到 Java 服务端与 API(第10—12章)**。 | 章节 | 主题 | 在全书中作用 | |---|---|---| | 第1章 导论 | 性能观、平台约定、全面性能 | 定义目标与边界 | | 第2章 性能测试方法 | 真实应用、三类时间、可变性、基准示例 | 提供实验方法 | | 第3章 Java性能工具箱 | OS 工具、Java 监控、profiler、JFR | 建立观测链 | | 第4章 使用即时编译器 | 分层编译、编译器标志、GraalVM、预编译 | 解释冷热差异 | | 第5章 垃圾回收简介 | GC 概览、优化基础、GC 工具 | GC 入门 | | 第6章 垃圾回收算法 | Throughput、G1、CMS、实验性算法 | 按 SLA 选回收器 | | 第7章 堆内存最佳实践 | 堆分析、减少内存、对象生命周期 | 管分配与泄漏 | | 第8章 原生内存最佳实践 | 内存占用、面向 OS 的 JVM 优化 | 排查堆外与容器 | | 第9章 线程和同步性能 | 线程池、ForkJoinPool、同步、监控 | 并发吞吐与争用 | | 第10章 Java服务器 | NIO、容器、异步出站、JSON | 请求链路 | | 第11章 数据库性能最佳实践 | JDBC、JPA、Spring Data | 数据访问成本 | | 第12章 Java SE API技巧 | 字符串、缓冲 I/O、类加载、随机数、JNI、异常 | 热点 API 微成本 | 主线:**指标 → 工具 → JIT → GC → 内存 → 线程 → 服务端 → 数据库 → API**;定位时反向走:**业务指标 → OS/容器 → JVM → 应用 → 下游**。 > 版本边界:本书以 Java 8/11 为主;Java 17/21 的新回收器、虚拟线程需按当前 JDK 另补。 ## 二、核心知识点 **1. 全面性能观**:**是什么**——性能不只是“快”,还包括吞吐、响应、启动、内存、扩展性和单位成本。**为什么**——单点优化会转移瓶颈,如调大堆减少 Full GC 却抬高停顿。**怎么用**——先定 P99、TPS、内存上限、启动时间和成本,再选指标;每轮只改一个变量。**误区**——只看 CPU 或均值,忽略锁、IO、连接池等待。 **2. 真实负载与基准测试**:**是什么**——基准要复现真实请求、数据规模、并发和依赖;JMH 可隔离微基准,但不能替代系统压测。**为什么**——JIT 预热、GC、缓存和数据库往返会让短测试失真。**怎么用**——生产采样做宏观基线,JMH 验证局部假设;固定环境与数据,预热到稳态,多次重复并报告分布。**误区**——只测一次、只测纯计算、忽略预热。 **3. 三类时间与可变性**:**是什么**——吞吐是单位时间完成量,批处理时间是整批总耗时,响应时间是单请求等待;可变性衡量波动。**为什么**——系统常用吞吐换延迟,用户感知由尾部延迟决定,GC、锁竞争和调度都会制造抖动。**怎么用**——同时报告吞吐、平均、P50/P95/P99 和最大值;压测覆盖多个 GC 周期;区分服务时间与排队时间。**误区**——把 CPU 时间当墙钟时间,忽略环境差异。 **4. JFR 与观测链**:**是什么**——JFR 是 JVM 内置低开销事件记录器,记录 GC、JIT、线程、锁、IO、异常等;它与 OS 工具、JMX、profiler 组成观测链。**为什么**——无数据调参是赌博;JFR 适合生产持续采样,profiler 适合定位热点。**怎么用**——先看 OS 的 CPU/内存/IO,再用 `jcmd`、`jstat` 看 JVM,随后录 JFR,结合火焰图定位并保留业务背景。**误区**——生产开高开销采样,只依赖一个工具,把相关当因果。 **5. 分层编译与 JIT**:**是什么**——解释器快速启动,C1 快速编译,C2 深度优化;分层编译按调用计数和循环回边在启动与峰值间折中。**为什么**——代码冷热表现不同;JIT 能内联、逃逸分析、消除锁和分配,也会去优化;代码缓存满会停止编译。**怎么用**——用 `-XX:+PrintCompilation` 观察编译/去优化;基准跑到稳态;关注代码缓存和编译阈值;启动敏感服务可评估 AOT、AppCDS。**误区**——认为编译过就一定快,乱抄参数,混谈启动慢与峰值慢。 **6. GC 基础与回收器选型**:**是什么**——GC 通过可达性分析回收不可达对象,权衡吞吐、停顿和内存;分代收集利用“多数对象朝生夕死”。**为什么**——GC 是延迟和容量的大头,回收器选错会让调参事倍功半。**怎么用**——高吞吐批处理看 Throughput/Parallel;大堆兼顾停顿看 G1;老系统 CMS 结合版本评估;用 GC 日志验证。**误区**——盲目调大堆,只看次数不看停顿,忽略分配与晋升速率。 **7. G1 与停顿目标**:**是什么**——G1 把堆划为 Region,按收益优先回收,支持年轻代和混合回收,用 `MaxGCPauseMillis` 表达目标。**为什么**——G1 试图在较大堆上兼顾吞吐与可预测停顿,但暂停目标是软目标,堆越大标记成本越高。**怎么用**——设合理堆上限和暂停目标;看 Young/Mixed/Full GC、Humongous 对象、晋升失败和疏散失败。**误区**——目标设得极低,忽略大对象跨 Region,见一次 Full GC 就乱改。 **8. 堆内存与对象生命周期**:**是什么**——堆分析用转储、类直方图、支配树和保留集找泄漏与大对象;优化分配、对象大小、缓存和引用。**为什么**——分配速率高会推高 GC 频率;泄漏最终 OOM;无界缓存会把内存问题伪装成“慢慢变慢”。**怎么用**——看 GC 后存活集是否持续上升;用 `jcmd`/`jmap` 转储,用 MAT 看支配树和 Retained Size;减少临时对象、装箱和重复字符串;缓存设有界、过期和权重。**误区**——只看堆使用量,把 `SoftReference` 当万能缓存,依赖 `finalize`。 **9. 原生内存与容器**:**是什么**——JVM 总内存不只堆,还包括 Metaspace、代码缓存、线程栈、直接缓冲、本地库和 GC 结构;容器还受 cgroup 限制。**为什么**——`-Xmx` 只限堆,堆外泄漏或线程过多会让容器因总内存超限被杀,而堆指标正常。**怎么用**——用 NMT、`jcmd VM.native_memory`、`pmap` 和容器指标分解;显式设堆、Metaspace、栈和直接内存上限;给堆外留余量。**误区**——把容器内存全给堆,忽略线程栈,认为旧 JDK 都能识别容器限额。 **10. 线程池、ForkJoin 与同步**:**是什么**——线程池复用线程并控制并发,ForkJoinPool 用工作窃取处理分治任务,锁和原子操作解决可见性与互斥。**为什么**——线程太少浪费 CPU,太多增加切换和内存;锁竞争让多核退化;阻塞 ForkJoin 工作线程会拖垮并行度。**怎么用**——CPU 密集型接近核数,IO 密集型按等待比例估算并压测校正;有界队列加明确拒绝策略;锁内不做 IO/RPC;用 `jstack`、JFR 看争用。**误区**——无界队列,线程数拍脑袋,在 ForkJoinPool 中阻塞却不用 `ManagedBlocker`。 **11. 服务器与数据库**:**是什么**——NIO 提升连接密度但不自动提升单请求速度;异步出站要配超时、隔离和背压;JDBC/JPA 成本在 SQL、往返、映射和事务。**为什么**——瓶颈常在下游、序列化、连接池和 N+1 查询,数据库等待会迅速耗尽线程池。**怎么用**——连接池按数据库容量设上限;JDBC 用 PreparedStatement、批处理和合理 fetchSize;JPA 避免 N+1、懒加载越界和过大事务;大 JSON 流式处理。**误区**——连接池越大越好,把 ORM 当零成本,异步不设超时,循环逐条查询。 **12. Java SE API 细节**:**是什么**——字符串拼接、缓冲 IO、类加载、随机数、JNI、异常处理都有固定成本,会在热点路径放大。**为什么**——单次开销小,但高频调用会制造大量临时对象、系统调用或边界切换。**怎么用**——循环拼接用 `StringBuilder`;IO 加缓冲;启动优化关注类加载、反射和 AppCDS;非安全场景用 `ThreadLocalRandom`,安全场景用 `SecureRandom`;JNI 批量传参;异常只用于异常。**误区**——循环用 `+` 拼字符串,高并发争用 `Random`,用异常做流程控制。 ## 三、关键概念速查表 | 概念 | 一句话解释 | 用在哪儿 | |---|---|---| | 吞吐/延迟/容量 | 单位时间做多少、单次等多久、能扛多少 | 定义 SLO 与压测 | | P99/P999 | 99%/99.9% 请求不超过的耗时 | 判断尾部体验 | | 分层编译 | 解释器、C1、C2 按热度协作 | 平衡启动与峰值 | | 去优化 | 已编译代码退回解释执行 | 解释性能突降 | | GC 日志 | 记录回收类型、停顿、堆变化 | 判断回收器是否达标 | | G1 Region | G1 管理堆的基本分区 | 大堆、可预测停顿 | | 存活集 | GC 后仍存活的对象总量 | 判断泄漏与容量 | | NMT | JVM 原生内存跟踪 | 排查堆外内存增长 | | N+1 查询 | 一次主查询引发多次子查询 | JPA/ORM 排查 | | 背压 | 下游变慢时向上游限流 | 异步与队列保护 | ## 四、可复用的方法/框架 **A. 性能调优七步**:定目标(P99、吞吐、内存、成本)→ 建基线(生产采样+可复现压测)→ 分层定位(业务→OS/容器→JVM→应用→下游)→ 提假设(指标、机制、预期)→ 最小改动(一次一变量,可回滚)→ 再验证(同负载多轮,看分布)→ 固化(配置、监控、回归)。 **B. 三问**:瓶颈真在 JVM 内吗?不在就先修下游。有 SLO 吗?没有就别调参。改动可观测、可回滚吗?不能就不进生产。 **C. GC 选型速判**:批处理追吞吐,优先 Throughput/Parallel;大堆兼顾停顿,优先 G1 并看日志;极低延迟,评估低停顿回收器但核对版本与成熟度;CMS 只在维护策略允许时保留。 **D. 线程池估算**:CPU 密集型接近核数;IO 密集型按“核数 ×(1 + 等待/计算)”估算,再用压测校正。队列必须有界,拒绝策略要配套降级。 **E. 数据库排查**:慢 SQL → 执行计划与索引 → 往返/批处理 → 事务与锁 → 连接池 → ORM 映射。先证明数据库是瓶颈。 ## 五、我的阅读轨迹 | 项目 | 数据 | |---|---| | 进度 | 99% | | 累计阅读 | 14小时1分钟 | | 开始阅读 | 2023-02-23 | | 最近阅读 | 2023-03-16 | | 读完时间 | 2023-03-16 | 阅读状态:已基本读完,目录中没有明确的未读章节,不虚构“未读章节”。复习按掌握度安排:**第4—6章(JIT、GC)** 最值得重读,**第7—9章(堆、原生内存、线程)** 结合线上案例复盘,**第2—3章** 作为调优前检查,**第10—12章** 作为查漏索引。 建议:先重画“JIT—GC—内存—线程”因果图;再用真实服务做 JFR + GC 日志分析;最后审计数据库访问和热点 API。 ## 六、行动清单 - 为当前服务写下 P99、目标吞吐、内存上限三项 SLO。 - 用 JFR 录制一次高峰,列出 GC 暂停、锁竞争、CPU 热点各前三。 - 打开 GC 日志,确认回收器、堆上限、Full GC 次数与最长停顿。 - 对最大堆对象做转储,检查支配树和 GC 后存活集是否上升。 - 给线程池补上界队列、拒绝策略、超时和监控。 - 审计数据库:N+1、缺索引、无批处理、连接池大小。 - 建参数变更表:假设、旧值、新值、指标、回滚方式。 ## 七、延伸阅读 1. **《深入理解Java虚拟机:JVM高级特性与最佳实践》周志明**——先读或与本书第4—9章交替,补类加载、内存模型、GC 实现;本书偏“如何调”,它偏“为什么”。 2. **《Java并发编程实战》Brian Goetz**——放在第9章之后,把线程池、锁、可见性从“性能技巧”提升到“正确性设计”。 3. **《高性能MySQL(第4版)》**——放在第11章之后,深入索引、执行计划、事务和复制。 顺序建议:**JVM 基础 → 本书测量与 JVM 部分 → 并发实战 → 数据库专项**。按“先建运行时模型,再补并发与数据访问”推进即可。