<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>JAVA on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/categories/java/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 03 Mar 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/categories/java/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>Netflix 用 JDK Vector API 优化推荐系统：一次教科书级的性能工程实录</title>
      <link>https://ftmao.com/blog/posts/netflix-jdk-vector-api-recommendation/</link>
      <pubDate>Tue, 03 Mar 2026 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/netflix-jdk-vector-api-recommendation/</guid>
      <description>&lt;h1 id=&#34;netflix-用-jdk-vector-api-优化推荐系统一次教科书级的性能工程实录&#34;&gt;Netflix 用 JDK Vector API 优化推荐系统：一次教科书级的性能工程实录&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href=&#34;https://netflixtechblog.com/optimizing-recommendation-systems-with-jdks-vector-api-30d2830401ec&#34;&gt;Optimizing Recommendation Systems with JDK&amp;rsquo;s Vector API&lt;/a&gt;
目标读者：写 Java 后端、关心 JVM 性能工程的工程师；也适合任何在“要不要引入原生库/SIMD”上纠结过的人。
阅读目标：看清一次完整性能优化的正确顺序——先改计算形状，再改数据布局，最后才轮到换计算内核。以及理解为什么 BLAS 这种“标准答案”在真实生产路径里会失效。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一背景ranker-里的-75-热点&#34;&gt;一、背景：Ranker 里的 7.5% 热点&lt;/h2&gt;
&lt;p&gt;Ranker 是 Netflix 最大、最复杂的服务之一，首页那些个性化推荐行就是它算出来的。看 CPU profile 时有一个特征始终扎眼：&lt;strong&gt;视频惊喜度打分（video serendipity scoring）&lt;/strong&gt;——回答一个简单问题：“这部新片和你最近看的东西有多不一样？”这个特征吃掉每个节点约 &lt;strong&gt;7.5% 的总 CPU&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;算法本身不复杂：候选视频和用户历史里的每个视频都表示成 embedding 向量；对每个候选，算出它与所有历史向量的余弦相似度，取最大值，用 1 减去它得到“新颖度”分数，作为下游推荐逻辑的输入特征。原始实现非常直白：对每个候选取 embedding，遍历历史逐对算余弦，维护最大值。容易理解，但在 Ranker 的规模下意味着大量串行工作、重复查找、分散的内存访问和糟糕的缓存局部性。火焰图确认了这一点：服务里最热的热点之一就是惊喜度编码器里的 Java 点积。算法层面，这是一个 &lt;strong&gt;M 个候选 × N 个历史&lt;/strong&gt;的嵌套循环，产生 O(M×N) 次独立点积。&lt;/p&gt;
&lt;p&gt;一个容易被忽略但决定了整个优化方向的数据：他们埋点统计了流量形状，发现&lt;strong&gt;约 98% 的请求是单视频请求，剩下 2% 是大批量请求&lt;/strong&gt;——但那 2% 的批次实在太大，最终处理的视频总量在单视频和批处理之间大约是 &lt;strong&gt;50:50&lt;/strong&gt;。所以批处理值得做，即使它对中位数请求毫无帮助。&lt;/p&gt;
&lt;h2 id=&#34;二第一步把嵌套循环看成矩阵乘&#34;&gt;二、第一步：把嵌套循环看成矩阵乘&lt;/h2&gt;
&lt;p&gt;第一个想法是换“计算形状”：不再想成“很多次小点积”，而是当成一次矩阵运算。设 embedding 维度为 D：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把所有候选 embedding 装进矩阵 A（形状 M×D）；&lt;/li&gt;
&lt;li&gt;把所有历史 embedding 装进矩阵 B（形状 N×D）；&lt;/li&gt;
&lt;li&gt;所有行归一化成单位向量；&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;C = A × Bᵀ&lt;/code&gt;，C 就是 M×N 的余弦相似度矩阵。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样 &lt;strong&gt;M×N 次独立点积变成一次矩阵乘&lt;/strong&gt;——而这正是 CPU 和优化过的计算内核最擅长的东西。工程上通过同时支持 &lt;code&gt;encode()&lt;/code&gt;（单视频）和 &lt;code&gt;batchEncode()&lt;/code&gt;（批量）接入现有框架，保持向后兼容。到这里看起来像“做完了”。但并没有。&lt;/p&gt;
&lt;h2 id=&#34;三第二步批处理反而慢了-5&#34;&gt;三、第二步：批处理反而慢了 5%&lt;/h2&gt;
&lt;p&gt;上了批处理版本跑金丝雀，结果出人意料：&lt;strong&gt;性能回退约 5%&lt;/strong&gt;。算法没问题——把点积归并成矩阵乘在数学上是等价的。问题出在第一版实现自己引入的开销：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个批次都新建 &lt;code&gt;double[][]&lt;/code&gt; 矩阵装候选、历史和结果。这些又大又短命的对象制造了 GC 压力；而且 &lt;code&gt;double[][]&lt;/code&gt; 在内存里本身就不连续，多一层指针跳转，缓存行为更差。&lt;/li&gt;
&lt;li&gt;第一版 Java 矩阵乘是朴素的标量实现，用不上 SIMD。换句话说，&lt;strong&gt;付了批处理的成本，却没拿到批处理该有的计算效率&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;教训来得很快：算法层面的改进，如果实现细节——内存布局、分配策略、计算内核——跟你作对，就一文不值。这一步定下了后面的路线：先把数据布局改成缓存友好的，消灭每批次分配，然后再回头解决矩阵乘内核。&lt;/p&gt;
&lt;h2 id=&#34;四第三步扁平-buffer-加-threadlocal-复用&#34;&gt;四、第三步：扁平 buffer 加 ThreadLocal 复用&lt;/h2&gt;
&lt;p&gt;重做数据布局。&lt;code&gt;double[m][n]&lt;/code&gt; 换成&lt;strong&gt;行主序的扁平 &lt;code&gt;double[]&lt;/code&gt;&lt;/strong&gt;：内存连续，访问模式可预测。然后引入 &lt;code&gt;ThreadLocal&amp;lt;BufferHolder&amp;gt;&lt;/code&gt;，持有候选、历史等可复用 buffer——&lt;strong&gt;buffer 只增长不收缩&lt;/strong&gt;，避免每请求分配，同时每个线程持有自己的一份，无锁无争用。核心逻辑就几行：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class BufferHolder {
  double[] candidatesFlat = new double[0];
  double[] getCandidatesFlat(int required) {
    if (candidatesFlat.length &amp;lt; required) {
      candidatesFlat = new double[required];
    }
    return candidatesFlat;
  }
  // historyFlat 同理
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;光是这一步，批处理路径就变得可预测多了：分配少了，GC 压力小了，缓存局部性好了。剩下的问题回到最初的那个：矩阵乘到底用什么算？&lt;/p&gt;
&lt;h2 id=&#34;五第四步blas测试里很好生产里不行&#34;&gt;五、第四步：BLAS——测试里很好，生产里不行&lt;/h2&gt;
&lt;p&gt;显而易见的下一步是 BLAS（Basic Linear Algebra Subprograms）。隔离的微基准看起来很有希望，但一旦接进真实的批量打分路径，收益就没兑现。几个因素在作对：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;默认的 &lt;code&gt;netlib-java&lt;/code&gt; 路径走的是 F2J（Fortran 转 Java）的 BLAS，不是真正的原生实现；&lt;/li&gt;
&lt;li&gt;就算用原生 BLAS，也要付 setup 和 JNI 边界切换的开销；&lt;/li&gt;
&lt;li&gt;Java 的行主序和很多 BLAS 例程期望的列主序对不上，会引入转换和临时 buffer；&lt;/li&gt;
&lt;li&gt;这些额外的分配和拷贝在完整流水线里很扎眼，尤其是旁边还有 TensorFlow 的 embedding 计算。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BLAS 不是白试——它帮忙看清了时间到底花在哪。但它不是那种即插即用的胜利。他们需要的是：&lt;strong&gt;纯 Java、贴合扁平 buffer 架构、还能用上 SIMD&lt;/strong&gt; 的东西。&lt;/p&gt;
&lt;h2 id=&#34;六第五步jdk-vector-api&#34;&gt;六、第五步：JDK Vector API&lt;/h2&gt;
&lt;h3 id=&#34;先补一课jdk-vector-api-到底是什么&#34;&gt;先补一课：JDK Vector API 到底是什么&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;起点是 SIMD。&lt;/strong&gt; 现代 x86 CPU 都有向量寄存器，一条指令可以同时算多个数：AVX2 的 256 位寄存器一次装 4 个 double，AVX-512 的 512 位一次装 8 个。一条向量乘加指令顶 4-8 条标量指令——这就是 SIMD（Single Instruction, Multiple Data，单指令多数据），点积、矩阵乘这类规则的数值计算是它的主场。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Java 的痛点是：这些指令一直在硬件里，Java 程序员却够不着。&lt;/strong&gt; JIT 有自动向量化，但只对形状简单、能被它证明安全的循环生效，代码稍微复杂一点就悄悄退回标量，而且没有任何方式强制它发生；想要确定性的 SIMD，只能走 JNI 调原生库——代价就是上一步 BLAS 踩过的那些坑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vector API 是 Project Panama 的一部分&lt;/strong&gt;，从 JDK 16 起以孵化模块 &lt;code&gt;jdk.incubator.vector&lt;/code&gt; 的形式提供，到原文写作时仍在孵化（所以运行时要显式 &lt;code&gt;--add-modules&lt;/code&gt;）。它把 SIMD 变成了普通 Java 代码：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VectorSpecies&lt;/code&gt; 描述「一次算几个 lane」（lane 就是向量寄存器里的一个槽位），&lt;code&gt;SPECIES_PREFERRED&lt;/code&gt; 让运行时按当前 CPU 挑最优宽度；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DoubleVector.fromArray(...)&lt;/code&gt; 从数组加载一组 lane，&lt;code&gt;fma(b, acc)&lt;/code&gt; 做向量融合乘加，&lt;code&gt;reduceLanes(ADD)&lt;/code&gt; 把各 lane 归约成一个标量；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同一份代码的可移植性由 JIT 兜底&lt;/strong&gt;：编译时映射到宿主机器最好的指令集，AVX-512 机器跑 8 lane，AVX2 机器跑 4 lane，都不支持就退回标量。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph S[&amp;#34;标量路径 一条指令算一个数&amp;#34;]
        A1[&amp;#34;a0 乘 b0&amp;#34;] --&amp;gt; R1[&amp;#34;acc 逐个累加&amp;#34;]
        A2[&amp;#34;a1 乘 b1&amp;#34;] --&amp;gt; R1
        A3[&amp;#34;a2 乘 b2&amp;#34;] --&amp;gt; R1
        A4[&amp;#34;a3 乘 b3&amp;#34;] --&amp;gt; R1
    end
    subgraph V[&amp;#34;SIMD 路径 AVX2 一条指令算四个数&amp;#34;]
        B1[&amp;#34;向量寄存器加载&amp;lt;br/&amp;gt;a0 a1 a2 a3&amp;#34;] --&amp;gt; F[&amp;#34;一条 fma 指令&amp;#34;]
        B2[&amp;#34;向量寄存器加载&amp;lt;br/&amp;gt;b0 b1 b2 b3&amp;#34;] --&amp;gt; F
        F --&amp;gt; R2[&amp;#34;acc 四个 lane 同时累加&amp;#34;]
    end
    style F fill:#e8f5e9&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;一句话总结：&lt;strong&gt;它把「手写 intrinsics 的活」变成了「写普通循环的活」&lt;/strong&gt;——你不用知道目标机器是 SSE 还是 AVX-512，JIT 替你选。对他们来说更关键的是：&lt;strong&gt;纯 Java，没有原生依赖，没有 JNI 切换，写起来就是普通 Java 代码&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;跟他们的负载是绝配：embedding 已经搬进了扁平连续的 &lt;code&gt;double[]&lt;/code&gt; buffer，热循环被海量点积占满。最后一步就是用基于 Vector API 的纯 Java SIMD 实现换掉 BLAS——藏在一个小工厂后面，类加载时由 &lt;code&gt;MatMulFactory&lt;/code&gt; 选最优实现：&lt;code&gt;jdk.incubator.vector&lt;/code&gt; 可用就用 Vector API，否则退回一个高度优化的标量实现——循环展开的点积（同事 Patrick Strawderman 实现，灵感来自 Lucene 里的模式）。&lt;/p&gt;
&lt;p&gt;Vector API 版本的内层循环：用 &lt;code&gt;fma()&lt;/code&gt;（fused multiply-add）把 &lt;code&gt;a * b&lt;/code&gt; 累加进向量累加器；&lt;code&gt;DoubleVector.SPECIES_PREFERRED&lt;/code&gt; 让运行时自己挑适合机器的 lane 宽度（AVX2 上通常 4 个 double，AVX-512 上 8 个）。简化版：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; M; i++) {
  for (int j = 0; j &amp;lt; N; j++) {
    DoubleVector acc = DoubleVector.zero(SPECIES);
    int k = 0;
    for (; k + SPECIES.length() &amp;lt;= D; k += SPECIES.length()) {
      DoubleVector a = DoubleVector.fromArray(SPECIES, candidatesFlat, i*D + k);
      DoubleVector b = DoubleVector.fromArray(SPECIES, historyFlat, j*D + k);
      acc = a.fma(b, acc);  // 融合乘加
    }
    double dot = acc.reduceLanes(VectorOperators.ADD);
    // 处理 k..D-1 的尾部
    similaritiesFlat[i*N + j] = dot;
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;原来的一长串标量乘加，变成少量向量 &lt;code&gt;fma()&lt;/code&gt; 加一次归约——算法没变，CPU 向量单元的利用率完全不同。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph OLD[&amp;#34;原始路径 标量&amp;#34;]
        O1[&amp;#34;候选 embedding&amp;#34;] --&amp;gt; O3[&amp;#34;逐个算余弦相似度&amp;#34;]
        O2[&amp;#34;历史 embedding 逐个取&amp;#34;] --&amp;gt; O3
        O3 --&amp;gt; O4[&amp;#34;M 乘 N 次独立点积&amp;lt;br/&amp;gt;分散内存访问 缓存不友好&amp;#34;]
    end
    subgraph NEW[&amp;#34;优化后路径 SIMD&amp;#34;]
        N1[&amp;#34;候选装入扁平 buffer A&amp;lt;br/&amp;gt;M×D 行主序&amp;#34;] --&amp;gt; N3[&amp;#34;归一化为单位向量&amp;#34;]
        N2[&amp;#34;历史装入扁平 buffer B&amp;lt;br/&amp;gt;N×D 行主序&amp;#34;] --&amp;gt; N3
        N3 --&amp;gt; N4[&amp;#34;一次矩阵乘 C = A × Bᵀ&amp;lt;br/&amp;gt;Vector API fma 累加&amp;#34;]
        N4 --&amp;gt; N5[&amp;#34;每行取最大值&amp;lt;br/&amp;gt;novelty = 1 - maxSim&amp;#34;]
    end
    O4 -.-&amp;gt;|&amp;#34;重构计算形状&amp;#34;| N1
    style O4 fill:#ffcccc
    style N4 fill:#e8f5e9&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h2 id=&#34;七回退与安全孵化特性的生产姿势&#34;&gt;七、回退与安全：孵化特性的生产姿势&lt;/h2&gt;
&lt;p&gt;Vector API 还在孵化，运行时要加 &lt;code&gt;--add-modules=jdk.incubator.vector&lt;/code&gt; 才能用。他们不想让正确性和可用性绑在这个 flag 上，所以把回退行为显式设计出来：启动时探测 Vector API 支持，有就走 SIMD 批量矩阵乘，没有就走优化过的标量路径，单视频请求继续用逐条实现。运营故事很干净：服务可以选择启用 Vector API 拿满性能，不启用系统也照样安全可预测。&lt;/p&gt;
&lt;h2 id=&#34;八生产结果&#34;&gt;八、生产结果&lt;/h2&gt;
&lt;p&gt;完整方案（批处理 + 扁平 buffer + ThreadLocal 复用 + Vector API）在跑生产流量的金丝雀上测得：&lt;strong&gt;CPU 利用率下降约 7%，平均延迟下降约 12%&lt;/strong&gt;；为了归一化吞吐差异还跟踪了 CPU/RPS（每 RPS 消耗的 CPU），&lt;strong&gt;改善约 10%&lt;/strong&gt;——同样的流量可以少用约 10% 的 CPU，全量上线后数据保持住了。在函数级别，这个特征占的 CPU 从最初的 &lt;strong&gt;7.5% 降到约 1%&lt;/strong&gt;。汇编层面的变化也很直白：从循环展开的标量点积，变成 AVX-512 硬件上的向量化矩阵乘。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;工程实践联想&#34;&gt;工程实践联想&lt;/h2&gt;
&lt;p&gt;这篇文章值得读两遍：第一遍看技术，第二遍看顺序。沉淀下来的启示：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;优化顺序是：计算形状 → 数据布局 → 分配策略 → 计算内核。&lt;/strong&gt; 他们最后换的才是内核（Vector API），但前面三步缺任何一步，换内核都拿不到收益。那个 5% 的回退也说明：跳过布局直接批处理，等于同时引入新开销和新瓶颈。做性能优化先问“形状对不对”，再问“数据在内存里躺得对不对”，最后才轮到“用什么算”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;微基准通过不等于生产有效。&lt;/strong&gt; BLAS 在隔离测试里很有希望，一进真实路径就被 JNI、布局转换、临时分配吃光收益。任何库的 benchmark 都只对它自己的测试负责；决策必须在真实流量路径里做，最好带着上下游（他们旁边还跑着 TensorFlow 的 embedding 计算）一起测。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;double[][]&lt;/code&gt; 到扁平 &lt;code&gt;double[]&lt;/code&gt; 是 Java 性能工程的经典动作。&lt;/strong&gt; 二维数组是“数组的数组”，行指针分散，缓存局部性差；扁平数组行主序连续、访问可预测，配合 ThreadLocal buffer 只增不缩，每请求分配降成零——这套组合在 Lucene、Netty 里到处都是，值得变成肌肉记忆。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流量形状决定优化对象。&lt;/strong&gt; 98% 的请求是单视频，只看中位数的话批处理毫无价值；但那 2% 的大批量扛了一半总计算量。看 p50 优化和看总量优化是两个问题，先埋点搞清楚自己的流量长什么样。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;孵化/预览特性的正确姿势：工厂加探测加回退。&lt;/strong&gt; 不赌 &lt;code&gt;--add-modules&lt;/code&gt; 一定加得上，启动时探测、随时退回标量实现，“试用新 JDK 特性”就从一次豪赌变成一个配置项。这个模式可以复用到任何“新能力可用性不确定”的场景。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;顺带一个私货：这篇文章最好的地方是诚实——它把“我们以为做完了结果慢了 5%”和“BLAS 没成”都写了出来。性能工程的真实过程就是这样：每一步的正确性都要靠金丝雀和生产数据确认，不存在一步到位。那些只写成功结果的优化文章，参考价值反而低。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;本篇小结&#34;&gt;本篇小结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：Netflix Ranker 的视频惊喜度打分（候选与历史 embedding 的余弦相似度取 max）占单节点 CPU 约 7.5%，热点是 O(M×N) 的逐对点积；流量形状 98% 单视频、2% 大批量，但计算总量 50:50。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径&lt;/strong&gt;：M×N 次点积重构成矩阵乘 &lt;code&gt;C = A × Bᵀ&lt;/code&gt;（行归一化后即余弦相似度矩阵）→ 金丝雀回退约 5%（&lt;code&gt;double[][]&lt;/code&gt; 分配与 GC、非连续布局、标量内核）→ 扁平 &lt;code&gt;double[]&lt;/code&gt; 行主序加 &lt;code&gt;ThreadLocal&amp;lt;BufferHolder&amp;gt;&lt;/code&gt; 复用（buffer 只增不缩）→ BLAS 因 F2J 非原生、JNI 开销、行列主序不匹配而放弃 → 终解 JDK Vector API（&lt;code&gt;fma()&lt;/code&gt; 累加、&lt;code&gt;SPECIES_PREFERRED&lt;/code&gt; 自适应 lane 宽度、JIT 映射 SSE/AVX2/AVX-512）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全姿势&lt;/strong&gt;：&lt;code&gt;MatMulFactory&lt;/code&gt; 启动时探测，不可用则回退到 Lucene 风格的循环展开标量实现，不依赖 &lt;code&gt;--add-modules&lt;/code&gt; 一定在场。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产结果&lt;/strong&gt;：CPU 降约 7%、平均延迟降约 12%、CPU/RPS 改善约 10%、该特征的函数级 CPU 占比从 7.5% 降到约 1%；汇编从标量循环展开点积变成 AVX-512 向量化矩阵乘。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心结论&lt;/strong&gt;：优化拼的不是“最快的库”，而是把计算形状、内存布局、分配策略这些基本功做对——这些就位之后，Vector API 才成为那个“顺其自然的选择”。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://netflixtechblog.com/optimizing-recommendation-systems-with-jdks-vector-api-30d2830401ec&#34;&gt;Optimizing Recommendation Systems with JDK&amp;rsquo;s Vector API — Netflix Technology Blog&lt;/a&gt;（原文，作者 Harshad Sane，2026-03-03）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/338&#34;&gt;JEP 338: Vector API (Incubator) — OpenJDK&lt;/a&gt;（Vector API 的 JEP 入口，含 SPECIES、fma、lane 等概念定义；后续孵化迭代见 JEP 417/426/448/460）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/fommil/netlib-java&#34;&gt;netlib-java — GitHub&lt;/a&gt;（文中提到的 Java BLAS 库，默认 F2J 路径的出处）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lucene.apache.org/&#34;&gt;Apache Lucene&lt;/a&gt;（回退路径标量点积实现的灵感来源，Lucene 在向量检索里大量使用循环展开和 Panama Vector API）&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>我开发的keCache：面向 Spring Boot 3 的 L1 &#43; L2 两级缓存框架</title>
      <link>https://ftmao.com/blog/posts/kecache-intro/</link>
      <pubDate>Thu, 04 Sep 2025 14:16:44 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/kecache-intro/</guid>
      <description>&lt;p&gt;在高并发 Java 业务里，缓存几乎是标配。但“用缓存”这件事从来不是只有“加 Redis”一种答案：本地缓存快却解决不了多机一致，Redis 能共享却多了网络与序列化开销。我最近实现一个把两者结合起来、并针对集群一致性做了不少细节处理的小而美框架 —— &lt;strong&gt;keCache&lt;/strong&gt;，现在做一次系统梳理。&lt;/p&gt;
&lt;h2 id=&#34;一kecache-是什么&#34;&gt;一、keCache 是什么&lt;/h2&gt;
&lt;p&gt;keCache 是一个基于 &lt;strong&gt;Java 17 + Spring Boot 3.x + Caffeine + Redisson&lt;/strong&gt; 的本地缓存框架，采用 &lt;strong&gt;L1（Caffeine 本地缓存）+ L2（Redis 分布式缓存）&lt;/strong&gt; 两级架构，同时提供：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;声明式注解（&lt;code&gt;@LocalCacheable&lt;/code&gt;、&lt;code&gt;@LocalCacheEvict&lt;/code&gt;、&lt;code&gt;@LocalCacheClear&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;编程式 API（&lt;code&gt;CacheTemplate&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;集群失效广播（Redisson Pub/Sub）&lt;/li&gt;
&lt;li&gt;空值保护（防止缓存穿透）&lt;/li&gt;
&lt;li&gt;数据一致性校准 SPI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它的定位不是“又一个 Redis 客户端”，而是“让本地缓存能在集群环境下安全使用”的胶水层。&lt;/p&gt;
&lt;h3 id=&#34;11-整体架构&#34;&gt;1.1 整体架构&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    App[业务 Service] --&amp;gt;|注解或 API| Facade[&amp;#34;CacheTemplate / AOP 切面&amp;#34;]
    Facade --&amp;gt; L1{L1 Caffeine}
    L1 --&amp;gt;|命中| Return[返回数据]
    L1 --&amp;gt;|未命中| L2{L2 Redis}
    L2 --&amp;gt;|命中| Deserialize[Jackson 反序列化]
    Deserialize --&amp;gt; Backfill[回填 L1]
    Backfill --&amp;gt; Return
    L2 --&amp;gt;|未命中| Load[&amp;#34;执行方法 / 加载数据&amp;#34;]
    Load --&amp;gt; Write[&amp;#34;写入 L1 &amp;amp; L2&amp;#34;]
    Write --&amp;gt; Return
    PubSub[&amp;#34;Redisson Pub/Sub&amp;#34;] --&amp;gt;|失效事件| L1
    Calibration[CalibrationProvider SPI] --&amp;gt;|定时校准| L2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h2 id=&#34;二开源地址&#34;&gt;二、开源地址&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Gitee：&lt;a href=&#34;https://gitee.com/soulnew/ke-cache&#34;&gt;https://gitee.com/soulnew/ke-cache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;三为什么需要-kecache与纯-redis--纯-caffeine-的对比&#34;&gt;三、为什么需要 keCache？与纯 Redis / 纯 Caffeine 的对比&lt;/h2&gt;
&lt;p&gt;很多团队在选择缓存方案时，常常在“纯 Redis”和“纯 Caffeine”之间摇摆。keCache 试图在二者之间取得平衡。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;纯 Caffeine&lt;/th&gt;
					&lt;th&gt;纯 Redis&lt;/th&gt;
					&lt;th&gt;keCache（L1 + L2）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;读取延迟&lt;/td&gt;
					&lt;td&gt;极低（内存内）&lt;/td&gt;
					&lt;td&gt;较高（网络 + 序列化）&lt;/td&gt;
					&lt;td&gt;热数据近似 Caffeine，冷数据走 Redis&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;单机内存压力&lt;/td&gt;
					&lt;td&gt;大（全部数据在本 JVM）&lt;/td&gt;
					&lt;td&gt;小（Redis 承担）&lt;/td&gt;
					&lt;td&gt;小（只有热数据在本地）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;集群一致性&lt;/td&gt;
					&lt;td&gt;无，各节点独立加载&lt;/td&gt;
					&lt;td&gt;强一致&lt;/td&gt;
					&lt;td&gt;最终一致，写操作广播失效&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;缓存穿透保护&lt;/td&gt;
					&lt;td&gt;需自行实现&lt;/td&gt;
					&lt;td&gt;需自行实现&lt;/td&gt;
					&lt;td&gt;内置 NullValue 短 TTL 保护&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多机共享&lt;/td&gt;
					&lt;td&gt;不支持&lt;/td&gt;
					&lt;td&gt;支持&lt;/td&gt;
					&lt;td&gt;支持（L2 Redis）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;适用场景&lt;/td&gt;
					&lt;td&gt;只读/准静态数据、单节点&lt;/td&gt;
					&lt;td&gt;写多读少、需要强一致&lt;/td&gt;
					&lt;td&gt;读多写少、可接受最终一致&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;31-相比纯-caffeine-的好处&#34;&gt;3.1 相比纯 Caffeine 的好处&lt;/h3&gt;
&lt;p&gt;Caffeine 是 JVM 内缓存的佼佼者，但它天然只服务于当前进程。在集群部署时：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;每台机器都要独立加载数据&lt;/strong&gt;，导致数据库在启动/预热时被打 N 次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据更新后其他节点无法感知&lt;/strong&gt;，容易出现 A 节点已更新、B 节点仍读旧值的“ stale data ”问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存占用随节点数线性增长&lt;/strong&gt;，同样一份热数据在每个 JVM 都存一份。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;keCache 用 L2 Redis 作为共享底座：首次加载从 Redis 取，后续读请求命中本地 Caffeine；写操作时通过 Redis Pub/Sub 通知其他节点清除本地缓存。这样既保留了 Caffeine 的本地速度，又解决了多机一致性难题。&lt;/p&gt;
&lt;h3 id=&#34;32-相比纯-redis-的好处&#34;&gt;3.2 相比纯 Redis 的好处&lt;/h3&gt;
&lt;p&gt;Redis 虽然性能好，但毕竟跨进程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;网络 RTT 不可忽视&lt;/strong&gt;：高频读取时，哪怕 1ms 也会累积成可观的延迟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序列化/反序列化开销&lt;/strong&gt;：Java 对象要先转成 JSON/二进制，再还原，CPU 和 GC 都有压力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;热点 Key 风险&lt;/strong&gt;：大促期间某些热 Key 会被反复打到同一个 Redis 节点。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;keCache 把最热的数据放在本地 JVM 里，只有本地未命中或更新时才访问 Redis，显著降低网络和序列化开销，也分散了 Redis 热点压力。&lt;/p&gt;
&lt;h3 id=&#34;33-方案选型速查&#34;&gt;3.3 方案选型速查&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    Start([开始选缓存方案]) --&amp;gt; Q1{是否需要多机共享?}
    Q1 --&amp;gt;|否| Q2{数据是否只读 /&amp;lt;br/&amp;gt;准静态?}
    Q2 --&amp;gt;|是| Caffeine[纯 Caffeine&amp;lt;br/&amp;gt;简单高效]
    Q2 --&amp;gt;|否| Q3{能否接受最终一致?}
    Q3 --&amp;gt;|能| KeCache[keCache&amp;lt;br/&amp;gt;L1 + L2 加速]
    Q3 --&amp;gt;|否| RedisOnly[纯 Redis /&amp;lt;br/&amp;gt;DB 强一致]
    Q1 --&amp;gt;|是| Q4{写多读少 /&amp;lt;br/&amp;gt;强一致?}
    Q4 --&amp;gt;|是| RedisOnly
    Q4 --&amp;gt;|否| KeCache&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;34-一句话总结&#34;&gt;3.4 一句话总结&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;keCache 适合“读多写少、能接受最终一致、希望兼顾本地速度与集群共享”的业务场景；如果你的业务对实时一致性要求极高（如库存扣减、金融记账），建议直接走 Redis 或数据库方案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;四核心能力速览&#34;&gt;四、核心能力速览&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;能力&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;声明式缓存&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@LocalCacheable&lt;/code&gt; / &lt;code&gt;@LocalCacheEvict&lt;/code&gt; / &lt;code&gt;@LocalCacheClear&lt;/code&gt;，支持 SpEL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多键映射&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@LocalCacheable.Multi&lt;/code&gt; 一次方法返回构建多个索引 key&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;编程式 API&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CacheTemplate.get/put/evict/clear&lt;/code&gt;，类型安全&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;二级缓存&lt;/td&gt;
					&lt;td&gt;L1 Caffeine + L2 Redis，读时先 L1 再 L2，未命中回调加载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;空值保护&lt;/td&gt;
					&lt;td&gt;DB 不存在的 key 缓存短 TTL 的 NullValue，防止缓存穿透&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;集群一致性&lt;/td&gt;
					&lt;td&gt;Redisson Pub/Sub 广播失效事件，其他 JVM 清除本地 L1&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;自失效屏蔽&lt;/td&gt;
					&lt;td&gt;事件携带 &lt;code&gt;sourceId&lt;/code&gt;，本机忽略自己发出的事件，避免误清本地缓存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;广播风暴防护&lt;/td&gt;
					&lt;td&gt;远程失效事件只清 L1，不再次广播&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;数据校准&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CalibrationProvider&lt;/code&gt; SPI，支持时间戳扫描或 CDC 扩展&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;41-读取流程&#34;&gt;4.1 读取流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    Start([读请求]) --&amp;gt; L1[查 L1 Caffeine]
    L1 --&amp;gt;|命中| A[直接返回]
    L1 --&amp;gt;|未命中| L2[查 L2 Redis]
    L2 --&amp;gt;|命中| Deserialize[Jackson 反序列化]
    Deserialize --&amp;gt; Backfill[回填 L1]
    Backfill --&amp;gt; Return[返回数据]
    L2 --&amp;gt;|未命中| Method[执行真实方法]
    Method --&amp;gt; NullCheck{结果是否为 null?}
    NullCheck --&amp;gt;|是| NullVal[写入 NullValue 到 L1/L2&amp;lt;br/&amp;gt;TTL: null-value-ttl]
    NullCheck --&amp;gt;|否| RealVal[写入真实值到 L1/L2]
    NullVal --&amp;gt; ReturnNull[返回 null]
    RealVal --&amp;gt; Return&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;42-写入与集群失效广播流程&#34;&gt;4.2 写入与集群失效广播流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as 节点 A（写请求）
    participant DB as 数据库
    participant R as Redis（L2）
    participant P as Redis Pub/Sub
    participant B as 节点 B
    participant C as 节点 C

    A-&amp;gt;&amp;gt;DB: 更新数据
    A-&amp;gt;&amp;gt;R: 删除/覆盖 L2 Key
    A-&amp;gt;&amp;gt;P: 发布 EvictEvent（携带 sourceId）
    A-&amp;gt;&amp;gt;A: 清除本机 L1

    P-&amp;gt;&amp;gt;B: 接收 EvictEvent
    B-&amp;gt;&amp;gt;B: sourceId ≠ 本机？
    B-&amp;gt;&amp;gt;B: 清除本地 L1
    Note over B: 只清 L1，不再次广播

    P-&amp;gt;&amp;gt;C: 接收 EvictEvent
    C-&amp;gt;&amp;gt;C: sourceId ≠ 本机？
    C-&amp;gt;&amp;gt;C: 清除本地 L1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h2 id=&#34;五快速开始&#34;&gt;五、快速开始&lt;/h2&gt;
&lt;h3 id=&#34;51-引入-starter&#34;&gt;5.1 引入 Starter&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.keke&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kecache-spring-boot-starter&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0-SNAPSHOT&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-启用缓存&#34;&gt;5.2 启用缓存&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.keke.kecache.starter.annotation.EnableKeCache;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.springframework.boot.autoconfigure.SpringBootApplication;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@SpringBootApplication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@EnableKeCache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Application&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SpringApplication.&lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;(Application.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;, args);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;53-基础配置&#34;&gt;5.3 基础配置&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;local-cache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kecache.sample&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;default-ttl&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;null-value-ttl&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;caffeine&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;default&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;maximum-size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;expire-after-write&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;record-stats&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;redis&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;address&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redis://127.0.0.1:6379&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;broadcast-channel&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;kecache:evict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;default-ttl&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;calibration&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;enabled&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;54-声明式使用示例&#34;&gt;5.4 声明式使用示例&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AccountService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheable&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cacheNames &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;id_&amp;#39; + #accountId&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        unless &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#result == null&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; AccountInfo &lt;span style=&#34;color:#a6e22e&#34;&gt;getById&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; accountId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; accountDao.&lt;span style=&#34;color:#a6e22e&#34;&gt;loadById&lt;/span&gt;(accountId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 一次查询构建多个索引 key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheable.Multi&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheable&lt;/span&gt;(cacheNames &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;, key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;id_&amp;#39; + #accountId&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheable&lt;/span&gt;(cacheNames &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;, key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;nm_&amp;#39; + #result.accountName&amp;#34;&lt;/span&gt;, unless &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;#result == null&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; AccountInfo &lt;span style=&#34;color:#a6e22e&#34;&gt;getByIdMultiIndex&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; accountId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; accountDao.&lt;span style=&#34;color:#a6e22e&#34;&gt;loadById&lt;/span&gt;(accountId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheEvict&lt;/span&gt;(cacheNames &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;, key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#39;id_&amp;#39; + #accountId&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateById&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; accountId, AccountInfo info) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        accountDao.&lt;span style=&#34;color:#a6e22e&#34;&gt;update&lt;/span&gt;(accountId, info);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@LocalCacheClear&lt;/span&gt;(cacheNames &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clearAll&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        accountDao.&lt;span style=&#34;color:#a6e22e&#34;&gt;clear&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;55-编程式使用&#34;&gt;5.5 编程式使用&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AccountService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Autowired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; CacheTemplate cacheTemplate;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; AccountInfo &lt;span style=&#34;color:#a6e22e&#34;&gt;getById&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; accountId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; cacheTemplate.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tve_account_info&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id_&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; accountId, key &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; accountDao.&lt;span style=&#34;color:#a6e22e&#34;&gt;loadById&lt;/span&gt;(accountId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h2 id=&#34;六模块结构&#34;&gt;六、模块结构&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    Parent[kecache-parent] --&amp;gt; API[kecache-api&amp;lt;br/&amp;gt;注解 / SPI / 配置模型 / 事件]
    Parent --&amp;gt; Core[kecache-core&amp;lt;br/&amp;gt;统一门面 / Key策略 / 空值保护]
    Parent --&amp;gt; Caffeine[kecache-provider-caffeine&amp;lt;br/&amp;gt;L1 Caffeine 实现]
    Parent --&amp;gt; Redisson[kecache-provider-redisson&amp;lt;br/&amp;gt;L2 Redis + 失效广播]
    Parent --&amp;gt; Calibration[kecache-calibration&amp;lt;br/&amp;gt;数据一致性校准]
    Parent --&amp;gt; Starter[kecache-spring-boot-starter&amp;lt;br/&amp;gt;自动配置 + AOP]
    Parent --&amp;gt; Sample[kecache-spring-boot-sample&amp;lt;br/&amp;gt;示例应用 + 测试]

    Starter --&amp;gt; API
    Starter --&amp;gt; Core
    Core --&amp;gt; Caffeine
    Core --&amp;gt; Redisson
    Core --&amp;gt; Calibration&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h2 id=&#34;七关键约定写代码前必读&#34;&gt;七、关键约定（写代码前必读）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L1 key&lt;/strong&gt;：&lt;code&gt;cacheName#dataKey&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L2 key&lt;/strong&gt;：&lt;code&gt;{namespace|cacheName}:dataKey&lt;/code&gt;（使用 hash tag 避免集群模式下 slot 分散）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认读写策略&lt;/strong&gt;：Cache-Aside&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失效广播频道&lt;/strong&gt;：默认 &lt;code&gt;kecache:evict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序列化&lt;/strong&gt;：Jackson 启用 default typing，保留类型信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;八使用注意点与避坑指南&#34;&gt;八、使用注意点与避坑指南&lt;/h2&gt;
&lt;h3 id=&#34;81-redis-是硬性依赖&#34;&gt;8.1 Redis 是硬性依赖&lt;/h3&gt;
&lt;p&gt;L2 缓存和失效广播都依赖 Redis，测试与运行前请确保 Redis 可达。运行全部测试需要本地启动 Redis：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mvn clean test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;82-集群一致性是最终一致不是实时一致&#34;&gt;8.2 集群一致性是“最终一致”，不是实时一致&lt;/h3&gt;
&lt;p&gt;本地缓存存在一致窗口：A 节点写入并广播，B 节点收到消息前仍可能读到旧值。对于库存、余额、订单状态等强一致场景，建议绕过本地缓存，直接走 Redis 或数据库。&lt;/p&gt;
&lt;h3 id=&#34;83-空值保护是把双刃剑&#34;&gt;8.3 空值保护是把双刃剑&lt;/h3&gt;
&lt;p&gt;默认对 &lt;code&gt;null&lt;/code&gt; 结果缓存 5 分钟，可防止缓存穿透。但如果业务刚刚从 DB 写入了一条新记录，而之前查询时正好命中了 NullValue，就会出现“写入后 5 分钟内查不到”的延迟。需要时可：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缩短 &lt;code&gt;null-value-ttl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在写入后主动 &lt;code&gt;@LocalCacheEvict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;对新增数据敏感的 cacheName 关闭空值保护&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;84-序列化类型变更要兼容&#34;&gt;8.4 序列化类型变更要兼容&lt;/h3&gt;
&lt;p&gt;Redis 层使用 Jackson 并保留类型信息。如果缓存对象发生不兼容的类结构变更（如字段类型改变、删除字段、包名调整），旧数据反序列化可能失败。建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对缓存 DTO 做版本控制&lt;/li&gt;
&lt;li&gt;重大变更时清空 Redis 对应 key 或切换 &lt;code&gt;cacheName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;避免把接口、匿名类、动态代理对象放入缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;85-注意-key-的命名空间隔离&#34;&gt;8.5 注意 Key 的命名空间隔离&lt;/h3&gt;
&lt;p&gt;L2 key 格式为 &lt;code&gt;{namespace|cacheName}:dataKey&lt;/code&gt;。如果多个业务共用同一个 Redis 实例，务必设置不同的 &lt;code&gt;namespace&lt;/code&gt;，否则可能出现 Key 冲突。&lt;/p&gt;
&lt;h3 id=&#34;86-localcacheablemulti-的-result-引用要判空&#34;&gt;8.6 &lt;code&gt;@LocalCacheable.Multi&lt;/code&gt; 的 &lt;code&gt;result&lt;/code&gt; 引用要判空&lt;/h3&gt;
&lt;p&gt;多索引注解里如果通过 &lt;code&gt;#result.xxx&lt;/code&gt; 取字段，必须配合 &lt;code&gt;unless = &amp;quot;#result == null&amp;quot;&lt;/code&gt;，否则方法返回 null 时会触发 SpEL 计算异常。&lt;/p&gt;
&lt;h3 id=&#34;87-aop-自调用不会生效&#34;&gt;8.7 AOP 自调用不会生效&lt;/h3&gt;
&lt;p&gt;和 Spring Cache 一样，keCache 的注解基于 AOP。同类方法内部调用（&lt;code&gt;this.method()&lt;/code&gt;）不会走代理，缓存注解不会生效。需要时注入自身代理或改用编程式 &lt;code&gt;CacheTemplate&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;88-谨慎使用-localcacheclear&#34;&gt;8.8 谨慎使用 &lt;code&gt;@LocalCacheClear&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;@LocalCacheClear&lt;/code&gt; 会清空整个 &lt;code&gt;cacheName&lt;/code&gt; 的本地缓存并广播。在高并发场景下，频繁全量清除会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;导致本地缓存命中率骤降&lt;/li&gt;
&lt;li&gt;引发集体回源，DB 压力陡增&lt;/li&gt;
&lt;li&gt;产生大量广播消息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议优先使用 &lt;code&gt;@LocalCacheEvict&lt;/code&gt; 精确驱逐。&lt;/p&gt;
&lt;h3 id=&#34;89-校准任务不是开箱即用的&#34;&gt;8.9 校准任务不是开箱即用的&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;CalibrationProvider&lt;/code&gt; 目前只是 SPI 骨架，需要业务方自己实现 &lt;code&gt;fetchChanges(Instant start, Instant end)&lt;/code&gt; 逻辑（如查变更表、CDC 日志）。开启 &lt;code&gt;local-cache.calibration.enabled=true&lt;/code&gt; 后才会按 Cron 触发。&lt;/p&gt;
&lt;h3 id=&#34;810-当前-redis-部署模式限制&#34;&gt;8.10 当前 Redis 部署模式限制&lt;/h3&gt;
&lt;p&gt;自动配置里的 &lt;code&gt;RedissonClient&lt;/code&gt; 目前只实现了 &lt;strong&gt;single 单节点&lt;/strong&gt;模式。如果生产环境使用 Redis Cluster、Sentinel 或主从，需要自行扩展配置。&lt;/p&gt;
&lt;h3 id=&#34;811-关注-caffeine-内存与统计&#34;&gt;8.11 关注 Caffeine 内存与统计&lt;/h3&gt;
&lt;p&gt;默认开启 &lt;code&gt;record-stats: true&lt;/code&gt;，本地缓存命中率、加载次数等都会被记录。建议配合 actuator / Micrometer（后续可扩展）监控：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;maximum-size&lt;/code&gt; 是否设置合理&lt;/li&gt;
&lt;li&gt;本地缓存命中率&lt;/li&gt;
&lt;li&gt;加载耗时与异常率&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;九许可证&#34;&gt;九、许可证&lt;/h2&gt;
&lt;p&gt;keCache 采用 &lt;strong&gt;MIT&lt;/strong&gt; 协议开源。&lt;/p&gt;
&lt;h2 id=&#34;十总结&#34;&gt;十、总结&lt;/h2&gt;
&lt;p&gt;keCache 是一个目标聚焦、实现克制的两级缓存框架：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果你想要 &lt;strong&gt;Caffeine 的速度 + Redis 的共享能力&lt;/strong&gt;，它值得尝试。&lt;/li&gt;
&lt;li&gt;如果你希望 &lt;strong&gt;声明式注解即可解决集群本地缓存一致性问题&lt;/strong&gt;，它提供了失效广播与自失效屏蔽。&lt;/li&gt;
&lt;li&gt;如果你的业务对 &lt;strong&gt;实时一致性零容忍&lt;/strong&gt;，请把它当作“加速层”而非“唯一数据源”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;有兴趣可以直接到 Gitee 查看源码与示例：&lt;a href=&#34;https://gitee.com/soulnew/ke-cache&#34;&gt;https://gitee.com/soulnew/ke-cache&lt;/a&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>我开发的KE Reliable Executor：基于 Redis/MySQL 的 Spring Boot 可靠任务执行框架</title>
      <link>https://ftmao.com/blog/posts/ke-reliable-executor/</link>
      <pubDate>Mon, 04 Aug 2025 17:10:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/ke-reliable-executor/</guid>
      <description>&lt;h1 id=&#34;我开发的ke-reliable-executor基于-redismysql-的-spring-boot-可靠任务执行框架&#34;&gt;我开发的KE Reliable Executor：基于 Redis/MySQL 的 Spring Boot 可靠任务执行框架&lt;/h1&gt;
&lt;p&gt;在微服务里，异步任务随处可见：支付后通知第三方、订单超时取消、批量对账、报表生成……这些任务有一个共同诉求——&lt;strong&gt;不能丢、不能重复执行、失败能重试、状态要能查&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;传统做法是直接引入 Kafka、RocketMQ 这类重型消息中间件。但对于很多中小型业务来说，这未免太重：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要额外维护 Broker、Topic、消费组；&lt;/li&gt;
&lt;li&gt;团队要熟悉一套新的运维和监控体系；&lt;/li&gt;
&lt;li&gt;为了“可靠”还要配置死信队列、事务消息等高级特性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的系统里已经有 Redis（或 MySQL），并且任务量级尚未达到 MQ 的吞吐上限，那么完全可以基于 Redis 搭一个&lt;strong&gt;轻量且可靠的异步任务执行框架&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我开发是这样一个框架：&lt;strong&gt;KE Reliable Executor&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;开源地址&lt;/strong&gt;：https://gitee.com/soulnew/reliableexecutor&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一为什么需要这个工具&#34;&gt;一、为什么需要这个工具？&lt;/h2&gt;
&lt;p&gt;先回顾一下常见异步任务场景里的痛点：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;痛点&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;任务丢失&lt;/td&gt;
					&lt;td&gt;服务重启或执行线程异常退出后，未执行的任务找不到了&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;重复执行&lt;/td&gt;
					&lt;td&gt;多实例部署时，同一个任务被多个节点同时消费&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;失败即结束&lt;/td&gt;
					&lt;td&gt;业务异常或网络抖动后，没有自动重试机制&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;状态黑盒&lt;/td&gt;
					&lt;td&gt;无法查询任务当前是成功、失败、执行中还是已取消&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;接入成本高&lt;/td&gt;
					&lt;td&gt;引入 MQ 需要改代码、加依赖、搭集群&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;KE Reliable Executor 要解决的就是这些问题。它把“可靠任务执行”抽象成一个 Spring Boot Starter：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;业务方只负责&lt;strong&gt;定义任务处理器&lt;/strong&gt;和&lt;strong&gt;提交任务&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;框架负责&lt;strong&gt;存储、调度、加锁、重试、状态流转、清理&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;所有状态都可通过 API 查询，也可通过 Micrometer 指标监控。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;适合什么场景&#34;&gt;适合什么场景？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;已有 Redis/MySQL，不想额外维护 MQ；&lt;/li&gt;
&lt;li&gt;任务量中等，对可靠性和重试有要求；&lt;/li&gt;
&lt;li&gt;需要任务状态可查询、可人工介入；&lt;/li&gt;
&lt;li&gt;团队希望以最小成本获得“类 MQ”的可靠执行能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;不适合什么场景&#34;&gt;不适合什么场景？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;海量消息、高吞吐（Kafka/RocketMQ 更合适）；&lt;/li&gt;
&lt;li&gt;严格顺序消费（ZSET 按时间排序，不保证业务绝对顺序）；&lt;/li&gt;
&lt;li&gt;需要复杂路由、死信队列、事务消息等高级 MQ 特性。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二与竞品的对比&#34;&gt;二、与竞品的对比&lt;/h2&gt;
&lt;h3 id=&#34;21-与-kafka--rocketmq-对比&#34;&gt;2.1 与 Kafka / RocketMQ 对比&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;Kafka / RocketMQ&lt;/th&gt;
					&lt;th&gt;KE Reliable Executor&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;基础设施&lt;/td&gt;
					&lt;td&gt;需要独立 Broker 集群&lt;/td&gt;
					&lt;td&gt;复用 Redis 或 MySQL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;运维成本&lt;/td&gt;
					&lt;td&gt;高（Topic、消费组、监控）&lt;/td&gt;
					&lt;td&gt;低（Spring Boot Starter）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;接入成本&lt;/td&gt;
					&lt;td&gt;需要学习 SDK 与语义&lt;/td&gt;
					&lt;td&gt;注解 + 注入接口即可&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;吞吐量&lt;/td&gt;
					&lt;td&gt;极高&lt;/td&gt;
					&lt;td&gt;中等&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;状态查询&lt;/td&gt;
					&lt;td&gt;需自建审计表&lt;/td&gt;
					&lt;td&gt;内置状态查询&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;重试机制&lt;/td&gt;
					&lt;td&gt;需配置消费重试&lt;/td&gt;
					&lt;td&gt;内置可配置重试&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;适用阶段&lt;/td&gt;
					&lt;td&gt;大型业务、高并发&lt;/td&gt;
					&lt;td&gt;中小型业务、快速落地&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结论：KE Reliable Executor 不是取代 MQ，而是&lt;strong&gt;在“已有 Redis/MySQL、任务量可控”的场景下，提供一个更轻量的选择&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;22-与-spring-scheduled--数据库轮询对比&#34;&gt;2.2 与 Spring &lt;code&gt;@Scheduled&lt;/code&gt; + 数据库轮询对比&lt;/h3&gt;
&lt;p&gt;很多团队最早会用数据库表 + &lt;code&gt;@Scheduled&lt;/code&gt; 轮询来实现异步任务。这种做法的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无分布式锁&lt;/strong&gt;：多实例同时轮询会重复执行；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无状态机&lt;/strong&gt;：任务状态全靠字段控制，容易混乱；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无统一重试策略&lt;/strong&gt;：重试间隔、重试次数需要手写；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无优雅关闭&lt;/strong&gt;：应用关闭时可能丢失正在执行的任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;KE Reliable Executor 把这些都封装好了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Master 选举 + 任务级锁保证不重复；&lt;/li&gt;
&lt;li&gt;明确的状态机（CREATED → RUNNING → PENDING/COMPLETED/FAILED）；&lt;/li&gt;
&lt;li&gt;可配置的重试间隔与次数；&lt;/li&gt;
&lt;li&gt;优雅释放锁与线程池关闭。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;23-与-xxl-job--powerjob-对比&#34;&gt;2.3 与 XXL-JOB / PowerJob 对比&lt;/h3&gt;
&lt;p&gt;XXL-JOB 和 PowerJob 是优秀的分布式任务调度平台，但它们更适合&lt;strong&gt;定时任务&lt;/strong&gt;和&lt;strong&gt;调度中心&lt;/strong&gt;模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要单独部署调度中心；&lt;/li&gt;
&lt;li&gt;适合 Cron 表达式触发的批处理任务；&lt;/li&gt;
&lt;li&gt;任务由业务主动提交的场景下，接入不够自然。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;KE Reliable Executor 更像一个&lt;strong&gt;嵌入式任务队列&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无额外服务，随业务应用启动；&lt;/li&gt;
&lt;li&gt;任务由业务代码主动提交；&lt;/li&gt;
&lt;li&gt;强调“提交即可靠执行”，而非定时触发。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三整体架构&#34;&gt;三、整体架构&lt;/h2&gt;
&lt;h3 id=&#34;31-模块解构图&#34;&gt;3.1 模块解构图&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[业务服务] --&amp;gt;|submitTask| B(ReliableExecutor API)
    B --&amp;gt; C[TaskScheduler]
    C --&amp;gt; D{Master 锁}
    D --&amp;gt;|持有锁| E[扫描 ZSET 任务]
    E --&amp;gt; F[任务级锁]
    F --&amp;gt;|获取成功| G[提交线程池]
    G --&amp;gt; H[TaskExecutor]
    H --&amp;gt; I[&amp;#34;任务处理器 @ReliableTaskHandler&amp;#34;]
    I --&amp;gt;|成功| J[移入 FINISHED]
    I --&amp;gt;|失败| K[更新为 PENDING 等待重试]
    C --&amp;gt; L[TaskStorage]
    L --&amp;gt; M[RedisTaskStorage]
    L --&amp;gt; N[JdbcTaskStorage]
    H --&amp;gt; O[ReliableTaskListener]
    C --&amp;gt; P[ReliableExecutorMetrics]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;32-两层锁模型&#34;&gt;3.2 两层锁模型&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph 全局层
        A[Master 锁] --&amp;gt;|仅一个实例| B[负责调度扫描]
    end
    subgraph 任务层
        C[&amp;#34;任务级锁 task-lock:{taskId}&amp;#34;] --&amp;gt;|仅一个线程| D[执行任务]
    end
    B --&amp;gt; C&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Master 锁&lt;/strong&gt;：同一时刻只有一个实例负责扫描任务，避免多节点拉取同一批任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务级锁&lt;/strong&gt;：针对每个 CREATED 任务，提交执行前再抢一次锁，保证同一任务不会被并发执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;33-队列设计&#34;&gt;3.3 队列设计&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[提交任务] --&amp;gt;|add| B[Redis ZSET running-task-zset]
    B --&amp;gt;|ZRANGE 按 score| C[调度器]
    C --&amp;gt;|执行成功/失败/超时| D[Lua 原子转移]
    D --&amp;gt;|RPUSH + ZREM| E[Redis List finished-task-list]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;使用 ZSET 而非 List 的核心原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;天然支持时间调度&lt;/strong&gt;：score 编码创建时间或下次重试时间；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务唯一&lt;/strong&gt;：同一个 member 在队列中只有一份；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态分区&lt;/strong&gt;：CREATED 与 PENDING 使用不同 score 基数，一次范围查询即可区分。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Score 编码规则：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;score = BASE + epochMillis * 100 + retryCount
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATED 基数：1_000_000_000_000_000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PENDING 基数：2_000_000_000_000_000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;34-任务状态机&#34;&gt;3.4 任务状态机&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; CREATED: submitTask
    CREATED --&amp;gt; RUNNING: 调度/立即执行
    RUNNING --&amp;gt; COMPLETED: 执行成功
    RUNNING --&amp;gt; PENDING: 执行失败，未超重试
    PENDING --&amp;gt; RUNNING: 到达重试时间
    RUNNING --&amp;gt; FAILED: 超过重试次数
    RUNNING --&amp;gt; TIMEOUT: 超过最大时长
    CREATED --&amp;gt; CANCELLED: 取消任务
    PENDING --&amp;gt; CANCELLED: 取消任务
    RUNNING --&amp;gt; ERROR: 无处理器/参数错误&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;四核心设计要点&#34;&gt;四、核心设计要点&lt;/h2&gt;
&lt;h3 id=&#34;41-lua-原子转移&#34;&gt;4.1 Lua 原子转移&lt;/h3&gt;
&lt;p&gt;任务进入终态时，必须保证“写入 finished list”和“从 ZSET 移除”是原子的，否则可能出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任务已判定成功但还在 ZSET 里，被下一次调度捞出来；&lt;/li&gt;
&lt;li&gt;任务已从 ZSET 移除但 finished list 没写入，查询不到结果。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;框架使用 Lua 脚本完成这一转移：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-lua&#34; data-lang=&#34;lua&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;local&lt;/span&gt; score &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; redis.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ZSCORE&amp;#39;&lt;/span&gt;, KEYS[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], ARGV[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;redis.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;RPUSH&amp;#39;&lt;/span&gt;, KEYS[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;], ARGV[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; score &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    redis.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ZREM&amp;#39;&lt;/span&gt;, KEYS[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], ARGV[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;逻辑：先查 ZSET，无论是否存在都把终态信息写入 finished list，如果存在再删除。整个脚本在 Redis 单线程执行，天然原子。&lt;/p&gt;
&lt;h3 id=&#34;42-double-check&#34;&gt;4.2 Double-Check&lt;/h3&gt;
&lt;p&gt;调度器拿到任务级锁后、提交线程池前，会再次回查 Redis：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;任务是否仍在 running ZSET 中？&lt;/li&gt;
&lt;li&gt;任务状态是否仍是 CREATED？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;任一条件不满足，立即释放锁并跳过。这是经典的双重检查模式，把“检查-执行”串在锁保护的临界区内。&lt;/p&gt;
&lt;h3 id=&#34;43-双存储后端&#34;&gt;4.3 双存储后端&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[TaskStorage 接口] --&amp;gt; B[RedisTaskStorage]
    A --&amp;gt; C[JdbcTaskStorage]
    B --&amp;gt; D[Redisson ZSET + List]
    C --&amp;gt; E[reliable_executor_task 表]
    F[配置 reliable-executor.task-storage] --&amp;gt;|redis/mysql| A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redis&lt;/code&gt;：默认，适合已有 Redis 的场景；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql&lt;/code&gt;：适合不希望引入 Redis 或需要更强持久化的场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种后端通过 Spring 条件装配切换，业务代码无感知。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五快速开始&#34;&gt;五、快速开始&lt;/h2&gt;
&lt;h3 id=&#34;51-引入依赖&#34;&gt;5.1 引入依赖&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gradle&#34; data-lang=&#34;gradle&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dependencies &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    implementation &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;com.ftmao:ke-reliable-executor:1.0.0-SNAPSHOT&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    implementation &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;org.redisson:redisson-spring-boot-starter:3.27.2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-配置&#34;&gt;5.2 配置&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;application&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;my-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;redis&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;redisson&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;config&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        singleServerConfig:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          address: &amp;#34;redis://127.0.0.1:6379&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;reliable-executor&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;task-storage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;core-pool-size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;max-pool-size&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;queue-capacity&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;53-定义处理器&#34;&gt;5.3 定义处理器&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Component&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderTaskHandler&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@ReliableTaskHandler&lt;/span&gt;(name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;process_order&amp;#34;&lt;/span&gt;, paramType &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; OrderParam.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;processOrder&lt;/span&gt;(OrderParam param) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 业务逻辑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@ReliableTaskHandler&lt;/span&gt;(name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;send_notify&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sendNotify&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 无参任务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;54-提交任务&#34;&gt;5.4 提交任务&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; ReliableExecutor reliableExecutor;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;submitOrder&lt;/span&gt;(OrderParam param) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TaskSubmitRequest request &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TaskSubmitRequest.&lt;span style=&#34;color:#a6e22e&#34;&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;taskId&lt;/span&gt;(UUID.&lt;span style=&#34;color:#a6e22e&#34;&gt;randomUUID&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;taskName&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;process_order&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;taskParameter&lt;/span&gt;(param)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;retryLimit&lt;/span&gt;(3)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;retryInterval&lt;/span&gt;(2000)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;maxDurationMillis&lt;/span&gt;(60000)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        reliableExecutor.&lt;span style=&#34;color:#a6e22e&#34;&gt;submitTask&lt;/span&gt;(request);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;55-监听生命周期&#34;&gt;5.5 监听生命周期&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Component&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyTaskListener&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; ReliableTaskListener {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onTaskCompleted&lt;/span&gt;(String taskId, String taskName) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 任务成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onTaskFailed&lt;/span&gt;(String taskId, String taskName, String status) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 任务最终失败&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;六使用注意点与避坑指南&#34;&gt;六、使用注意点与避坑指南&lt;/h2&gt;
&lt;h3 id=&#34;61-业务幂等仍然是最后一道防线&#34;&gt;6.1 业务幂等仍然是最后一道防线&lt;/h3&gt;
&lt;p&gt;框架通过两层锁、double-check、Lua 原子转移把重复执行的概率降到极低，但&lt;strong&gt;任何分布式系统都无法 100% 保证幂等&lt;/strong&gt;。业务 Handler 仍然应该保证幂等，尤其是涉及写数据库、调用外部接口的场景。&lt;/p&gt;
&lt;h3 id=&#34;62-taskid-需要业务保证唯一&#34;&gt;6.2 taskId 需要业务保证唯一&lt;/h3&gt;
&lt;p&gt;框架使用业务传入的 &lt;code&gt;taskId&lt;/code&gt; 作为任务唯一标识。如果同一业务单据重复提交相同 &lt;code&gt;taskId&lt;/code&gt;，第二次提交会生成新的 ZSET member（如果业务字段不同）或覆盖（如果完全相同）。&lt;strong&gt;建议用业务主键 + 操作类型组合生成 taskId&lt;/strong&gt;，例如 &lt;code&gt;order_12345_pay&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;63-重试间隔与最大时长的权衡&#34;&gt;6.3 重试间隔与最大时长的权衡&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;reliable-executor&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;retryInterval&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2000&lt;/span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;# 失败后 2s 再试&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;maxDurationMillis&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60000&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 任务最长存活 60s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;retryInterval&lt;/code&gt; 过小会导致失败任务快速反复执行，放大下游压力；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxDurationMillis&lt;/code&gt; 是从任务创建时间算起，不是单次执行时间；&lt;/li&gt;
&lt;li&gt;建议失败任务采用指数退避（当前版本为固定间隔，后续可扩展）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;64-mysql-分支需要自行引入-jpa&#34;&gt;6.4 MySQL 分支需要自行引入 JPA&lt;/h3&gt;
&lt;p&gt;框架中 &lt;code&gt;spring-boot-starter-data-jpa&lt;/code&gt; 是 &lt;code&gt;compileOnly&lt;/code&gt;，使用 MySQL 存储时需要在业务应用里显式引入：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gradle&#34; data-lang=&#34;gradle&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;implementation &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;org.springframework.boot:spring-boot-starter-data-jpa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;runtimeOnly &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;com.mysql:mysql-connector-j&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;并执行建表脚本 &lt;code&gt;db/migration/V1__create_reliable_executor_tables.sql&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;65-redis-集群环境注意-key-前缀&#34;&gt;6.5 Redis 集群环境注意 Key 前缀&lt;/h3&gt;
&lt;p&gt;框架默认使用 &lt;code&gt;spring.application.name&lt;/code&gt; 作为 Redis Key 前缀。如果多个应用共用同一个 Redis 实例，务必设置不同的 &lt;code&gt;spring.application.name&lt;/code&gt;，否则任务会互相串扰。&lt;/p&gt;
&lt;h3 id=&#34;66-线程池队列满载时会跳过调度&#34;&gt;6.6 线程池队列满载时会跳过调度&lt;/h3&gt;
&lt;p&gt;当线程池队列达到 &lt;code&gt;queueCapacity&lt;/code&gt; 时，调度器会跳过本次周期，避免无限制堆积。如果业务出现持续高峰，建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;调大 &lt;code&gt;maxPoolSize&lt;/code&gt; 和 &lt;code&gt;queueCapacity&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;监控 &lt;code&gt;reliable_executor_threadpool_queue_size&lt;/code&gt; 指标；&lt;/li&gt;
&lt;li&gt;必要时配合业务限流。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;67-不要忽略超时任务&#34;&gt;6.7 不要忽略超时任务&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;maxDurationMillis&lt;/code&gt; 是从创建时间算起的总时长。如果一个任务在队列里排队太久，可能还没执行就触发 TIMEOUT。对于允许较长延迟的任务，要把这个值设大；对于实时性任务，要把重试间隔和排队容量一起考虑。&lt;/p&gt;
&lt;h3 id=&#34;68-本地开发无-redis-怎么办&#34;&gt;6.8 本地开发无 Redis 怎么办？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用 MySQL 分支（H2 也可以跑测试）；&lt;/li&gt;
&lt;li&gt;或者本地启动一个 Redis Docker 容器；&lt;/li&gt;
&lt;li&gt;框架的 Redis 集成测试会在无 Redis 时自动跳过，不影响 CI。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七监控指标&#34;&gt;七、监控指标&lt;/h2&gt;
&lt;p&gt;框架内置 Micrometer 指标，可接入 Prometheus/Actuator：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;指标名&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_is_master&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前节点是否为 Master&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_scheduled_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计调度任务数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_executed_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计执行任务数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_completed_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计成功任务数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_failed_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计失败任务数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_timeout_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计超时任务数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_tasks_retried_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;累计重试次数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_threadpool_queue_size&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;线程池队列大小&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;reliable_executor_task_execution_seconds&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;任务执行耗时&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八开源与示例&#34;&gt;八、开源与示例&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开源地址&lt;/strong&gt;：https://gitee.com/soulnew/reliableexecutor&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;示例模块&lt;/strong&gt;：&lt;code&gt;example/&lt;/code&gt; 子项目包含可运行的 Spring Boot 应用和 REST 接口示例，可直接启动体验任务提交与执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九总结&#34;&gt;九、总结&lt;/h2&gt;
&lt;p&gt;KE Reliable Executor 是一个&lt;strong&gt;轻量、可靠、易接入&lt;/strong&gt;的异步任务执行框架。它用 Redis ZSET + 两层锁 + Lua 原子转移，在“不引入重型 MQ”的前提下，提供了任务提交、调度、重试、状态查询、监控等完整能力；同时通过 MySQL 分支提供了另一种存储选择。&lt;/p&gt;
&lt;p&gt;如果你的业务符合以下特征，不妨试试它：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;已有 Redis/MySQL；&lt;/li&gt;
&lt;li&gt;任务量中等，重视可靠性；&lt;/li&gt;
&lt;li&gt;希望快速落地，不想为 MQ 付出额外运维成本；&lt;/li&gt;
&lt;li&gt;需要任务状态可观测、可人工介入。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;记住：框架层负责把重复消费的概率降到极低，业务层仍应保证 Handler 幂等，这才是工程上的稳妥做法。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>jphotoshop：用 Java Swing 手写一个 Photoshop——架构与算法全解析</title>
      <link>https://ftmao.com/blog/posts/jphotoshop-java-swing-photoshop-clone/</link>
      <pubDate>Fri, 18 May 2018 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/jphotoshop-java-swing-photoshop-clone/</guid>
      <description>&lt;h1 id=&#34;jphotoshop用-java-swing-手写一个-photoshop架构与算法全解析&#34;&gt;jphotoshop：用 Java Swing 手写一个 Photoshop——架构与算法全解析&lt;/h1&gt;
&lt;p&gt;把多年前的一个练手项目整理开源了：&lt;strong&gt;jphotoshop&lt;/strong&gt;，一个用纯 Java Swing 手写的 Photoshop 模拟器。不写不知道，写一个&amp;quot;简化版 PS&amp;quot;几乎把计算机图形学、数据结构和设计模式都过了一遍，这篇文章既是功能介绍，也是一次技术复盘。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开源地址：&lt;a href=&#34;https://gitee.com/soulnew/jphotoshop&#34;&gt;https://gitee.com/soulnew/jphotoshop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;技术栈：Java + Swing（Swing Application Framework / JSR-296），NetBeans Ant 工程&lt;/li&gt;
&lt;li&gt;规模：197 个 Java 文件，约 1.8 万行（自研约 1.4 万行），开发周期约半年&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一功能概览一个简化版-ps能做什么&#34;&gt;一、功能概览：一个&amp;quot;简化版 PS&amp;quot;能做什么&lt;/h2&gt;
&lt;p&gt;UI 形态上和老版 PS 一致：&lt;strong&gt;MDI 多文档界面&lt;/strong&gt;——每张图片一个内部编辑窗口，图层面板、导航缩放面板都是可拖动的浮动内窗。&lt;/p&gt;
&lt;h3 id=&#34;11-图层系统&#34;&gt;1.1 图层系统&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;多图层编辑：图像图层、&lt;strong&gt;文字图层&lt;/strong&gt;、浮动选区层（拖动选区内容时的临时层）；&lt;/li&gt;
&lt;li&gt;图层面板带 40×30 &lt;strong&gt;实时缩略图&lt;/strong&gt;，支持拖拽换序（基于 Swing TransferHandler 的 DnD）、复制粘贴为新图层；&lt;/li&gt;
&lt;li&gt;透明区域用经典的灰白棋盘格打底。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-选区工具7-个&#34;&gt;1.2 选区工具（7 个）&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;工具&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;魔棒&lt;/td&gt;
					&lt;td&gt;容差可调（默认 23），支持多连通区域一次选中&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;色彩范围&lt;/td&gt;
					&lt;td&gt;全图相似色选取&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多边形套索&lt;/td&gt;
					&lt;td&gt;逐点勾勒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;自由套索&lt;/td&gt;
					&lt;td&gt;鼠标轨迹勾勒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;磁性套索&lt;/td&gt;
					&lt;td&gt;局部窗口内吸附边缘（完成度不高）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;贝塞尔选区&lt;/td&gt;
					&lt;td&gt;双击闭合时自动做曲线拟合，生成平滑选区&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;移动工具&lt;/td&gt;
					&lt;td&gt;把选区内容提成浮动层，拖动实时合成，松手落回底层&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;选区还支持：&lt;strong&gt;羽化&lt;/strong&gt;（可调半径）、反选、平移/缩放（AffineTransform）、以及标志性的&lt;strong&gt;蚂蚁线动画&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;13-滤镜24-个分-5-大类&#34;&gt;1.3 滤镜（24 个，分 5 大类）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模糊&lt;/strong&gt;：均值平滑、高斯模糊（3×3 高斯核 &lt;code&gt;{1,2,1;2,4,2;1,2,1}/16&lt;/code&gt;）、中值滤波；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锐化&lt;/strong&gt;：Sharpen / SharpenMore（拉普拉斯卷积核 &lt;code&gt;{0,-1,0;-1,5,-1;0,-1,0}&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;艺术&lt;/strong&gt;：曝光过度、冰晶、熔岩、暗调（平方衰减）、漫画等 8 种风格化效果；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单&lt;/strong&gt;：反色、怀旧（sepia 矩阵 &lt;code&gt;0.393/0.769/0.189...&lt;/code&gt;）、灰度；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扭曲&lt;/strong&gt;：挤压、球面化、正/反旋转、正弦波浪、摩尔纹——扭曲类全部带&lt;strong&gt;多段滑块实时预览&lt;/strong&gt;，取消可还原。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;14-图像调整与文字&#34;&gt;1.4 图像调整与文字&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HSB 调整&lt;/strong&gt;：先把整图 RGB 转成 HSB 三维 float 数组，色相/饱和度/亮度三个滑块实时调节再转回 RGB；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对比度&lt;/strong&gt;调整；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文字工具&lt;/strong&gt;：点击图片任意位置就地出现透明输入框 + 字体选择面板，完成后生成为独立的文字图层。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;15-文件与工程&#34;&gt;1.5 文件与工程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;打开 jpg / png / gif / bmp；&lt;/li&gt;
&lt;li&gt;自定义 &lt;strong&gt;&lt;code&gt;.jps&lt;/code&gt; 工程格式&lt;/strong&gt;（类似 PSD）：整个图层模型实现 &lt;code&gt;Serializable&lt;/code&gt;，用 &lt;code&gt;ObjectOutputStream&lt;/code&gt; 套 &lt;code&gt;GZIPOutputStream&lt;/code&gt; 序列化保存，可以随时回来继续编辑；&lt;/li&gt;
&lt;li&gt;复制 / 剪切 / 粘贴、导航缩放窗口、中文界面。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二架构总览&#34;&gt;二、架构总览&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;JPhotoShop 入口&amp;lt;br/&amp;gt;EDT 中构建主窗口&amp;#34;] --&amp;gt; B[&amp;#34;JDesktopPane MDI 主框架&amp;#34;]
    B --&amp;gt; C[&amp;#34;EditFrame 编辑窗口&amp;lt;br/&amp;gt;每张图一个&amp;#34;]
    B --&amp;gt; D[&amp;#34;LayerControl 图层面板&amp;#34;]
    B --&amp;gt; E[&amp;#34;ViewSizeFrame 导航/缩放&amp;#34;]
    C --&amp;gt; F[&amp;#34;EditModel&amp;lt;br/&amp;gt;像素模型 + MemoryImageSource 渲染&amp;#34;]
    F --&amp;gt; G[&amp;#34;ImageModel&amp;lt;br/&amp;gt;LinkedList 图层栈&amp;#34;]
    G --&amp;gt; H[&amp;#34;ImagePaintLayer / TextPaintLayer / TempImageLayer&amp;#34;]
    H --&amp;gt; I[&amp;#34;LayerPlug 混合样式&amp;lt;br/&amp;gt;Alpha 合成&amp;#34;]
    C --&amp;gt; J[&amp;#34;ToolManager&amp;lt;br/&amp;gt;工具代理/状态切换&amp;#34;]
    J --&amp;gt; K[&amp;#34;Tool 抽象类&amp;lt;br/&amp;gt;魔棒/套索/贝塞尔/移动/文字...&amp;#34;]
    C --&amp;gt; L[&amp;#34;FilterManager&amp;lt;br/&amp;gt;properties + 反射装配&amp;#34;]
    L --&amp;gt; M[&amp;#34;PicFilter 滤镜即 Action&amp;#34;]
    M --&amp;gt; N[&amp;#34;Math 包&amp;lt;br/&amp;gt;CoverOp 卷积 / FeatherSide 羽化&amp;lt;br/&amp;gt;FindSide 轮廓追踪 / PointConnection 并查集&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;整体分四层：&lt;strong&gt;UI 层&lt;/strong&gt;（MDI 窗口与面板）、&lt;strong&gt;模型层&lt;/strong&gt;（图层栈与像素数据）、&lt;strong&gt;工具/滤镜层&lt;/strong&gt;（操作入口）、&lt;strong&gt;算法层&lt;/strong&gt;（&lt;code&gt;jphotoshop.Math&lt;/code&gt; 包）。工具和滤镜都通过 properties 配置 + 反射装配，下面详细说。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三技术亮点&#34;&gt;三、技术亮点&lt;/h2&gt;
&lt;h3 id=&#34;31-自绘像素管线int--memoryimagesource&#34;&gt;3.1 自绘像素管线：int[][] + MemoryImageSource&lt;/h3&gt;
&lt;p&gt;核心存储没有用 &lt;code&gt;BufferedImage&lt;/code&gt;，而是直接操作 &lt;strong&gt;&lt;code&gt;int[][]&lt;/code&gt; ARGB 二维数组&lt;/strong&gt;——所有算法（卷积、混合、羽化）都在裸数组上做，直观且没有额外封装开销。&lt;/p&gt;
&lt;p&gt;显示侧用了一个当年很典型的 Swing 高性能技巧：&lt;code&gt;MemoryImageSource&lt;/code&gt; + &lt;code&gt;setAnimated(true)&lt;/code&gt;，像素变化后调 &lt;code&gt;newPixels()&lt;/code&gt; &lt;strong&gt;增量刷新&lt;/strong&gt;屏幕图像，避免每次重绘都重建整个 Image 对象。编辑、拖选区、调滤镜滑块时的实时反馈都靠它撑着。&lt;/p&gt;
&lt;h3 id=&#34;32-链式图层合成缓存&#34;&gt;3.2 链式图层合成缓存&lt;/h3&gt;
&lt;p&gt;多图层合成是最频繁的操作，朴素实现是每次重绘把所有层从头合一遍。这里的做法是给每个图层挂一个&amp;quot;&lt;strong&gt;本层与下面所有层合成结果的缓存&lt;/strong&gt; &lt;code&gt;cacheData&lt;/code&gt;&amp;quot;，层与层之间用 &lt;code&gt;backLayer&lt;/code&gt; 引用连成链：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    BG[&amp;#34;背景层&amp;lt;br/&amp;gt;棋盘格&amp;#34;] --&amp;gt;|&amp;#34;合成&amp;#34;| L1[&amp;#34;图层1&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L1 --&amp;gt;|&amp;#34;合成&amp;#34;| L2[&amp;#34;图层2&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L2 --&amp;gt;|&amp;#34;合成&amp;#34;| L3[&amp;#34;图层3&amp;lt;br/&amp;gt;cacheData&amp;#34;]
    L3 --&amp;gt; OUT[&amp;#34;最终显示图像&amp;#34;]
    style L2 fill:#f96
    L2 -. &amp;#34;仅从此层向上重建&amp;#34; .-&amp;gt; L3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;某一层像素变化时，&lt;strong&gt;只从该层向上依次 &lt;code&gt;reBuild&lt;/code&gt;&lt;/strong&gt;，下面的层缓存不动。层数多、图大时，这个收益非常明显。&lt;/p&gt;
&lt;h3 id=&#34;33-手写-alpha-混合&#34;&gt;3.3 手写 Alpha 混合&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AlphaLayerStyle&lt;/code&gt; 实现了逐像素的经典混合公式 &lt;code&gt;C = α·Cf + (1-α)·Cb&lt;/code&gt;，并且处理了&lt;strong&gt;任意偏移（含负坐标裁剪）&lt;/strong&gt;——浮动选区拖到画布边缘外也能正确合成。羽化后的选区掩码是 short 型 alpha 权重，抠图粘贴时按权重衰减，边缘不会出现生硬的锯齿。&lt;/p&gt;
&lt;h3 id=&#34;34-魔棒一条完整的算法链&#34;&gt;3.4 魔棒：一条完整的算法链&lt;/h3&gt;
&lt;p&gt;魔棒是整个项目里算法含量最高的功能，完整链路分三步：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;用户点击图片某点&amp;#34;] --&amp;gt; B[&amp;#34;Flood Fill 泛洪填充&amp;lt;br/&amp;gt;8邻域 + 容差判断&amp;lt;br/&amp;gt;ΔR+ΔG+ΔB ＜ range&amp;#34;]
    B --&amp;gt; C[&amp;#34;PointConnection 并查集&amp;lt;br/&amp;gt;id 数组 + sz 数组按秩合并&amp;lt;br/&amp;gt;分离多个互不连通的区域&amp;#34;]
    C --&amp;gt; D[&amp;#34;FindSide 8方向轮廓追踪&amp;lt;br/&amp;gt;沿区域边界走一圈&amp;lt;br/&amp;gt;每3步抽稀路径点&amp;#34;]
    D --&amp;gt; E[&amp;#34;GeneralPath 选区路径&amp;#34;]
    E --&amp;gt; F[&amp;#34;蚂蚁线动画&amp;lt;br/&amp;gt;Timer 500ms 切换两条相位差3的虚线&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;泛洪填充用 &lt;code&gt;LinkedList&lt;/code&gt; 当队列，容差判断用曼哈顿距离（&lt;code&gt;|ΔR|+|ΔG|+|ΔB|&lt;/code&gt;），比欧氏距离省开方；&lt;/li&gt;
&lt;li&gt;一次点击可能选中多个互不连通的区域（比如图片里分散的几块蓝天），用&lt;strong&gt;并查集&lt;/strong&gt;把连通域分组，每个域独立生成轮廓；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轮廓追踪&lt;/strong&gt;把面状的 boolean 掩码转成线状的路径点列，这一步直接决定蚂蚁线的平滑程度；&lt;/li&gt;
&lt;li&gt;蚂蚁线动画是两条相位差为 3 的虚线 &lt;code&gt;BasicStroke&lt;/code&gt; 用 500ms 的 &lt;code&gt;Timer&lt;/code&gt; 交替绘制——朴素但效果完全对味。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;35-通用卷积引擎-coverop&#34;&gt;3.5 通用卷积引擎 CoverOp&lt;/h3&gt;
&lt;p&gt;所有卷积类滤镜（模糊、锐化背后是同一个引擎 &lt;code&gt;CoverOp&lt;/code&gt;，提供三种重载：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;全图卷积；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;掩码限定区域卷积&lt;/strong&gt;——滤镜只在选区内生效，这是 PS 的基本体验；&lt;/li&gt;
&lt;li&gt;short alpha 掩码卷积——配合羽化选区做渐变处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;RGB 三通道分离卷积 + 边界 clamp，滤镜类只需要给一个卷积核就完事。新增一个模糊变体的成本是十几行代码。&lt;/p&gt;
&lt;h3 id=&#34;36-贝塞尔选区schneider-曲线拟合&#34;&gt;3.6 贝塞尔选区：Schneider 曲线拟合&lt;/h3&gt;
&lt;p&gt;自由手绘的选区轨迹是抖动的折线，直接当选区很难看。贝塞尔选区工具在双击闭合时调用 JHotDraw 的 &lt;code&gt;Bezier.fitCurve&lt;/code&gt;（Schneider 的经典拟合算法），把鼠标点列拟合成平滑的三次贝塞尔曲线段——这就是 PS 里&amp;quot;钢笔工具&amp;quot;的底层原理。&lt;/p&gt;
&lt;h3 id=&#34;37-声明式装配properties--反射的原始插件化&#34;&gt;3.7 声明式装配：properties + 反射的原始&amp;quot;插件化&amp;quot;&lt;/h3&gt;
&lt;p&gt;工具和滤镜都&lt;strong&gt;不写死在代码里&lt;/strong&gt;，而是从 properties 读类名、&lt;code&gt;Class.forName&lt;/code&gt; 反射实例化：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-properties&#34; data-lang=&#34;properties&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# resource/filters.properties&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;filters.Kinds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;模糊,锐化,艺术效果,简单,扭曲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;模糊&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Smooth,GaussBlur,Median&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;加一个新滤镜 = 写一个类 + 改一行配置，菜单自动按分类分组生成。工具栏同理（&lt;code&gt;ToolManager.initTool&lt;/code&gt;）。项目里还专门有一个 &lt;code&gt;plug&lt;/code&gt; 包在探索真正的外部插件机制（虽然只是个雏形）。在 2007 年没有 OSGi、没有 IoC 框架依赖的纯 Swing 工程里，这是最务实的可扩展方案。&lt;/p&gt;
&lt;h3 id=&#34;38-工具架构proxy--state-的混合&#34;&gt;3.8 工具架构：Proxy + State 的混合&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Tool&lt;/code&gt; 抽象类把底层 MouseEvent 翻译成语义化回调（&lt;code&gt;click / pressed / dragged / release / rightClick / clickDouble / move&lt;/code&gt;），子类只关心语义。&lt;code&gt;ToolManager&lt;/code&gt; 本身也是 &lt;code&gt;Tool&lt;/code&gt; 的子类，作为&lt;strong&gt;代理&lt;/strong&gt;把事件转发给当前激活的工具——选中哪个工具，事件流就切到哪个状态机上。每个工具还可以自带 &lt;code&gt;getPlugPanel()&lt;/code&gt; 参数面板，选中时挂到选项栏上，和 PS 的交互完全一致。&lt;/p&gt;
&lt;h3 id=&#34;39-扭曲滤镜的预览-取消模式&#34;&gt;3.9 扭曲滤镜的&amp;quot;预览-取消&amp;quot;模式&lt;/h3&gt;
&lt;p&gt;扭曲类滤镜（球面、旋转、波浪……）都是坐标重映射：对每个目标像素做逆极坐标变换，找它在原图的取样点。为了支持滑块&lt;strong&gt;实时预览又能取消还原&lt;/strong&gt;，&lt;code&gt;ContortFilter&lt;/code&gt; 在打开对话框时先备份 &lt;code&gt;tempData&lt;/code&gt;，滑块每次变化都在备份基础上重算，点取消直接把备份拷回——一个廉价的 Memento 模式。配套的 &lt;code&gt;MultiSlider&lt;/code&gt; 多段滑块控件也是自绘的。&lt;/p&gt;
&lt;h3 id=&#34;310-滤镜即-action&#34;&gt;3.10 滤镜即 Action&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PicFilter extends AbstractAction&lt;/code&gt;，滤镜本身就是可以挂进菜单/工具栏的 Action：&lt;code&gt;actionPerformed&lt;/code&gt; 里自动取当前窗口、当前图层、当前选区掩码，还处理了&lt;strong&gt;选区坐标系到图层坐标系的换算&lt;/strong&gt;（&lt;code&gt;start -= layerStart&lt;/code&gt;），执行完触发 &lt;code&gt;rePaintEdit&lt;/code&gt; 刷新。框架把脏活都干了，滤镜作者只需要关注像素算法。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四诚实的复盘留下了什么遗憾&#34;&gt;四、诚实的复盘：留下了什么遗憾&lt;/h2&gt;
&lt;p&gt;开源的代码就是当年的样子，包括未完成的部分，如实列出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;撤销/重做是空架子&lt;/strong&gt;：&lt;code&gt;UndoRedoManager&lt;/code&gt; 的骨架搭好了，但 &lt;code&gt;propertyChange&lt;/code&gt; 三个分支全是空的——这是当时没啃下来的硬骨头，现在看应该用 Command 模式重做，而不是当初设想的 property 监听方案；&lt;/li&gt;
&lt;li&gt;历史面板、通道面板、外部插件机制都只有雏形；&lt;/li&gt;
&lt;li&gt;磁性套索完成度不高；&lt;/li&gt;
&lt;li&gt;若干小 bug，比如 &lt;code&gt;FindSide&lt;/code&gt; 里找最近点写成了找最远点（&lt;code&gt;if (temp &amp;gt; MinLength)&lt;/code&gt; 应为 &lt;code&gt;&amp;lt;&lt;/code&gt;）、&lt;code&gt;&amp;quot;undo&amp;quot;.endsWith(...)&lt;/code&gt; 应为 &lt;code&gt;startsWith&lt;/code&gt;、几处类名 typo（&lt;code&gt;WaveFiler&lt;/code&gt;、&lt;code&gt;getSize&lt;/code&gt; 拼写）——权当留给读者的&amp;quot;考古彩蛋&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五写在最后&#34;&gt;五、写在最后&lt;/h2&gt;
&lt;p&gt;写 jphotoshop 的半年收获远超预期：并查集、轮廓追踪、卷积、贝塞尔拟合这些算法不再是教科书上的名词，而是要亲手调到能跑的东西；合成缓存、声明式装配、Action 抽象这些设计，也都是被真实需求逼出来的。如果你也想扎实练一次&amp;quot;算法 + 工程&amp;quot;的综合功，手写一个图像编辑器是我能想到的最好题目之一。&lt;/p&gt;
&lt;p&gt;源码在 &lt;a href=&#34;https://gitee.com/soulnew/jphotoshop&#34;&gt;https://gitee.com/soulnew/jphotoshop&lt;/a&gt;，欢迎 Star、Fork，更欢迎把那个空着的 &lt;code&gt;UndoRedoManager&lt;/code&gt; 补上。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Java 高并发编程实战：同步工具、锁与线程池</title>
      <link>https://ftmao.com/blog/posts/java-concurrency-guide/</link>
      <pubDate>Wed, 28 Mar 2018 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/java-concurrency-guide/</guid>
      <description>&lt;h1 id=&#34;java-高并发编程实战同步工具锁与线程池&#34;&gt;Java 高并发编程实战：同步工具、锁与线程池&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;高并发程序的核心不是“让线程跑得多快”，而是&lt;strong&gt;让多个线程在共享可变状态下安全、可预期地协作&lt;/strong&gt;。JDK 的 &lt;code&gt;java.util.concurrent&lt;/code&gt;（JUC）包已经为我们提供了大量经过打磨的同步工具，善用它们比手写 &lt;code&gt;wait/notify&lt;/code&gt; 要可靠得多。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一高层次思想并发编程在解决什么问题&#34;&gt;一、高层次思想：并发编程在解决什么问题？&lt;/h2&gt;
&lt;p&gt;并发编程的本质是&lt;strong&gt;约束&lt;/strong&gt;：当多个线程访问同一组可变资源时，通过某种机制保证以下三件事至少满足其一：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;互斥&lt;/strong&gt;：同一时刻只有一个线程能访问临界区。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可见性&lt;/strong&gt;：一个线程修改的状态，其他线程能正确看到。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有序性&lt;/strong&gt;：指令不会被编译器或 CPU 乱序到破坏程序语义。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[共享可变状态] --&amp;gt;|互斥| B[锁 / 信号量]
    A --&amp;gt;|可见性| C[volatile / happens-before]
    A --&amp;gt;|有序性| D[内存屏障 / 同步原语]
    B --&amp;gt; E[正确的并发程序]
    C --&amp;gt; E
    D --&amp;gt; E&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;首选策略金字塔&lt;/strong&gt;（从推荐到不推荐）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[不可变对象 &amp;amp; 线程封闭] --&amp;gt; B[JUC 并发集合 / 线程池]
    B --&amp;gt; C[高级同步工具 CountDownLatch Semaphore 等]
    C --&amp;gt; D[ReentrantLock &amp;amp; Condition]
    D --&amp;gt; E[synchronized]
    E --&amp;gt; F[手写 wait/notify]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #1&lt;/strong&gt;：不到万不得已不要手写 &lt;code&gt;wait/notify&lt;/code&gt;，极容易写出遗漏 &lt;code&gt;notify&lt;/code&gt;、虚假唤醒、持锁休眠等问题。JUC 工具类已经帮你封装好了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二线程状态与原语对照&#34;&gt;二、线程状态与原语对照&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;方法&lt;/th&gt;
					&lt;th&gt;调用者&lt;/th&gt;
					&lt;th&gt;是否释放锁&lt;/th&gt;
					&lt;th&gt;进入状态&lt;/th&gt;
					&lt;th&gt;唤醒条件&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Thread.sleep(ms)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前线程&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;TIMED_WAITING&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;时间到&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Thread.yield()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前线程&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;RUNNABLE&lt;/code&gt;（让出 CPU）&lt;/td&gt;
					&lt;td&gt;OS 调度&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;t.join()&lt;/code&gt; / &lt;code&gt;t.join(ms)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前线程等 t&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WAITING&lt;/code&gt; / &lt;code&gt;TIMED_WAITING&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;t 结束或超时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;obj.wait()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前线程&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;是&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WAITING&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;notify/notifyAll&lt;/code&gt; 或超时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;obj.notify()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;当前线程&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;—&lt;/td&gt;
					&lt;td&gt;唤醒一个等待线程&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; 新建: new Thread()
    新建 --&amp;gt; 就绪: start()
    就绪 --&amp;gt; 运行: 调度
    运行 --&amp;gt; 就绪: yield / 时间片到
    运行 --&amp;gt; 阻塞: 等待锁
    阻塞 --&amp;gt; 就绪: 获得锁
    运行 --&amp;gt; 等待: wait / join / await
    等待 --&amp;gt; 就绪: notify / 超时 / 被等待线程结束
    运行 --&amp;gt; 超时等待: sleep / wait(timeout)
    超时等待 --&amp;gt; 就绪: 时间到
    运行 --&amp;gt; 终止: 执行完毕&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #2&lt;/strong&gt;：&lt;code&gt;sleep&lt;/code&gt; 不释放锁，若在 synchronized 块内 sleep，其他线程会被长时间阻塞。需要“让出锁去等”时，用 &lt;code&gt;wait/Condition.await&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三多用-juc-同步类少用-waitnotify&#34;&gt;三、多用 JUC 同步类，少用 wait/notify&lt;/h2&gt;
&lt;h3 id=&#34;31-semaphore信号量&#34;&gt;3.1 Semaphore（信号量）&lt;/h3&gt;
&lt;p&gt;信号量维护一组“许可”，控制同时访问某资源的线程数量。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Semaphore semaphore &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Semaphore(3); &lt;span style=&#34;color:#75715e&#34;&gt;// 最多 3 个线程并发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    semaphore.&lt;span style=&#34;color:#a6e22e&#34;&gt;acquire&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 访问受限资源&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    semaphore.&lt;span style=&#34;color:#a6e22e&#34;&gt;release&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[线程1] --&amp;gt;|acquire| S[许可池 3]
    B[线程2] --&amp;gt;|acquire| S
    C[线程3] --&amp;gt;|acquire| S
    D[线程4] --&amp;gt;|acquire 阻塞| S
    S --&amp;gt;|release| S&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #3&lt;/strong&gt;：&lt;code&gt;release()&lt;/code&gt; 一定要放在 &lt;code&gt;finally&lt;/code&gt; 里，否则异常会导致许可泄漏，最终所有线程都拿不到许可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-countdownlatch-与-cyclicbarrier&#34;&gt;3.2 CountDownLatch 与 CyclicBarrier&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特性&lt;/th&gt;
					&lt;th&gt;CountDownLatch&lt;/th&gt;
					&lt;th&gt;CyclicBarrier&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;方向&lt;/td&gt;
					&lt;td&gt;一个线程等待其他线程&lt;/td&gt;
					&lt;td&gt;多个线程互相等待&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;可复用&lt;/td&gt;
					&lt;td&gt;否（一次性）&lt;/td&gt;
					&lt;td&gt;是（可 reset）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;计数归零后&lt;/td&gt;
					&lt;td&gt;门闩打开，直接退出&lt;/td&gt;
					&lt;td&gt;所有线程同时继续&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph CountDownLatch
        M[主线程 await] -.-&amp;gt;|计数=0| M2[继续执行]
        W1[工作线程1] --&amp;gt;|countDown| C[计数器]
        W2[工作线程2] --&amp;gt;|countDown| C
    end

    subgraph CyclicBarrier
        T1[线程A await] --&amp;gt; B[屏障]
        T2[线程B await] --&amp;gt; B
        T3[线程C await] --&amp;gt; B
        B --&amp;gt;|全部到达| Go[一起继续]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #4&lt;/strong&gt;：不要把 &lt;code&gt;CountDownLatch&lt;/code&gt; 当循环栅栏用，计数到 0 后无法重置；需要循环同步用 &lt;code&gt;CyclicBarrier&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;33-exchanger&#34;&gt;3.3 Exchanger&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Exchanger&lt;/code&gt; 让两个线程在某个同步点交换数据，适合生产者-消费者配对场景，但使用频率较低。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四volatile可见性与有序性但不保证原子性&#34;&gt;四、volatile：可见性与有序性，但不保证原子性&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;volatile&lt;/code&gt; 的三条语义：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可见性&lt;/strong&gt;：写操作会刷入主内存，读操作会从主内存刷新。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有序性&lt;/strong&gt;：禁止指令重排序（happens-before）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不保证原子性&lt;/strong&gt;：复合操作（如 &lt;code&gt;i++&lt;/code&gt;）仍需要锁或原子类。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[线程A 写 volatile flag=true] --&amp;gt;| happens-before | B[线程B 读 flag]
    B --&amp;gt; C[线程B 看到 flag 之前所有写入]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 错误示例：volatile 不能保证 i++ 原子&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;volatile&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;increment&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    count&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 读-改-写，非原子&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 正确做法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; AtomicInteger count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; AtomicInteger(0);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;increment&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    count.&lt;span style=&#34;color:#a6e22e&#34;&gt;incrementAndGet&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #5&lt;/strong&gt;：只有在&lt;strong&gt;单次写、多次读、且不依赖前值&lt;/strong&gt;的场景下才用 &lt;code&gt;volatile&lt;/code&gt;（如状态标志 &lt;code&gt;volatile boolean running = true&lt;/code&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五死锁原因与破解&#34;&gt;五、死锁：原因与破解&lt;/h2&gt;
&lt;h3 id=&#34;51-典型死锁&#34;&gt;5.1 典型死锁&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    T1[线程1] --&amp;gt;|持有 A 锁| B[等待 B 锁]
    T2[线程2] --&amp;gt;|持有 B 锁| A[等待 A 锁]
    B --&amp;gt; T2
    A --&amp;gt; T1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 错误：锁顺序不一致&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;devoid &lt;span style=&#34;color:#a6e22e&#34;&gt;transfer&lt;/span&gt;(Account a, Account b, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; amount) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;synchronized&lt;/span&gt;(a) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;synchronized&lt;/span&gt;(b) { ... }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-常见诱因&#34;&gt;5.2 常见诱因&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不一致的加锁顺序&lt;/strong&gt;：两个线程以不同顺序获取多把锁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在同步块内调用外部同步方法&lt;/strong&gt;：引入了不可控的锁依赖。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持锁等待 I/O / 网络 / 用户输入&lt;/strong&gt;：放大了死锁窗口。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;53-破解方案&#34;&gt;5.3 破解方案&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;全局统一锁顺序&lt;/strong&gt;：先按对象哈希值排序再加锁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 &lt;code&gt;tryLock&lt;/code&gt; 超时获取锁&lt;/strong&gt;，失败则回退并重试。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;减少锁粒度&lt;/strong&gt;，避免在同步块内调用外部方法（Open Call）。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 使用 ReentrantLock 的 tryLock 避免死锁&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (lockA.&lt;span style=&#34;color:#a6e22e&#34;&gt;tryLock&lt;/span&gt;(1, TimeUnit.&lt;span style=&#34;color:#a6e22e&#34;&gt;SECONDS&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (lockB.&lt;span style=&#34;color:#a6e22e&#34;&gt;tryLock&lt;/span&gt;(1, TimeUnit.&lt;span style=&#34;color:#a6e22e&#34;&gt;SECONDS&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 执行业务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt; { lockB.&lt;span style=&#34;color:#a6e22e&#34;&gt;unlock&lt;/span&gt;(); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt; { lockA.&lt;span style=&#34;color:#a6e22e&#34;&gt;unlock&lt;/span&gt;(); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #6&lt;/strong&gt;：死锁往往不是锁本身的问题，而是&lt;strong&gt;锁的顺序和依赖关系&lt;/strong&gt;的问题。设计阶段先画锁依赖图，能发现大部分隐患。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六公平锁-vs-非公平锁&#34;&gt;六、公平锁 vs 非公平锁&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ReentrantLock fairLock &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ReentrantLock(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);   &lt;span style=&#34;color:#75715e&#34;&gt;// 公平&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ReentrantLock unfairLock &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ReentrantLock(&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;); &lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt; 非公平&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;（&lt;/span&gt;默认&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;）&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph 公平锁
        Q[等待队列 T1→T2→T3] --&amp;gt;|按顺序| L[锁]
    end
    subgraph 非公平锁
        N[新线程] --&amp;gt;|插队尝试| L2[锁]
        Q2[队列 T1→T2] -.-&amp;gt;|可能继续等待| L2
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;公平锁&lt;/strong&gt;：按请求顺序分配锁，减少饥饿，但上下文切换多、吞吐低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非公平锁&lt;/strong&gt;：允许“插队”，若锁恰好可用，新线程可直接获取，减少唤醒延迟；但老线程可能长时间等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #7&lt;/strong&gt;：默认选&lt;strong&gt;非公平锁&lt;/strong&gt;即可，性能更好。除非业务明确要求严格的调度公平性（如资源分配器），否则不必打开公平模式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七condition比-waitnotify-更精确的线程协作&#34;&gt;七、Condition：比 wait/notify 更精确的线程协作&lt;/h2&gt;
&lt;p&gt;一个 &lt;code&gt;ReentrantLock&lt;/code&gt; 可以创建多个 &lt;code&gt;Condition&lt;/code&gt;，分别管理不同的等待队列。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    L[ReentrantLock] --&amp;gt; C1[Condition A 队列]
    L --&amp;gt; C2[Condition B 队列]
    L --&amp;gt; C3[Condition C 队列]
    C1 --&amp;gt;|signal| W1[唤醒指定线程]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PrintABC&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Lock lock &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ReentrantLock();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Condition a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lock.&lt;span style=&#34;color:#a6e22e&#34;&gt;newCondition&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Condition b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lock.&lt;span style=&#34;color:#a6e22e&#34;&gt;newCondition&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Condition c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lock.&lt;span style=&#34;color:#a6e22e&#34;&gt;newCondition&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;volatile&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; index &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(String name, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; expect, Condition wait, Condition next) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Thread(() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            lock.&lt;span style=&#34;color:#a6e22e&#34;&gt;lock&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (index &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; expect) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    wait.&lt;span style=&#34;color:#a6e22e&#34;&gt;await&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(name);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                index &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (expect &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1) &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; 3;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                next.&lt;span style=&#34;color:#a6e22e&#34;&gt;signal&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (InterruptedException e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Thread.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentThread&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;interrupt&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lock.&lt;span style=&#34;color:#a6e22e&#34;&gt;unlock&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }).&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        PrintABC p &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PrintABC();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;A&amp;#34;&lt;/span&gt;, 0, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;B&amp;#34;&lt;/span&gt;, 1, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;C&amp;#34;&lt;/span&gt;, 2, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;c&lt;/span&gt;, p.&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #8&lt;/strong&gt;：&lt;code&gt;Condition.await/signal&lt;/code&gt; 与 &lt;code&gt;wait/notify&lt;/code&gt; 一样，必须在持有锁的线程中调用，且 &lt;code&gt;await&lt;/code&gt; 要用 &lt;code&gt;while&lt;/code&gt; 循环而不是 &lt;code&gt;if&lt;/code&gt;，防止虚假唤醒。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八线程池不要裸开线程&#34;&gt;八、线程池：不要裸开线程&lt;/h2&gt;
&lt;h3 id=&#34;81-常见工厂方法&#34;&gt;8.1 常见工厂方法&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;方法&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
					&lt;th&gt;隐患&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;newSingleThreadExecutor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;单线程，按提交顺序执行&lt;/td&gt;
					&lt;td&gt;无界队列，任务堆积会 OOM&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;newFixedThreadPool(n)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;固定线程数&lt;/td&gt;
					&lt;td&gt;同样使用无界队列&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;newCachedThreadPool&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;线程可复用、可无限创建&lt;/td&gt;
					&lt;td&gt;高并发下线程数爆炸&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;newScheduledThreadPool&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;支持定时/周期任务&lt;/td&gt;
					&lt;td&gt;注意任务异常终止&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;82-threadpoolexecutor-核心参数&#34;&gt;8.2 ThreadPoolExecutor 核心参数&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    T[提交任务] --&amp;gt;|核心线程数未满| C[创建核心线程]
    T --&amp;gt;|队列未满| Q[进入阻塞队列]
    T --&amp;gt;|队列满且线程数&amp;lt;max| M[创建非核心线程]
    T --&amp;gt;|队列满且线程数=max| R[拒绝策略]
    C --&amp;gt; E[执行]
    Q --&amp;gt; E
    M --&amp;gt; E&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ThreadPoolExecutor executor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ThreadPoolExecutor(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    4,                      &lt;span style=&#34;color:#75715e&#34;&gt;// 核心线程数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    8,                      &lt;span style=&#34;color:#75715e&#34;&gt;// 最大线程数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    60L, TimeUnit.&lt;span style=&#34;color:#a6e22e&#34;&gt;SECONDS&lt;/span&gt;,  &lt;span style=&#34;color:#75715e&#34;&gt;// 非核心线程空闲存活时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArrayBlockingQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(100), &lt;span style=&#34;color:#75715e&#34;&gt;// 有界队列&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ThreadFactoryBuilder().&lt;span style=&#34;color:#a6e22e&#34;&gt;setNameFormat&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;biz-pool-%d&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ThreadPoolExecutor.&lt;span style=&#34;color:#a6e22e&#34;&gt;CallerRunsPolicy&lt;/span&gt;() &lt;span style=&#34;color:#75715e&#34;&gt;// 拒绝策略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #9&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生产环境尽量直接创建 &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;，不要用 Executors 的便捷工厂（无界队列/无限线程是 OOM 元凶）。&lt;/li&gt;
&lt;li&gt;任务抛异常会导致线程被回收重建，建议任务内部 &lt;code&gt;try/catch&lt;/code&gt; 并记录。&lt;/li&gt;
&lt;li&gt;应用关闭前调用 &lt;code&gt;shutdown()&lt;/code&gt; 或 &lt;code&gt;shutdownNow()&lt;/code&gt;，否则 JVM 可能不会退出。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九阻塞队列&#34;&gt;九、阻塞队列&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;队列&lt;/th&gt;
					&lt;th&gt;有界性&lt;/th&gt;
					&lt;th&gt;实现&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ArrayBlockingQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;有界&lt;/td&gt;
					&lt;td&gt;数组 + ReentrantLock&lt;/td&gt;
					&lt;td&gt;高吞吐、需要指定容量&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LinkedBlockingQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;可选有界&lt;/td&gt;
					&lt;td&gt;链表 + 两把锁（take/put）&lt;/td&gt;
					&lt;td&gt;默认无界，小心 OOM&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PriorityBlockingQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;无界&lt;/td&gt;
					&lt;td&gt;堆&lt;/td&gt;
					&lt;td&gt;按优先级出队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DelayQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;无界&lt;/td&gt;
					&lt;td&gt;堆&lt;/td&gt;
					&lt;td&gt;元素到延迟时间才能出队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;SynchronousQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;零容量&lt;/td&gt;
					&lt;td&gt;无缓冲区&lt;/td&gt;
					&lt;td&gt;一手交钱一手交货&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LinkedTransferQueue&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;无界&lt;/td&gt;
					&lt;td&gt;无锁 CAS&lt;/td&gt;
					&lt;td&gt;支持 transfer 直接传递&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LinkedBlockingDeque&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;无界&lt;/td&gt;
					&lt;td&gt;双向链表&lt;/td&gt;
					&lt;td&gt;支持两端操作&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    P[生产者 put] --&amp;gt; Q[阻塞队列]
    Q --&amp;gt; C[消费者 take]
    P -.-&amp;gt;|队列满| W1[阻塞]
    C -.-&amp;gt;|队列空| W2[阻塞]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #10&lt;/strong&gt;：无界阻塞队列在生产者快、消费者慢时会把内存撑爆。优先使用 &lt;code&gt;ArrayBlockingQueue&lt;/code&gt; 或指定容量的 &lt;code&gt;LinkedBlockingQueue&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十自旋锁reentrantlock-与-synchronized&#34;&gt;十、自旋锁、ReentrantLock 与 synchronized&lt;/h2&gt;
&lt;h3 id=&#34;101-自旋锁&#34;&gt;10.1 自旋锁&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SpinLock&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; AtomicReference&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Thread&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; owner &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; AtomicReference&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lock&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Thread t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Thread.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentThread&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;owner.&lt;span style=&#34;color:#a6e22e&#34;&gt;compareAndSet&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, t)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// 自旋等待，不进入内核态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unlock&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Thread t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Thread.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentThread&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        owner.&lt;span style=&#34;color:#a6e22e&#34;&gt;compareAndSet&lt;/span&gt;(t, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[互斥锁 sleep] --&amp;gt;|上下文切换| B[内核阻塞]
    C[自旋锁 CAS] --&amp;gt;|用户态循环| D[CPU 忙等]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;优点：没有线程上下文切换开销。&lt;/li&gt;
&lt;li&gt;缺点：持锁时间长会浪费 CPU；&lt;strong&gt;同一线程不可重入&lt;/strong&gt;，递归调用会死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;102-reentrantlock-与-synchronized-对比&#34;&gt;10.2 ReentrantLock 与 synchronized 对比&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;能力&lt;/th&gt;
					&lt;th&gt;synchronized&lt;/th&gt;
					&lt;th&gt;ReentrantLock&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;可重入&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;可中断&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;lockInterruptibly()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;超时获取&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tryLock(timeout)&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;公平锁&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;可选&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多条件队列&lt;/td&gt;
					&lt;td&gt;一个 wait set&lt;/td&gt;
					&lt;td&gt;多个 Condition&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;性能&lt;/td&gt;
					&lt;td&gt;JDK6+ 优化后接近&lt;/td&gt;
					&lt;td&gt;接近&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #11&lt;/strong&gt;：&lt;code&gt;ReentrantLock&lt;/code&gt; 必须显式 &lt;code&gt;unlock()&lt;/code&gt;，否则即使异常也会死锁；务必放在 &lt;code&gt;finally&lt;/code&gt; 中。&lt;code&gt;synchronized&lt;/code&gt; 则由 JVM 保证自动释放。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;103-偏向锁&#34;&gt;10.3 偏向锁&lt;/h3&gt;
&lt;p&gt;偏向锁是 JVM 对&lt;strong&gt;无竞争同步&lt;/strong&gt;的优化：第一次获取锁时把线程 ID 记录到对象头，之后同一线程再进入无需 CAS。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：JDK 15 开始偏向锁默认禁用（&lt;code&gt;-XX:+UseBiasedLocking&lt;/code&gt; 已废弃），因为现代应用中锁竞争更频繁，撤销偏向锁的开销反而可能大于收益。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一线程安全的-singleton&#34;&gt;十一、线程安全的 Singleton&lt;/h2&gt;
&lt;h3 id=&#34;111-饿汉式类加载时初始化&#34;&gt;11.1 饿汉式（类加载时初始化）&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Singleton INSTANCE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Singleton();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Singleton &lt;span style=&#34;color:#a6e22e&#34;&gt;getInstance&lt;/span&gt;() { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; INSTANCE; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;借助类加载机制保证线程安全，简单可靠；若实例创建开销大且未必使用，则浪费资源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;112-静态内部类推荐&#34;&gt;11.2 静态内部类（推荐）&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Holder&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; Singleton INSTANCE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Singleton();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Singleton &lt;span style=&#34;color:#a6e22e&#34;&gt;getInstance&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Holder.&lt;span style=&#34;color:#a6e22e&#34;&gt;INSTANCE&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[调用 getInstance] --&amp;gt;|首次触发| B[加载 Holder 类]
    B --&amp;gt; C[初始化 INSTANCE]
    C --&amp;gt; D[返回唯一实例]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;延迟加载 + 类加载保证线程安全，是大多数场景下的最佳实践。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;113-双重检查锁dcl-volatile&#34;&gt;11.3 双重检查锁（DCL）+ volatile&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Singleton&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;volatile&lt;/span&gt; Singleton instance;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; Singleton &lt;span style=&#34;color:#a6e22e&#34;&gt;getInstance&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (instance &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;synchronized&lt;/span&gt; (Singleton.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (instance &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    instance &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Singleton(); &lt;span style=&#34;color:#75715e&#34;&gt;// 禁止指令重排&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; instance;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #12&lt;/strong&gt;：DCL 必须加 &lt;code&gt;volatile&lt;/code&gt;，否则 &lt;code&gt;new Singleton()&lt;/code&gt; 的指令重排会导致其他线程读到未构造完成的对象。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十二synchronized-的底层行为&#34;&gt;十二、synchronized 的底层行为&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;synchronized&lt;/code&gt; 可以修饰方法或代码块，锁对象分别是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实例方法：当前实例 &lt;code&gt;this&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;静态方法：类的 &lt;code&gt;Class&lt;/code&gt; 对象。&lt;/li&gt;
&lt;li&gt;代码块：括号里指定的对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    O[对象头 Mark Word] --&amp;gt;|无锁| A[无锁状态]
    A --&amp;gt;|第一个线程获取| B[偏向锁]
    B --&amp;gt;|出现竞争| C[轻量级锁 CAS]
    C --&amp;gt;|竞争加剧| D[重量级锁 操作系统 Mutex]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #13&lt;/strong&gt;：不要把 &lt;code&gt;synchronized&lt;/code&gt; 当成“只要包住就安全”的万金油。锁的对象要选对，锁的粒度要尽量小，避免锁住整个方法或无关代码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十三避坑速查表&#34;&gt;十三、避坑速查表&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;不要这样做&lt;/th&gt;
					&lt;th&gt;推荐做法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;手写 &lt;code&gt;wait/notify&lt;/code&gt; 做复杂协调&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;CountDownLatch&lt;/code&gt;、&lt;code&gt;CyclicBarrier&lt;/code&gt;、&lt;code&gt;Semaphore&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;用 &lt;code&gt;volatile&lt;/code&gt; 做 &lt;code&gt;i++&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;AtomicInteger&lt;/code&gt; 或 &lt;code&gt;LongAdder&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;裸 &lt;code&gt;new Thread()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Executors.newFixedThreadPool&lt;/code&gt; 无脑用&lt;/td&gt;
					&lt;td&gt;自定义有界队列 + 拒绝策略&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;在锁里调外部同步方法 / 阻塞 I/O&lt;/td&gt;
					&lt;td&gt;缩小锁范围，使用 Open Call&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;不一致的加锁顺序&lt;/td&gt;
					&lt;td&gt;全局排序或使用 &lt;code&gt;tryLock&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DCL 不加 &lt;code&gt;volatile&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;必须加 &lt;code&gt;volatile&lt;/code&gt;，或改用静态内部类&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;自旋锁里递归加锁&lt;/td&gt;
					&lt;td&gt;使用可重入锁 &lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;忽略 &lt;code&gt;InterruptedException&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;恢复中断标志 &lt;code&gt;Thread.currentThread().interrupt()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;线程池任务吞掉异常&lt;/td&gt;
					&lt;td&gt;任务内部 &lt;code&gt;try/catch&lt;/code&gt; 或使用 &lt;code&gt;Future.get()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十四总结&#34;&gt;十四、总结&lt;/h2&gt;
&lt;p&gt;Java 高并发编程的演进路径，其实是从“手动管理线程和锁”走向“使用高层抽象”。牢记下面三句话：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;能不变就不变&lt;/strong&gt;：优先使用不可变对象、线程封闭和 JUC 并发集合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;能用高层就不用底层&lt;/strong&gt;：用 &lt;code&gt;Semaphore&lt;/code&gt;、&lt;code&gt;CountDownLatch&lt;/code&gt;、&lt;code&gt;Executor&lt;/code&gt; 替代 &lt;code&gt;wait/notify&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锁是最后手段&lt;/strong&gt;：如果必须用锁，确保加锁顺序一致、粒度最小、持有时间最短。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把这三点落地到代码里，配合上面的图例和示例，就能避开大多数并发陷阱。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;参考与延伸阅读&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《Java 并发编程实战》（Java Concurrency in Practice）&lt;/li&gt;
&lt;li&gt;《Java 并发编程的艺术》&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/package-summary.html&#34;&gt;JDK 文档：java.util.concurrent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>JVM 内功修炼：运行时数据区、GC 演进与现代调优实践</title>
      <link>https://ftmao.com/blog/posts/jvm-internals-guide/</link>
      <pubDate>Wed, 28 Mar 2018 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/jvm-internals-guide/</guid>
      <description>&lt;h1 id=&#34;jvm-内功修炼运行时数据区gc-演进与现代调优实践&#34;&gt;JVM 内功修炼：运行时数据区、GC 演进与现代调优实践&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;JVM 是 Java 程序员逃不掉的「操作系统」。懂它不是为了背八股，而是为了在 OOM、Full GC、CPU 飙升时，能一眼定位根因并给出调优方案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一高层次思想jvm-在帮你解决什么问题&#34;&gt;一、高层次思想：JVM 在帮你解决什么问题？&lt;/h2&gt;
&lt;p&gt;JVM 的核心职责可以抽象成三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一次编译，到处运行&lt;/strong&gt;：通过字节码 + 解释/编译执行，屏蔽底层 OS 与硬件差异。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动内存管理&lt;/strong&gt;：让开发者专注于业务，而不是像 C/C++ 那样手动 malloc/free。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时优化&lt;/strong&gt;：JIT 编译、逃逸分析、内联、锁消除等，把「解释执行」慢慢优化成「本地机器码执行」。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[.java 源码] --&amp;gt;|javac| B[字节码 .class]
    B --&amp;gt;|类加载子系统| C[方法区/Metaspace]
    C --&amp;gt;|解释器| D[逐行解释执行]
    C --&amp;gt;|JIT 编译器| E[本地机器码]
    D --&amp;gt; F[执行引擎]
    E --&amp;gt; F
    F --&amp;gt; G[操作系统]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #1&lt;/strong&gt;：不要只把 JVM 当「黑盒」。线上 90% 的 Java 性能问题，根源都在堆外内存泄漏、GC 停顿过长、Metaspace 膨胀、线程栈溢出这四类。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二运行时数据区新版-jdk-的版图&#34;&gt;二、运行时数据区：新版 JDK 的版图&lt;/h2&gt;
&lt;p&gt;不同虚拟机的运行时数据区实现略有差异，但都遵循《Java 虚拟机规范》。现代视角下，区域可划分为「线程私有」和「线程共享」两类：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph 线程私有
        A[程序计数器 PC Register]
        B[虚拟机栈 JVM Stack]
        C[本地方法栈 Native Method Stack]
    end
    subgraph 线程共享
        D[堆 Heap]
        E[方法区 Method Area / Metaspace]
        F[直接内存 Direct Memory]
    end
    A --&amp;gt; G[线程执行上下文]
    B --&amp;gt; G
    C --&amp;gt; G
    D --&amp;gt; H[几乎所有对象实例]
    E --&amp;gt; I[类元数据/常量/静态变量]
    F --&amp;gt; J[NIO / 堆外内存]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;21-程序计数器program-counter-register&#34;&gt;2.1 程序计数器（Program Counter Register）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;当前线程执行字节码的&lt;strong&gt;行号指示器&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;分支、循环、跳转、异常处理、线程恢复都依赖它。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程私有&lt;/strong&gt;，生命周期与线程一致。&lt;/li&gt;
&lt;li&gt;唯一一个在《Java 虚拟机规范》中&lt;strong&gt;没有规定 OOM&lt;/strong&gt; 的区域。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-java-虚拟机栈jvm-stack&#34;&gt;2.2 Java 虚拟机栈（JVM Stack）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每个方法执行时创建一个&lt;strong&gt;栈帧&lt;/strong&gt;（Stack Frame）。&lt;/li&gt;
&lt;li&gt;栈帧里包含：局部变量表、操作数栈、动态链接、方法返回地址。&lt;/li&gt;
&lt;li&gt;局部变量表中的基本数据类型和对象引用在编译期大小就确定。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[JVM Stack] --&amp;gt; B[栈帧 Frame 1]
    A --&amp;gt; C[栈帧 Frame 2]
    A --&amp;gt; D[栈帧 Frame 3]
    B --&amp;gt; B1[局部变量表]
    B --&amp;gt; B2[操作数栈]
    B --&amp;gt; B3[动态链接]
    B --&amp;gt; B4[方法返回地址]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #2&lt;/strong&gt;：&lt;code&gt;-Xss&lt;/code&gt; 设置线程栈大小。递归过深、方法调用链过长会抛出 &lt;code&gt;StackOverflowError&lt;/code&gt;；如果线程创建太多，总栈空间超过限制，会抛出 &lt;code&gt;OutOfMemoryError: unable to create new native thread&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;23-本地方法栈native-method-stack&#34;&gt;2.3 本地方法栈（Native Method Stack）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;与虚拟机栈作用相同，只是为 Native 方法服务。&lt;/li&gt;
&lt;li&gt;HotSpot 中把虚拟机栈和本地方法栈合二为一了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;24-java-堆java-heap&#34;&gt;2.4 Java 堆（Java Heap）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JVM 管理的&lt;strong&gt;最大一块内存&lt;/strong&gt;，被所有线程共享。&lt;/li&gt;
&lt;li&gt;几乎所有对象实例和数组都在这里分配。&lt;/li&gt;
&lt;li&gt;现代 GC 基本都采用&lt;strong&gt;分代收集&lt;/strong&gt;，逻辑上分为新生代、老年代。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[Java Heap] --&amp;gt; B[Young Generation 新生代]
    A --&amp;gt; C[Old Generation 老年代]
    B --&amp;gt; D[Eden 区]
    B --&amp;gt; E[Survivor From]
    B --&amp;gt; F[Survivor To]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #3&lt;/strong&gt;：不要把「堆」和「栈」对立记忆。对象在堆里，引用在栈里；但 HotSpot 的&lt;strong&gt;逃逸分析 + 标量替换&lt;/strong&gt;会把未逃逸的小对象直接分配在栈上，减轻 GC 压力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;25-方法区method-area与-metaspace&#34;&gt;2.5 方法区（Method Area）与 Metaspace&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储类信息、常量、静态变量、JIT 编译后的代码缓存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JDK 7 之前&lt;/strong&gt;：方法区在 PermGen（永久代）中，大小固定，&lt;code&gt;-XX:MaxPermSize&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JDK 8 及以后&lt;/strong&gt;：PermGen 被移除，方法区使用本地内存中的 &lt;strong&gt;Metaspace&lt;/strong&gt;，默认只受限于系统内存，用 &lt;code&gt;-XX:MaxMetaspaceSize&lt;/code&gt; 控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;版本&lt;/th&gt;
					&lt;th&gt;实现&lt;/th&gt;
					&lt;th&gt;配置参数&lt;/th&gt;
					&lt;th&gt;常见问题&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;JDK 7-&lt;/td&gt;
					&lt;td&gt;PermGen&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-XX:PermSize&lt;/code&gt; / &lt;code&gt;-XX:MaxPermSize&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;动态生成类过多导致 &lt;code&gt;OutOfMemoryError: PermGen space&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JDK 8+&lt;/td&gt;
					&lt;td&gt;Metaspace&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-XX:MetaspaceSize&lt;/code&gt; / &lt;code&gt;-XX:MaxMetaspaceSize&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;类加载器泄漏导致 Metaspace 持续增长&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #4&lt;/strong&gt;：Metaspace 默认无上限，服务运行久了可能把容器内存撑爆。线上建议显式设置 &lt;code&gt;-XX:MaxMetaspaceSize&lt;/code&gt;，配合监控告警。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;26-直接内存direct-memory&#34;&gt;2.6 直接内存（Direct Memory）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不属于 JVM 堆，但会被 JVM 直接使用，常见于 NIO 的 &lt;code&gt;DirectByteBuffer&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;分配和回收成本较高，但读写 I/O 时避免了堆内外内存拷贝（Zero Copy）。&lt;/li&gt;
&lt;li&gt;默认大小约等于 &lt;code&gt;-Xmx&lt;/code&gt;，可用 &lt;code&gt;-XX:MaxDirectMemorySize&lt;/code&gt; 控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #5&lt;/strong&gt;：Netty、RocketMQ、Elasticsearch 等中间件大量使用堆外内存。出现 &lt;code&gt;OutOfMemoryError: Direct buffer memory&lt;/code&gt; 时，先检查是否有 &lt;code&gt;ByteBuf&lt;/code&gt; / &lt;code&gt;DirectByteBuffer&lt;/code&gt; 未释放，而不是盲目加堆内存。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三垃圾回收从算法到收集器演进&#34;&gt;三、垃圾回收：从算法到收集器演进&lt;/h2&gt;
&lt;h3 id=&#34;31-判断对象是否存活&#34;&gt;3.1 判断对象是否存活&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;引用计数法&lt;/strong&gt;：Python 用，但 Java 不用，因为解决不了循环引用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可达性分析（Reachability Analysis）&lt;/strong&gt;：从 GC Roots 出发，不可达则回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见 GC Roots：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;虚拟机栈中局部变量表引用的对象。&lt;/li&gt;
&lt;li&gt;方法区中类静态属性、常量引用的对象。&lt;/li&gt;
&lt;li&gt;本地方法栈中 JNI 引用的对象。&lt;/li&gt;
&lt;li&gt;被同步锁持有的对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[GC Roots] --&amp;gt; B[栈帧局部变量]
    A --&amp;gt; C[静态变量]
    A --&amp;gt; D[常量]
    A --&amp;gt; E[JNI 引用]
    A --&amp;gt; F[锁持有对象]
    B --&amp;gt; G[可达对象]
    C --&amp;gt; G
    D --&amp;gt; H[不可达对象&amp;lt;br/&amp;gt;等待回收]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;32-四种基础-gc-算法&#34;&gt;3.2 四种基础 GC 算法&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;算法&lt;/th&gt;
					&lt;th&gt;思想&lt;/th&gt;
					&lt;th&gt;优点&lt;/th&gt;
					&lt;th&gt;缺点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;标记-清除&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;标记无用对象，然后清除&lt;/td&gt;
					&lt;td&gt;简单，不需要移动对象&lt;/td&gt;
					&lt;td&gt;效率低，产生内存碎片&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;复制&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;内存分两块，活着的对象复制到另一块&lt;/td&gt;
					&lt;td&gt;无碎片，回收效率高&lt;/td&gt;
					&lt;td&gt;内存利用率只有一半&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;标记-整理&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;标记后把存活对象移到一端，清理边界外&lt;/td&gt;
					&lt;td&gt;无碎片，内存利用率高&lt;/td&gt;
					&lt;td&gt;移动对象开销大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;分代收集&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按对象存活周期分代，不同代用不同算法&lt;/td&gt;
					&lt;td&gt;兼顾吞吐与停顿&lt;/td&gt;
					&lt;td&gt;需要维护复杂的跨代引用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;33-经典垃圾收集器&#34;&gt;3.3 经典垃圾收集器&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;收集器&lt;/th&gt;
					&lt;th&gt;工作区域&lt;/th&gt;
					&lt;th&gt;算法&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
					&lt;th&gt;状态&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Serial&lt;/td&gt;
					&lt;td&gt;新生代&lt;/td&gt;
					&lt;td&gt;复制&lt;/td&gt;
					&lt;td&gt;单线程，简单高效&lt;/td&gt;
					&lt;td&gt;客户端模式 / 单核&lt;/td&gt;
					&lt;td&gt;可用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ParNew&lt;/td&gt;
					&lt;td&gt;新生代&lt;/td&gt;
					&lt;td&gt;复制&lt;/td&gt;
					&lt;td&gt;Serial 的多线程版&lt;/td&gt;
					&lt;td&gt;配合 CMS&lt;/td&gt;
					&lt;td&gt;随 CMS 移除&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Parallel Scavenge&lt;/td&gt;
					&lt;td&gt;新生代&lt;/td&gt;
					&lt;td&gt;复制&lt;/td&gt;
					&lt;td&gt;吞吐量优先&lt;/td&gt;
					&lt;td&gt;后台计算&lt;/td&gt;
					&lt;td&gt;可用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Serial Old&lt;/td&gt;
					&lt;td&gt;老年代&lt;/td&gt;
					&lt;td&gt;标记-整理&lt;/td&gt;
					&lt;td&gt;单线程&lt;/td&gt;
					&lt;td&gt;客户端模式&lt;/td&gt;
					&lt;td&gt;可用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Parallel Old&lt;/td&gt;
					&lt;td&gt;老年代&lt;/td&gt;
					&lt;td&gt;标记-整理&lt;/td&gt;
					&lt;td&gt;多线程吞吐量优先&lt;/td&gt;
					&lt;td&gt;后台计算&lt;/td&gt;
					&lt;td&gt;可用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CMS&lt;/td&gt;
					&lt;td&gt;老年代&lt;/td&gt;
					&lt;td&gt;标记-清除&lt;/td&gt;
					&lt;td&gt;低停顿，并发收集&lt;/td&gt;
					&lt;td&gt;互联网低延迟服务&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;JDK 14 已移除&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;G1&lt;/td&gt;
					&lt;td&gt;整堆&lt;/td&gt;
					&lt;td&gt;标记-整理 + 复制&lt;/td&gt;
					&lt;td&gt;可控停顿， region 化&lt;/td&gt;
					&lt;td&gt;大堆、低延迟&lt;/td&gt;
					&lt;td&gt;JDK 9-22 默认&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ZGC&lt;/td&gt;
					&lt;td&gt;整堆&lt;/td&gt;
					&lt;td&gt;标记-复制/整理&lt;/td&gt;
					&lt;td&gt;亚毫秒停顿，TB 级堆&lt;/td&gt;
					&lt;td&gt;极低延迟&lt;/td&gt;
					&lt;td&gt;JDK 23+ 默认（分代）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Shenandoah&lt;/td&gt;
					&lt;td&gt;整堆&lt;/td&gt;
					&lt;td&gt;标记-复制/整理&lt;/td&gt;
					&lt;td&gt;低停顿，独立于分代&lt;/td&gt;
					&lt;td&gt;低延迟&lt;/td&gt;
					&lt;td&gt;可用，JDK 24 实验分代&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[Serial] --&amp;gt; B[ParNew]
    B --&amp;gt; C[Parallel Scavenge]
    C --&amp;gt; D[G1]
    D --&amp;gt; E[ZGC / Shenandoah]
    style E fill:#f9f,stroke:#333&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;34-分代模型的工作流程&#34;&gt;3.4 分代模型的工作流程&lt;/h3&gt;
&lt;p&gt;新生代默认占堆的 1/3，老年代占 2/3。新生代内部 Eden:From Survivor:To Survivor 默认约为 8:1:1。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant Eden
    participant From as Survivor From
    participant To as Survivor To
    participant Old as Old Generation
    Note over Eden,Old: Minor GC 触发
    Eden-&amp;gt;&amp;gt;To: 存活对象复制到 To
    From-&amp;gt;&amp;gt;To: 存活对象复制到 To
    From-&amp;gt;&amp;gt;Old: 年龄超过阈值的对象晋升
    Note over Eden,From: 清空 Eden 和 From
    To-&amp;gt;&amp;gt;From: To 变 From，From 变 To&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #6&lt;/strong&gt;：不要把 Survivor 区看作「两个对称缓存区」。它们在一次 Minor GC 后会&lt;strong&gt;交换身份&lt;/strong&gt;，始终有一个是空的，用于下一次复制目标。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;35-g1-收集器里程碑式的设计&#34;&gt;3.5 G1 收集器：里程碑式的设计&lt;/h3&gt;
&lt;p&gt;G1（Garbage First）把堆划分为多个大小相等的 Region，每个 Region 动态扮演 Eden、Survivor 或 Old 角色。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[G1 Heap Regions] --&amp;gt; B[Eden Region]
    A --&amp;gt; C[Survivor Region]
    A --&amp;gt; D[Old Region]
    A --&amp;gt; E[Humongous Region&amp;lt;br/&amp;gt;大对象]
    A --&amp;gt; F[Free Region]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;G1 的核心优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可预测的停顿时间模型&lt;/strong&gt;：通过 &lt;code&gt;-XX:MaxGCPauseMillis&lt;/code&gt; 设定目标停顿。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;整体基于标记-整理，局部基于复制&lt;/strong&gt;：不会产生大面积内存碎片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回收优先级按垃圾最多的 Region 来&lt;/strong&gt;：Garbage First 名字来源。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #7&lt;/strong&gt;：G1 的 &lt;code&gt;-XX:MaxGCPauseMillis&lt;/code&gt; 不是越小越好。设得太低（如 50ms）会导致回收 Region 太少、GC 频率飙升，反而降低吞吐。通常建议 100-200ms。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;36-现代低延迟-gczgc-与-shenandoah&#34;&gt;3.6 现代低延迟 GC：ZGC 与 Shenandoah&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特性&lt;/th&gt;
					&lt;th&gt;ZGC&lt;/th&gt;
					&lt;th&gt;Shenandoah&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;设计目标&lt;/td&gt;
					&lt;td&gt;亚毫秒级停顿，支持 TB 级堆&lt;/td&gt;
					&lt;td&gt;低停顿，独立于分代&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;并发能力&lt;/td&gt;
					&lt;td&gt;标记、转移、重定位几乎都并发&lt;/td&gt;
					&lt;td&gt;并发标记、并发压缩&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;着色指针&lt;/td&gt;
					&lt;td&gt;使用&lt;/td&gt;
					&lt;td&gt;不使用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;分代&lt;/td&gt;
					&lt;td&gt;JDK 21 引入分代 ZGC，JDK 23 起为默认&lt;/td&gt;
					&lt;td&gt;JDK 24 引入实验性分代 Shenandoah&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;适用版本&lt;/td&gt;
					&lt;td&gt;JDK 11+（生产推荐 JDK 17+）&lt;/td&gt;
					&lt;td&gt;OpenJDK 12+&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[传统 GC&amp;lt;br/&amp;gt;Serial/Parallel/CMS/G1] --&amp;gt;|停顿目标 ms~百 ms 级| B[G1]
    B --&amp;gt;|停顿目标 &amp;lt; 10ms| C[ZGC / Shenandoah]
    C --&amp;gt;|JDK 21+| D[分代 ZGC]
    D --&amp;gt;|JDK 23+| E[分代 ZGC 默认]
    C --&amp;gt;|JDK 24+| F[分代 Shenandoah 实验]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;高性能建议 #1&lt;/strong&gt;：如果应用对延迟敏感（如金融交易、在线游戏、实时推荐），JDK 17 下优先考虑 &lt;strong&gt;G1&lt;/strong&gt;；JDK 21+ 可直接启用 &lt;strong&gt;分代 ZGC&lt;/strong&gt;（&lt;code&gt;-XX:+UseZGC -XX:+ZGenerational&lt;/code&gt;），在吞吐和延迟之间取得更好平衡；升级到 &lt;strong&gt;JDK 23+&lt;/strong&gt; 后分代 ZGC 已是默认，无需额外参数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;37-jdk-21222324-的-gc-演进大事记&#34;&gt;3.7 JDK 21/22/23/24 的 GC 演进大事记&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;JDK 版本&lt;/th&gt;
					&lt;th&gt;GC 相关变化&lt;/th&gt;
					&lt;th&gt;影响&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 21&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/439&#34;&gt;JEP 439&lt;/a&gt; 正式发布&lt;strong&gt;分代 ZGC&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;默认仍是 G1，但可显式启用 &lt;code&gt;-XX:+ZGenerational&lt;/code&gt; 获得更低延迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 22&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/423&#34;&gt;JEP 423&lt;/a&gt; G1 Region Pinning&lt;/td&gt;
					&lt;td&gt;G1 在 JNI 临界区内无需禁用 GC，降低延迟毛刺&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 23&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/474&#34;&gt;JEP 474&lt;/a&gt; &lt;strong&gt;分代 ZGC 成为默认&lt;/strong&gt;；非分代 ZGC 废弃&lt;/td&gt;
					&lt;td&gt;新默认 GC 从 G1 切换到分代 ZGC，亚毫秒级停顿更易获得&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 23&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://bugs.openjdk.org/browse/JDK-8322630&#34;&gt;JDK-8322630&lt;/a&gt; ZGC 延迟优化&lt;/td&gt;
					&lt;td&gt;减少 GC 根节点扫描抖动&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 24&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/490&#34;&gt;JEP 490&lt;/a&gt; 移除非分代 ZGC&lt;/td&gt;
					&lt;td&gt;简化 ZGC 维护，统一使用分代模式&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 24&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/404&#34;&gt;JEP 404&lt;/a&gt; &lt;strong&gt;分代 Shenandoah（实验性）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Shenandoah 也走向分代，未来低延迟 GC 竞争更激烈&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JDK 24&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;a href=&#34;https://openjdk.org/jeps/475&#34;&gt;JEP 475&lt;/a&gt; G1 Late Barrier Expansion&lt;/td&gt;
					&lt;td&gt;优化 G1 写屏障，提升编译和运行时性能&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;timeline
    title GC 默认与里程碑
    section JDK 8
        默认 : Parallel Scavenge + Parallel Old
    section JDK 9
        默认 : G1
    section JDK 11
        ZGC 实验性
    section JDK 15
        ZGC 生产可用
    section JDK 17
        G1 默认，ZGC/Shenandoah 成熟
    section JDK 21
        分代 ZGC 正式发布（仍非默认）
    section JDK 23
        分代 ZGC 成为默认 GC
    section JDK 24
        非分代 ZGC 移除；分代 Shenandoah 实验&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #7.5&lt;/strong&gt;：网上有说法称 &amp;ldquo;JDK 21 默认 GC 是 ZGC&amp;rdquo;，这是&lt;strong&gt;错误&lt;/strong&gt;的。JDK 21 默认仍是 &lt;strong&gt;G1&lt;/strong&gt;；分代 ZGC 只是正式发布，需要显式开启。真正的默认切换发生在 &lt;strong&gt;JDK 23&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四引用类型不只是强引用&#34;&gt;四、引用类型：不只是强引用&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;引用类型&lt;/th&gt;
					&lt;th&gt;回收时机&lt;/th&gt;
					&lt;th&gt;使用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;强引用&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;永不回收，即使 OOM&lt;/td&gt;
					&lt;td&gt;普通对象&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;软引用 SoftReference&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;内存不足前回收&lt;/td&gt;
					&lt;td&gt;缓存，如图片缓存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;弱引用 WeakReference&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;下一次 GC 时回收&lt;/td&gt;
					&lt;td&gt;防止内存泄漏，如 &lt;code&gt;WeakHashMap&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;虚引用 PhantomReference&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;随时可能被回收，必须配合 ReferenceQueue&lt;/td&gt;
					&lt;td&gt;跟踪对象被回收的事件，做资源后置清理&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[强引用] --&amp;gt;|最强| B[不会被回收]
    C[软引用] --&amp;gt;|内存紧张| D[回收]
    E[弱引用] --&amp;gt;|下次 GC| F[回收]
    G[虚引用] --&amp;gt;|无法获取对象| H[仅回收通知]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #8&lt;/strong&gt;：不要把软引用当「万能缓存」。在内存紧张时软引用会被批量回收，导致缓存命中率骤降。高并发缓存建议用 Caffeine 等带 TTL 和淘汰策略的专业缓存框架。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五类加载机制双亲委派与破坏&#34;&gt;五、类加载机制：双亲委派与破坏&lt;/h2&gt;
&lt;h3 id=&#34;51-类加载器层次&#34;&gt;5.1 类加载器层次&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[自定义类加载器] --&amp;gt;|委托| B[应用程序类加载器 AppClassLoader]
    B --&amp;gt;|委托| C[平台类加载器 PlatformClassLoader]
    C --&amp;gt;|委托| D[启动类加载器 BootstrapClassLoader]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：JDK 9 引入模块化系统后，原来的 &lt;strong&gt;Extension ClassLoader&lt;/strong&gt; 被改名为 &lt;strong&gt;Platform ClassLoader&lt;/strong&gt;，负责加载平台模块（如 &lt;code&gt;java.sql&lt;/code&gt;、&lt;code&gt;java.xml&lt;/code&gt; 等）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;52-双亲委派模型&#34;&gt;5.2 双亲委派模型&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;类加载器收到加载请求，先不自己加载。&lt;/li&gt;
&lt;li&gt;向上委托给父加载器。&lt;/li&gt;
&lt;li&gt;父加载器无法加载时，子加载器才尝试加载。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #9&lt;/strong&gt;：Tomcat、SPI、OSGi 都&lt;strong&gt;破坏了双亲委派&lt;/strong&gt;。如果你的项目出现 &lt;code&gt;NoSuchMethodError&lt;/code&gt; / &lt;code&gt;ClassCastException: cannot cast to the same class&lt;/code&gt;，很可能是同一个类被不同类加载器加载了两次。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六jit-编译与执行优化&#34;&gt;六、JIT 编译与执行优化&lt;/h2&gt;
&lt;p&gt;Java 代码先被解释器执行，热点代码会被 JIT 编译成本地机器码。&lt;/p&gt;
&lt;h3 id=&#34;61-hotspot-的两层编译器&#34;&gt;6.1 HotSpot 的两层编译器&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;编译器&lt;/th&gt;
					&lt;th&gt;别称&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;C1&lt;/td&gt;
					&lt;td&gt;Client Compiler&lt;/td&gt;
					&lt;td&gt;编译快，优化简单，适合启动速度和 GUI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;C2&lt;/td&gt;
					&lt;td&gt;Server Compiler&lt;/td&gt;
					&lt;td&gt;编译慢，激进优化，适合长期运行的服务端&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;JDK 7 引入&lt;strong&gt;分层编译&lt;/strong&gt;（Tiered Compilation），JDK 8 默认开启：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0: 解释执行
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1~3: C1 编译，带简单/复杂优化
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4: C2 编译，激进优化&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;62-常见-jit-优化&#34;&gt;6.2 常见 JIT 优化&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;优化&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;方法内联&lt;/td&gt;
					&lt;td&gt;把短方法体直接嵌入调用处，减少栈帧开销&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;逃逸分析&lt;/td&gt;
					&lt;td&gt;判断对象是否逃出方法/线程作用域&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;标量替换&lt;/td&gt;
					&lt;td&gt;把对象拆成基本类型分配在栈上&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;锁消除&lt;/td&gt;
					&lt;td&gt;确认对象只被单线程访问时去掉同步锁&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;锁粗化&lt;/td&gt;
					&lt;td&gt;把相邻的同步块合并，减少加解锁次数&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;高性能建议 #2&lt;/strong&gt;：不要过度纠结「每个对象都在堆上分配」。现代 JVM 的逃逸分析已经很强，写代码时优先保证&lt;strong&gt;对象作用域最小化&lt;/strong&gt;、&lt;strong&gt;避免无意义的全局引用&lt;/strong&gt;，让 JIT 有优化空间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七现代-jvm-前沿特性jdk-21&#34;&gt;七、现代 JVM 前沿特性（JDK 21+）&lt;/h2&gt;
&lt;p&gt;除了 GC，JDK 21 及之后版本还在内存模型、执行模型和 Native 互操作上做了大量演进，这些也会间接影响 JVM 调优思路。&lt;/p&gt;
&lt;h3 id=&#34;71-虚拟线程virtual-threadsjep-444--jdk-21&#34;&gt;7.1 虚拟线程（Virtual Threads，JEP 444 / JDK 21）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;虚拟线程是 JVM 管理的轻量级线程，由 JVM 调度到少量平台线程（Carrier Thread）上运行。&lt;/li&gt;
&lt;li&gt;创建成本极低（KB 级栈），适合高并发 I/O 密集型应用。&lt;/li&gt;
&lt;li&gt;对 JVM 数据区的影响：虚拟线程的栈帧仍存在于 Java 堆中（作为 &lt;code&gt;Continuation&lt;/code&gt; 对象），而不是传统的线程栈。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[平台线程 Platform Thread] --&amp;gt;|1:N 映射| B[虚拟线程 Virtual Thread]
    B --&amp;gt;|挂载| C[Carrier Thread]
    C --&amp;gt;|运行在| D[OS 线程]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #9.5&lt;/strong&gt;：虚拟线程虽好，但不要和 &lt;code&gt;synchronized&lt;/code&gt; 块、本地方法、文件 I/O 混用导致 &amp;ldquo;Pinning&amp;rdquo;，会让载体线程被卡住，降低并发收益。优先使用 &lt;code&gt;ReentrantLock&lt;/code&gt; 和异步 I/O。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;72-foreign-function--memory-apijep-454--jdk-22&#34;&gt;7.2 Foreign Function &amp;amp; Memory API（JEP 454 / JDK 22）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;正式替代 &lt;code&gt;sun.misc.Unsafe&lt;/code&gt; 进行堆外内存操作。&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;Arena&lt;/code&gt; 管理堆外内存生命周期，更安全、更可移植。&lt;/li&gt;
&lt;li&gt;对直接内存调优的影响：未来 &lt;code&gt;DirectByteBuffer&lt;/code&gt; 的底层实现会逐步迁移到 FFM API。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;73-project-lilliput压缩对象头&#34;&gt;7.3 Project Lilliput：压缩对象头&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;目标是把 HotSpot 的对象头从 96/128 bit 压缩到 64 bit。&lt;/li&gt;
&lt;li&gt;可以显著降低内存占用，提升缓存命中率。&lt;/li&gt;
&lt;li&gt;JDK 24 起部分功能已集成（如 64-bit object headers）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;74-project-valhalla值类型value-classes--primitive-classes&#34;&gt;7.4 Project Valhalla：值类型（Value Classes / Primitive Classes）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;引入 &amp;ldquo;值对象&amp;rdquo;，避免对象头和引用开销。&lt;/li&gt;
&lt;li&gt;未来 &lt;code&gt;int[]&lt;/code&gt; 和 &lt;code&gt;Integer[]&lt;/code&gt; 之间的性能鸿沟会缩小。&lt;/li&gt;
&lt;li&gt;目前处于预览阶段，持续关注即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;75-project-leydenjava-静态镜像&#34;&gt;7.5 Project Leyden：Java 静态镜像&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;目标是通过 &amp;ldquo;静态图像&amp;rdquo; 加速 Java 启动时间。&lt;/li&gt;
&lt;li&gt;类似 GraalVM Native Image，但保持完整 Java 语义和 JVM 运行时能力。&lt;/li&gt;
&lt;li&gt;对 Serverless、CLI 工具、云原生场景意义重大。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;八避坑指南线上常见问题&#34;&gt;八、避坑指南：线上常见问题&lt;/h2&gt;
&lt;h3 id=&#34;81-慎用-systemgc&#34;&gt;8.1 慎用 &lt;code&gt;System.gc()&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;只是「建议」JVM 进行 Full GC，不保证执行。&lt;/li&gt;
&lt;li&gt;很多框架（如某些老版本 JDBC）会调用它，导致不可控停顿。&lt;/li&gt;
&lt;li&gt;线上建议关闭：&lt;code&gt;-XX:+DisableExplicitGC&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;82-finalize-已过时&#34;&gt;8.2 finalize() 已过时&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JDK 9 起标记为 &lt;code&gt;@Deprecated&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;对象回收前不保证一定执行 &lt;code&gt;finalize()&lt;/code&gt;，且执行顺序不确定。&lt;/li&gt;
&lt;li&gt;资源清理用 &lt;code&gt;try-with-resources&lt;/code&gt; 或 &lt;code&gt;Cleaner&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;83-大对象与-humongous&#34;&gt;8.3 大对象与 Humongous&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;G1 中超过 Region 大小一半的对象叫 Humongous Object，直接进老年代，增加 GC 负担。&lt;/li&gt;
&lt;li&gt;避免一次性创建超大数组或字符串。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;84-动态代理与-cglib-的-metaspace-泄漏&#34;&gt;8.4 动态代理与 CGLIB 的 Metaspace 泄漏&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spring AOP、MyBatis、Dubbo 大量生成代理类。&lt;/li&gt;
&lt;li&gt;如果 ClassLoader 没释放，代理类元数据会持续占用 Metaspace。&lt;/li&gt;
&lt;li&gt;常见于热部署、OSGi、动态脚本场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;85-大堆--full-gc-的雪崩&#34;&gt;8.5 大堆 + Full GC 的雪崩&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;堆内存越大，G1 的 Mixed GC、CMS 的 Full GC 停顿越长。&lt;/li&gt;
&lt;li&gt;超过 32GB 时，指针压缩会失效，对象头变大，反而浪费内存。&lt;/li&gt;
&lt;li&gt;建议堆大小控制在 16GB-32GB 之间，再大的堆考虑横向拆分服务或换 ZGC。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九高性能调优建议&#34;&gt;九、高性能调优建议&lt;/h2&gt;
&lt;h3 id=&#34;91-选择-gc-与-jdk-版本&#34;&gt;9.1 选择 GC 与 JDK 版本&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;场景&lt;/th&gt;
					&lt;th&gt;推荐配置&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;通用 Web 服务，JDK 17-22&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-XX:+UseG1GC -XX:MaxGCPauseMillis=200&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;通用 Web 服务，&lt;strong&gt;JDK 23+&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;默认已是分代 ZGC，一般无需改动&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;延迟敏感，JDK 21-22&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-XX:+UseZGC -XX:+ZGenerational&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;延迟敏感，JDK 23+&lt;/td&gt;
					&lt;td&gt;默认分代 ZGC，无需显式开启&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;批处理/大数据，吞吐优先&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-XX:+UseParallelGC&lt;/code&gt; 或 G1&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;容器环境&lt;/td&gt;
					&lt;td&gt;显式设置 &lt;code&gt;-Xmx&lt;/code&gt;、&lt;code&gt;-Xms&lt;/code&gt;、&lt;code&gt;-XX:MaxMetaspaceSize&lt;/code&gt;，并开启 &lt;code&gt;-XX:+UseContainerSupport&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;92-参数模板&#34;&gt;9.2 参数模板&lt;/h3&gt;
&lt;h4 id=&#34;jdk-17-22-通用-web-服务g1&#34;&gt;JDK 17-22 通用 Web 服务（G1）&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms4g -Xmx4g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseG1GC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxGCPauseMillis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar app.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;jdk-23-通用-web-服务默认分代-zgc&#34;&gt;JDK 23+ 通用 Web 服务（默认分代 ZGC）&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms4g -Xmx4g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar app.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;说明&lt;/strong&gt;：JDK 23 起默认就是分代 ZGC，不需要 &lt;code&gt;-XX:+UseZGC&lt;/code&gt;。如果仍想显式启用，可保留；如果想用回 G1，使用 &lt;code&gt;-XX:+UseG1GC&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;93-监控与诊断三板斧&#34;&gt;9.3 监控与诊断三板斧&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;工具&lt;/th&gt;
					&lt;th&gt;用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jstat -gcutil &amp;lt;pid&amp;gt; 1000&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;实时查看 GC 频率与内存占比&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jmap -dump:format=b,file=... &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;生成堆快照，MAT 分析内存泄漏&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jstack &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;打印线程栈，排查死锁与 CPU 热点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;GC 日志 / &lt;code&gt;-Xlog:gc*&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;分析 GC 原因与停顿时间&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;async-profiler&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;低开销 CPU/内存/锁分析&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;高性能建议 #3&lt;/strong&gt;：调优前一定先&lt;strong&gt;定目标&lt;/strong&gt;——是降低停顿？提高吞吐？还是减少内存？没有目标的调优就是瞎调。先用 GC 日志和监控找到瓶颈，再改参数，而不是反向操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十jvm-问题排查实战发现--定位--修复&#34;&gt;十、JVM 问题排查实战：发现 → 定位 → 修复&lt;/h2&gt;
&lt;p&gt;线上 JVM 问题通常表现为：服务卡顿、响应 P99 飙高、CPU 打满、内存持续增长、频繁 OOM。解决它们的核心思路是：&lt;strong&gt;先量化，再猜测&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;101-问题发现的四层入口&#34;&gt;10.1 问题发现的四层入口&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[业务层] --&amp;gt;|接口超时/P99 飙升| B[应用层]
    B --&amp;gt;|Full GC 频繁/GC 时间长| C[JVM 层]
    C --&amp;gt;|堆/栈/Metaspace 异常| D[系统层]
    D --&amp;gt;|容器 OOMKilled/CPU 限流| E[基础设施层]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;入口&lt;/th&gt;
					&lt;th&gt;典型现象&lt;/th&gt;
					&lt;th&gt;常见根因&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;业务监控&lt;/td&gt;
					&lt;td&gt;接口 RT 上涨、错误率增加&lt;/td&gt;
					&lt;td&gt;GC 停顿、线程阻塞、锁竞争&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;APM / Trace&lt;/td&gt;
					&lt;td&gt;某段链路偶发耗时几百 ms&lt;/td&gt;
					&lt;td&gt;Full GC、Metaspace 扩容、类加载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;容器监控&lt;/td&gt;
					&lt;td&gt;Pod 内存触顶被 OOMKilled&lt;/td&gt;
					&lt;td&gt;堆外内存泄漏、堆太大、Metaspace 无上限&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;GC 日志&lt;/td&gt;
					&lt;td&gt;GC 频率异常、停顿时间超标&lt;/td&gt;
					&lt;td&gt;收集器选择不当、堆大小不合理、大对象过多&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JVM 告警&lt;/td&gt;
					&lt;td&gt;Heap 使用率 &amp;gt; 85%、FGC 次数突增&lt;/td&gt;
					&lt;td&gt;内存泄漏、流量突增、缓存失控&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;102-排查工具箱&#34;&gt;10.2 排查工具箱&lt;/h3&gt;
&lt;h4 id=&#34;1021-命令行工具适合快速止血&#34;&gt;10.2.1 命令行工具（适合快速止血）&lt;/h4&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;命令&lt;/th&gt;
					&lt;th&gt;场景&lt;/th&gt;
					&lt;th&gt;输出重点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jps -lvm&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;找到 Java 进程 PID&lt;/td&gt;
					&lt;td&gt;主类名、启动参数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jstat -gcutil &amp;lt;pid&amp;gt; 1000&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;实时监控 GC 与内存&lt;/td&gt;
					&lt;td&gt;S0/S1/E/O/M 各区使用率、YGC/FGC 次数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jmap -heap &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看堆配置与概览&lt;/td&gt;
					&lt;td&gt;新生代/老年代大小、收集器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jmap -histo &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看对象直方图&lt;/td&gt;
					&lt;td&gt;哪个类实例数/占用最多&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jmap -dump:format=b,file=... &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;生成堆 dump&lt;/td&gt;
					&lt;td&gt;配合 MAT / JProfiler 分析&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jstack &amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;打印线程栈&lt;/td&gt;
					&lt;td&gt;死锁、CPU 热点线程、Blocked 线程&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jcmd &amp;lt;pid&amp;gt; VM.native_memory summary&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;查看 Native Memory Tracking&lt;/td&gt;
					&lt;td&gt;堆外内存、Metaspace、Symbol 区占用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;1022-可视化--专业工具&#34;&gt;10.2.2 可视化 / 专业工具&lt;/h4&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;工具&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;MAT (Eclipse Memory Analyzer)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;分析堆 dump&lt;/td&gt;
					&lt;td&gt;大对象、内存泄漏、支配树、Leak Suspects&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;JProfiler / YourKit&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;全链路 JVM 分析&lt;/td&gt;
					&lt;td&gt;CPU、内存、线程、GC 一体化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;async-profiler&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;生产环境火焰图&lt;/td&gt;
					&lt;td&gt;低开销，适合线上持续采样&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;GCViewer / GCEasy&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;解析 GC 日志&lt;/td&gt;
					&lt;td&gt;可视化 GC 事件、停顿分布&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Prometheus + Grafana&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;长期监控与告警&lt;/td&gt;
					&lt;td&gt;自定义 JVM 指标大盘&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Arthas&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;线上实时诊断&lt;/td&gt;
					&lt;td&gt;无需重启，支持 trace、watch、thread、heapdump&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;103-典型问题排查流程&#34;&gt;10.3 典型问题排查流程&lt;/h3&gt;
&lt;h4 id=&#34;场景-1cpu-飙升&#34;&gt;场景 1：CPU 飙升&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 1. 找到 CPU 占用最高的线程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ps -mp &amp;lt;pid&amp;gt; -o THREAD,tid,time | sort -rn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 2. 把 tid 转成十六进制&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;printf &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%x\n&amp;#34;&lt;/span&gt; &amp;lt;tid&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 3. 用 jstack 查看对应线程栈&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jstack &amp;lt;pid&amp;gt; | grep -A &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt; &amp;lt;hex_tid&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;常见根因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;死循环或复杂正则&lt;/li&gt;
&lt;li&gt;Young GC 过于频繁&lt;/li&gt;
&lt;li&gt;JIT 编译器 C2 线程忙碌（启动期正常）&lt;/li&gt;
&lt;li&gt;大量 &lt;code&gt;ThreadLocal&lt;/code&gt; 未清理&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;场景-2内存持续增长--oom&#34;&gt;场景 2：内存持续增长 / OOM&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 1. 确认哪个区域增长&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jstat -gcutil &amp;lt;pid&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 2. 开启 Native Memory Tracking&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jcmd &amp;lt;pid&amp;gt; VM.native_memory baseline
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jcmd &amp;lt;pid&amp;gt; VM.native_memory summary.diff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 3. 触发堆 dump（若还能响应）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jmap -dump:live,format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;b,file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/tmp/heap.hprof &amp;lt;pid&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;常见根因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;堆：缓存无淘汰、大对象、集合只增不减&lt;/li&gt;
&lt;li&gt;Metaspace：动态代理/CGLIB 类加载器泄漏&lt;/li&gt;
&lt;li&gt;堆外：Netty &lt;code&gt;ByteBuf&lt;/code&gt; 未释放、NIO DirectBuffer 堆积&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;场景-3接口偶发卡顿--p99-高&#34;&gt;场景 3：接口偶发卡顿 / P99 高&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;打开 GC 日志，看是否有 &lt;strong&gt;长停顿&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;jstack&lt;/code&gt; 抓取卡顿时刻线程栈，看是否有大量 &lt;code&gt;BLOCKED&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;async-profiler&lt;/code&gt; 采集 CPU 火焰图，定位热点方法。&lt;/li&gt;
&lt;li&gt;检查是否有 &lt;code&gt;System.gc()&lt;/code&gt;、Metaspace 扩容、类加载抖动。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;104-jvm-监控告警建议&#34;&gt;10.4 JVM 监控告警建议&lt;/h3&gt;
&lt;p&gt;建议把以下指标接入 Prometheus + Grafana，并设置合理阈值：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;指标&lt;/th&gt;
					&lt;th&gt;告警阈值建议&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jvm_memory_used_bytes / jvm_memory_max_bytes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&amp;gt; 85%&lt;/td&gt;
					&lt;td&gt;堆/非堆内存使用率&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jvm_gc_pause_seconds_sum&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;P99 &amp;gt; 200ms&lt;/td&gt;
					&lt;td&gt;GC 停顿时间&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jvm_gc_memory_allocated_bytes_total&lt;/code&gt; 增长率&lt;/td&gt;
					&lt;td&gt;突增 2 倍以上&lt;/td&gt;
					&lt;td&gt;对象分配速率异常&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jvm_classes_loaded_total&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;持续增长&lt;/td&gt;
					&lt;td&gt;类泄漏嫌疑&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;process_cpu_usage&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&amp;gt; 80% 持续 5min&lt;/td&gt;
					&lt;td&gt;CPU 压力&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;jvm_threads_live&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;突增或接近上限&lt;/td&gt;
					&lt;td&gt;线程泄漏&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;容器维度 &lt;code&gt;container_memory_working_set_bytes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&amp;gt; 90% limit&lt;/td&gt;
					&lt;td&gt;容器 OOMKilled 预警&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[应用埋点] --&amp;gt;|Micrometer| B[Prometheus]
    C[GC 日志] --&amp;gt;|Loki / Filebeat| D[日志平台]
    B --&amp;gt; E[Grafana 大盘]
    D --&amp;gt; E
    E --&amp;gt;|阈值触发| F[AlertManager]
    F --&amp;gt;|通知| G[钉钉/企业微信/Slack]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;十一不同场景的-jvm-配置策略&#34;&gt;十一、不同场景的 JVM 配置策略&lt;/h2&gt;
&lt;h3 id=&#34;111-配置前的通用原则&#34;&gt;11.1 配置前的通用原则&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先压测，再调优&lt;/strong&gt;：没有真实流量数据，调参都是拍脑袋。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Xms = Xmx&lt;/strong&gt;：避免运行时堆扩容带来的 GC 抖动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metaspace 必须设上限&lt;/strong&gt;：容器环境下尤其重要。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GC 日志一定要开&lt;/strong&gt;：&lt;code&gt;-Xlog:gc*&lt;/code&gt;（JDK 9+）成本很低，信息无价。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容器环境开启 Container Support&lt;/strong&gt;：&lt;code&gt;-XX:+UseContainerSupport&lt;/code&gt;（JDK 8u191+/10+ 默认开启）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要堆内存无限大&lt;/strong&gt;：超过 32GB 指针压缩失效，性价比骤降。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;112-场景一高并发-web--api-服务&#34;&gt;11.2 场景一：高并发 Web / API 服务&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：QPS 高、对象生命周期短、响应延迟敏感。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：降低 GC 停顿、控制 P99。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置（JDK 17-22，G1）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms4g -Xmx4g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseG1GC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxGCPauseMillis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:InitiatingHeapOccupancyPercent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;35&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar app.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;推荐配置（JDK 23+，默认分代 ZGC）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms4g -Xmx4g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar app.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新生代尽量大，让短生命周期对象在 Young GC 回收。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-XX:MaxGCPauseMillis&lt;/code&gt; 根据业务容忍度设置，100-200ms 常见。&lt;/li&gt;
&lt;li&gt;监控 &lt;code&gt;jvm_gc_pause_seconds_sum&lt;/code&gt;，若频繁触发 Mixed GC，考虑增大 &lt;code&gt;-XX:InitiatingHeapOccupancyPercent&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;113-场景二高流量--削峰填谷消息队列消费者&#34;&gt;11.3 场景二：高流量 / 削峰填谷（消息队列消费者）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：突发流量、消息处理批量化、吞吐优先、允许一定延迟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：提高吞吐，避免 OOM 和 Full GC。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms8g -Xmx8g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseG1GC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxGCPauseMillis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:G1HeapRegionSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;16m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:InitiatingHeapOccupancyPercent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;40&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar consumer.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;堆可以比 Web 服务稍大，因为要缓冲突发消息。&lt;/li&gt;
&lt;li&gt;批量消费时注意控制单次拉取消息数，避免一次创建过多对象。&lt;/li&gt;
&lt;li&gt;如果业务允许更高延迟，可考虑 &lt;code&gt;-XX:+UseParallelGC&lt;/code&gt; 追求极致吞吐。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;114-场景三低延迟金融--交易--实时推荐&#34;&gt;11.4 场景三：低延迟金融 / 交易 / 实时推荐&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：P99 要求 &amp;lt; 10ms，任何 GC 停顿都可能导致业务损失。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：亚毫秒级 GC 停顿。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置（JDK 21+）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms16g -Xmx16g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseZGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+ZGenerational &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar trading.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;JDK 23+ 可简化为&lt;/strong&gt;（默认就是分代 ZGC）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms16g -Xmx16g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar trading.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;堆 &amp;gt; 16GB 时 ZGC 优势更明显。&lt;/li&gt;
&lt;li&gt;避免大对象（Humongous），ZGC 对大对象分配路径不如小对象高效。&lt;/li&gt;
&lt;li&gt;监控 &lt;code&gt;jvm_gc_pause_seconds_max&lt;/code&gt;，正常应在 1ms 以内。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;115-场景四大数据--批处理--etl&#34;&gt;11.5 场景四：大数据 / 批处理 / ETL&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：长时间运行、对象存活久、吞吐优先、延迟不敏感。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：最大化吞吐，减少总 GC 时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms16g -Xmx16g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;1g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseParallelGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:ParallelGCThreads&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseLargePages &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;100m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar etl.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parallel GC 在纯吞吐场景下通常优于 G1。&lt;/li&gt;
&lt;li&gt;大堆 + 大页（LargePages）减少 TLB miss。&lt;/li&gt;
&lt;li&gt;如果批处理任务会频繁 Full GC，考虑分阶段释放引用或使用堆外缓存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;116-场景五容器--k8s-微服务&#34;&gt;11.6 场景五：容器 / K8s 微服务&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：资源受限、弹性伸缩、多实例共享节点。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：稳定、不 OOMKilled、启动快。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseContainerSupport &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:InitialRAMPercentage&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;70.0 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxRAMPercentage&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;70.0 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;128m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseG1GC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxGCPauseMillis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xlog:gc*:file&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/gc.log:time,uptime,level,tags:filecount&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;5,filesize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;50m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar service.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;-XX:MaxRAMPercentage&lt;/code&gt; 替代固定 &lt;code&gt;-Xmx&lt;/code&gt;，适配不同 Limit。&lt;/li&gt;
&lt;li&gt;容器 memory limit 要预留 10%-20% 给堆外内存、Metaspace、线程栈、JVM 自身。&lt;/li&gt;
&lt;li&gt;不要把 &lt;code&gt;-Xmx&lt;/code&gt; 设成容器 limit 的 100%，否则容易被 OOMKilled。&lt;/li&gt;
&lt;li&gt;JDK 23+ 在容器里可直接用默认分代 ZGC。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;117-场景六spring-boot--微服务网关&#34;&gt;11.7 场景六：Spring Boot / 微服务网关&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;特征&lt;/strong&gt;：启动时大量类加载、反射多、动态代理多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：启动快 + 运行稳。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐配置&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;java &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Xms2g -Xmx2g &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;256m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxMetaspaceSize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;512m &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+UseG1GC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:MaxGCPauseMillis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;150&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+AlwaysPreTouch &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+DisableExplicitGC &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:+HeapDumpOnOutOfMemoryError &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -XX:HeapDumpPath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;/var/log/app/heapdump.hprof &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -Dspring.backgroundpreinitializer.ignore&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;true &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -jar gateway.jar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;调优要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot 启动时 Metaspace 会快速上涨，初始值不要设太小。&lt;/li&gt;
&lt;li&gt;如果启动慢，可用 &lt;code&gt;-XX:+TieredCompilation&lt;/code&gt; 配合 &lt;code&gt;-XX:TieredStopAtLevel=1&lt;/code&gt; 加速启动（牺牲峰值性能）。&lt;/li&gt;
&lt;li&gt;网关避免在请求路径创建大对象，优先复用对象和缓冲区。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十二一句话总结&#34;&gt;十二、一句话总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存&lt;/strong&gt;：堆内分代、堆外 Direct、Metaspace 存类元数据；虚拟线程栈帧在堆中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GC&lt;/strong&gt;：Serial → Parallel → CMS → G1 → &lt;strong&gt;分代 ZGC&lt;/strong&gt;；JDK 21 分代 ZGC 正式发布，&lt;strong&gt;JDK 23 起成为默认 GC&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用&lt;/strong&gt;：强 &amp;gt; 软 &amp;gt; 弱 &amp;gt; 虚，缓存别滥用软引用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能&lt;/strong&gt;：JDK 17-22 优先 G1；JDK 23+ 默认分代 ZGC；延迟极致用 ZGC，吞吐优先用 Parallel。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;排查&lt;/strong&gt;：监控先行 → 日志定位 → 工具分析 → 压测验证 → 持续观察。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;场景&lt;/strong&gt;：Web 服务控延迟、消息队列保吞吐、金融交易用 ZGC、批处理用 Parallel、容器环境留余量。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;JVM 调优没有银弹，但有方法论：&lt;strong&gt;理解数据区 → 选对收集器 → 设定合理目标 → 用日志验证 → 持续监控&lt;/strong&gt;。把这套流程跑熟，面试八股和线上救火就都不慌了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《深入理解 Java 虚拟机》（第三版）周志明&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.oracle.com/en/java/javase/21/gctuning/&#34;&gt;OpenJDK HotSpot Virtual Machine Garbage Collection Tuning Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/333&#34;&gt;JEP 333: ZGC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/439&#34;&gt;JEP 439: Generational ZGC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/474&#34;&gt;JEP 474: ZGC Generational Mode by Default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/490&#34;&gt;JEP 490: Remove the Non-Generational ZGC Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/404&#34;&gt;JEP 404: Generational Shenandoah (Experimental)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/444&#34;&gt;JEP 444: Virtual Threads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/jeps/454&#34;&gt;JEP 454: Foreign Function &amp;amp; Memory API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://openjdk.org/projects/lilliput/&#34;&gt;Project Lilliput&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
