<?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 小蜜蜂</title>
        <link>https://xumf.net/tags/java/</link>
        <description>Recent content in Java on 小蜜蜂</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh</language>
        <lastBuildDate>Wed, 22 Jan 2025 13:16:53 +0800</lastBuildDate><atom:link href="https://xumf.net/tags/java/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Synchronized</title>
            <link>https://xumf.net/blog/synchronized/</link>
            <pubDate>Wed, 22 Jan 2025 13:16:53 +0800</pubDate>
            <guid>https://xumf.net/blog/synchronized/</guid>
            <description>&lt;p&gt;&lt;img alt=&#34;同步锁&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://xumf.net/images/Synchronized.png&#34;&gt;&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>JMM</title>
            <link>https://xumf.net/blog/javamemorymodel/</link>
            <pubDate>Fri, 09 Jul 2021 23:36:22 +0800</pubDate>
            <guid>https://xumf.net/blog/javamemorymodel/</guid>
            <description>&lt;p&gt;Java Memory Model（JMM）是 Java 虚拟机规范中定义的一组规则，用于屏蔽不同硬件和操作系统的内存访问差异，保证 Java 程序在多线程环境下的&lt;strong&gt;原子性、可见性、有序性&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;一为什么需要-jmm&#34;&gt;一、为什么需要 JMM？&#xA;&lt;/h2&gt;&lt;p&gt;多线程环境下，每个 CPU 核心都有自己的缓存（L1/L2/L3），主内存中的数据被多个核心同时读写时会出现三个问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;可见性&lt;/strong&gt;：一个线程修改变量，另一个线程可能看不到&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;原子性&lt;/strong&gt;：多个操作组合在一起可能被其他线程干扰&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;有序性&lt;/strong&gt;：编译器和处理器可能对指令重排序，导致执行顺序与代码顺序不一致&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;JMM 定义了主内存和工作内存的交互规范，以及 &lt;code&gt;happens-before&lt;/code&gt; 规则来约束重排序。&lt;/p&gt;&#xA;&lt;h2 id=&#34;二jmm-的内存操作&#34;&gt;二、JMM 的内存操作&#xA;&lt;/h2&gt;&lt;p&gt;JMM 定义了 8 种内存操作来完成变量在主内存和工作内存之间的同步：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;lock&lt;/strong&gt;：作用于主内存，将变量锁定为当前线程独享&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;unlock&lt;/strong&gt;：作用于主内存，释放锁定&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;read&lt;/strong&gt;：作用于主内存，将变量值从主内存传输到工作内存&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;load&lt;/strong&gt;：作用于工作内存，将 read 操作传输来的值放入工作内存副本&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;use&lt;/strong&gt;：作用于工作内存，将工作内存中的变量值传递给执行引擎&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;assign&lt;/strong&gt;：作用于工作内存，将执行引擎计算的值赋给工作内存副本&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;store&lt;/strong&gt;：作用于工作内存，将工作内存中的变量值传送到主内存&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;write&lt;/strong&gt;：作用于主内存，将 store 操作传来的值写入主内存&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;这 8 个操作必须满足以下规则：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;read 和 load、store 和 write 必须成对出现&lt;/li&gt;&#xA;&lt;li&gt;assign 不能单独发生（必须有 use 先获取值才能 assign）&lt;/li&gt;&#xA;&lt;li&gt;变量在工作内存中改变后必须同步回主内存&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;单线程下的基本流程&#34;&gt;单线程下的基本流程&#xA;&lt;/h3&gt;&lt;pre class=&#34;mermaid&#34; style=&#34;visibility:hidden&#34;&gt;sequenceDiagram&#xA;    participant 主内存&#xA;    participant 工作内存&#xA;    participant 线程&#xA;&#xA;    Note over 主内存,工作内存: 读取变量（主内存 → 工作内存）&#xA;    线程 -&gt;&gt; 主内存: read(var)&#xA;    主内存 --&gt;&gt; 工作内存: 传输变量值&#xA;    线程 -&gt;&gt; 工作内存: load(var)&#xA;&#xA;    Note over 工作内存: 修改变量（工作内存内操作）&#xA;    线程 -&gt;&gt; 工作内存: assign(var, newValue)&#xA;&#xA;    Note over 工作内存,主内存: 写回变量（工作内存 → 主内存）&#xA;    线程 -&gt;&gt; 工作内存: store(var)&#xA;    工作内存 --&gt;&gt; 主内存: 传输变量值&#xA;    线程 -&gt;&gt; 主内存: write(var)&lt;/pre&gt;&lt;h2 id=&#34;三三大特性详解&#34;&gt;三、三大特性详解&#xA;&lt;/h2&gt;&lt;h3 id=&#34;原子性问题&#34;&gt;原子性问题&#xA;&lt;/h3&gt;&lt;p&gt;多个独立操作组合在一起不是原子的。典型场景：&lt;code&gt;count++&lt;/code&gt; 实际上是 read→load→use→assign→store→write 六个步骤，线程 A 和线程 B 可能同时执行到中间的 use 步骤。&lt;/p&gt;&#xA;&lt;pre class=&#34;mermaid&#34; style=&#34;visibility:hidden&#34;&gt;sequenceDiagram&#xA;    participant 主内存&#xA;    participant 工作内存A&#xA;    participant 线程A&#xA;    participant 工作内存B&#xA;    participant 线程B&#xA;&#xA;    rect rgba(255, 0, 0, 0.1)&#xA;        Note left of 线程A: 线程A操作&#xA;        线程A -&gt;&gt; 主内存: read(var)&#xA;        主内存 --&gt;&gt; 工作内存A: var=0&#xA;        线程A -&gt;&gt; 工作内存A: load(var)&#xA;        线程A -&gt;&gt; 工作内存A: assign(var, 1)&#xA;        线程A -&gt;&gt; 工作内存A: store(var)&#xA;        工作内存A --&gt;&gt; 主内存: 准备写入var=1&#xA;    end&#xA;&#xA;    rect rgba(0, 255, 0, 0.1)&#xA;        Note right of 线程B: 线程B操作（插入A的store→write之间）&#xA;        线程B -&gt;&gt; 主内存: read(var)（主内存仍为0）&#xA;        主内存 --&gt;&gt; 工作内存B: var=0&#xA;        线程B -&gt;&gt; 工作内存B: load(var)&#xA;        线程B -&gt;&gt; 工作内存B: assign(var, 1)&#xA;        线程B -&gt;&gt; 工作内存B: store(var)&#xA;        工作内存B --&gt;&gt; 主内存: 写入var=1&#xA;        主内存 --&gt;&gt; 线程B: write(var)完成&#xA;    end&#xA;&#xA;    rect rgba(255, 0, 0, 0.1)&#xA;        Note left of 线程A: 线程A完成写入（被覆盖）&#xA;        主内存 --&gt;&gt; 线程A: write(var)（最终var=1，预期为2）&#xA;    end&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;解决方式&lt;/strong&gt;：使用 &lt;code&gt;synchronized&lt;/code&gt; 或 &lt;code&gt;Lock&lt;/code&gt; 保证原子性，或使用 &lt;code&gt;AtomicInteger&lt;/code&gt; 等 CAS 操作。&lt;/p&gt;&#xA;&lt;h3 id=&#34;可见性问题&#34;&gt;可见性问题&#xA;&lt;/h3&gt;&lt;p&gt;线程 B 修改了变量，线程 A 可能仍然使用自己工作内存中的旧副本。原因：A 没有及时从主内存重新 read→load。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;volatile 解决可见性&lt;/strong&gt;：对 volatile 变量的写操作会立即执行 store→write 刷新到主内存，读操作会强制从主内存 read→load，而不是使用工作内存缓存的旧值。&lt;/p&gt;&#xA;&lt;pre class=&#34;mermaid&#34; style=&#34;visibility:hidden&#34;&gt;sequenceDiagram&#xA;    participant 主内存&#xA;    participant 工作内存A&#xA;    participant 线程A&#xA;    participant 工作内存B&#xA;    participant 线程B&#xA;&#xA;    rect rgba(0, 255, 0, 0.1)&#xA;        Note left of 线程A: 线程A操作（volatile变量）&#xA;        线程A -&gt;&gt; 主内存: read(var)&#xA;        主内存 --&gt;&gt; 工作内存A: var=0&#xA;        线程A -&gt;&gt; 工作内存A: load(var)&#xA;        线程A -&gt;&gt; 工作内存A: assign(var, 1)&#xA;        线程A -&gt;&gt; 工作内存A: store(var)&#xA;        工作内存A --&gt;&gt; 主内存: 立即写入var=1&#xA;        主内存 --&gt;&gt; 线程A: write(var)完成&#xA;    end&#xA;&#xA;    rect rgba(0, 0, 255, 0.1)&#xA;        Note right of 线程B: 线程B操作（强制读取最新值）&#xA;        线程B -&gt;&gt; 主内存: read(var)（必须读取var=1）&#xA;        主内存 --&gt;&gt; 工作内存B: var=1&#xA;        线程B -&gt;&gt; 工作内存B: load(var)&#xA;    end&lt;/pre&gt;&lt;h3 id=&#34;有序性问题&#34;&gt;有序性问题&#xA;&lt;/h3&gt;&lt;p&gt;编译器和处理器为了优化性能会对指令重排序，但在多线程下可能导致意外的执行顺序。例如经典的双重检查锁单例：&lt;/p&gt;&#xA;&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 的版本&lt;/span&gt;&#xA;&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; Singleton instance;&#xA;&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;() {&#xA;&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 style=&#34;color:#75715e&#34;&gt;// 第一次检查&lt;/span&gt;&#xA;&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; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;) {&#xA;&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 style=&#34;color:#75715e&#34;&gt;// 第二次检查&lt;/span&gt;&#xA;&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&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;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;instance = new Singleton()&lt;/code&gt; 实际为三步操作：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;分配内存空间&lt;/li&gt;&#xA;&lt;li&gt;初始化对象&lt;/li&gt;&#xA;&lt;li&gt;将引用指向内存地址&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;步骤 2 和 3 可能被重排序，导致另一个线程获得一个未初始化完成的对象。&lt;strong&gt;volatile 通过禁止指令重排序解决了这个问题&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;四happens-before-规则&#34;&gt;四、happens-before 规则&#xA;&lt;/h2&gt;&lt;p&gt;happens-before 是 JMM 的核心规则，用于判断两个操作之间是否存在数据竞争：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;程序顺序规则&lt;/strong&gt;：一个线程中的每个操作 happens-before 该线程的后续操作&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;volatile 变量规则&lt;/strong&gt;：对 volatile 变量的写 happens-before 后续对该变量的读&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;锁规则&lt;/strong&gt;：解锁 happens-before 后续对同一把锁的加锁&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;传递性&lt;/strong&gt;：A happens-before B 且 B happens-before C → A happens-before C&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;线程启动规则&lt;/strong&gt;：Thread.start() happens-before 被启动线程中的任何操作&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;线程 join 规则&lt;/strong&gt;：线程中所有操作 happens-before 其他线程对该线程的 Thread.join() 成功返回&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;五volatile-vs-synchronized-对比&#34;&gt;五、volatile vs synchronized 对比&#xA;&lt;/h2&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;特性&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;volatile&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;synchronized&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;可见性&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;即时可见&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;通过解锁刷新到主内存实现&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;原子性&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;不保证&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;保证&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;有序性&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;禁止指令重排序&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;通过加解锁保证有序&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;性能&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;无锁，开销小&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;有锁，涉及线程挂起和唤醒&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;使用场景&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;状态标记、单次写入&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;复合操作、需要原子性的场景&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;常见误区&#34;&gt;常见误区&#xA;&lt;/h2&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;volatile 不能替代 synchronized&lt;/strong&gt;：volatile 仅保证可见性和有序性，不保证原子性（如 &lt;code&gt;count++&lt;/code&gt; 仍然有并发问题）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;final 也有可见性保证&lt;/strong&gt;：JMM 保证构造函数结束后，final 字段对其他线程可见（除非发生 this 引用逸出）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;happens-before 规则不是时间顺序&lt;/strong&gt;：它不要求物理时间上 A 先于 B 执行，只要求在语义上 A 的结果对 B 可见&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;</description>
        </item><item>
            <title>JVM 问题排查</title>
            <link>https://xumf.net/blog/jvmissuesolution/</link>
            <pubDate>Sun, 12 Jul 2020 13:16:53 +0800</pubDate>
            <guid>https://xumf.net/blog/jvmissuesolution/</guid>
            <description>&lt;h3 id=&#34;可正常运行系统&#34;&gt;可正常运行系统：&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;通过&lt;code&gt;jmap&lt;/code&gt;来查看JVM中各个区域的使用情况&lt;/li&gt;&#xA;&lt;li&gt;通过&lt;code&gt;jstack&lt;/code&gt;来查看线程的运行情况，比如哪些线程阻塞、是否出现死锁&lt;/li&gt;&#xA;&lt;li&gt;通过j&lt;code&gt;stat&lt;/code&gt;来查看垃圾回收情况，特别是fullgc，如果发现fullgc比较频繁，那么就得进行调优&lt;/li&gt;&#xA;&lt;li&gt;通过各个命令的结果或者&lt;code&gt;jvisualvm&lt;/code&gt;等工具来进行分析&lt;/li&gt;&#xA;&lt;li&gt;首先：初步猜测频发发送fullgc的原因，如果频繁发生fullgc，但是又一直没有出现内存溢出，那么表示fullgc实际上回收了很多对象，所以这些对象最好能在younggc过程中直接回收掉，避免这些对象进入到老年代，对于这种情况，就要考虑这些存活时间不长的对象是不是比较大，导致年轻代放不下，直接进入到老年代，尝试加大年轻代的大小，如果改完之后，fullgc减少，则证明修改有效&lt;/li&gt;&#xA;&lt;li&gt;同时，还可以找到占用CPU最多的线程，定位到具体的方法，优化这个方法的执行，看是否能避免某些对象的创建，从而节省内存&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;h3 id=&#34;运行中发生oom的系统&#34;&gt;运行中发生OOM的系统：&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;一般生产系统中都会设置当前系统发生了OOM时，生成当时的dump文件（&lt;code&gt;-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base&lt;/code&gt;）&lt;/li&gt;&#xA;&lt;li&gt;我们可以利用&lt;code&gt;jvisualvm&lt;/code&gt;等工具来分析dump文件&lt;/li&gt;&#xA;&lt;li&gt;根据dump文件找到异常的实例对象和异常的线程（占用CPU高），定位到具体代码&lt;/li&gt;&#xA;&lt;li&gt;然后再进行详细的分析和调试&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;</description>
        </item><item>
            <title>垃圾回收器</title>
            <link>https://xumf.net/blog/garbagecollector/</link>
            <pubDate>Wed, 18 Jul 2018 15:43:54 +0800</pubDate>
            <guid>https://xumf.net/blog/garbagecollector/</guid>
            <description>&lt;h2 id=&#34;一jvm-垃圾回收器概述&#34;&gt;一、JVM 垃圾回收器概述&#xA;&lt;/h2&gt;&lt;p&gt;常见的垃圾回收器：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Serial GC&lt;/li&gt;&#xA;&lt;li&gt;Parallel GC（Throughput GC）&lt;/li&gt;&#xA;&lt;li&gt;CMS GC（Concurrent Mark Sweep）&lt;/li&gt;&#xA;&lt;li&gt;G1 GC（Garbage First）&lt;/li&gt;&#xA;&lt;li&gt;ZGC（Z Garbage Collector）&lt;/li&gt;&#xA;&lt;li&gt;Shenandoah GC&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;二垃圾回收器的特点与回收流程&#34;&gt;二、垃圾回收器的特点与回收流程&#xA;&lt;/h2&gt;&lt;h3 id=&#34;衡量-gc-优劣的三个核心指标&#34;&gt;衡量 GC 优劣的三个核心指标&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;吞吐量（Throughput）&lt;/strong&gt;：用户代码运行时间 /（用户代码运行时间 + GC 时间）。越高越好。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;暂停时间（Pause Time / Latency）&lt;/strong&gt;：GC 时应用线程暂停的时间。越短越好。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;内存占用（Footprint）&lt;/strong&gt;：GC 额外消耗的内存（如 G1 的 RSet、ZGC 的染色指针）。越少越好。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这三者不可能同时优化——任何 GC 都在三者间做取舍。&lt;/p&gt;&#xA;&lt;h3 id=&#34;1-serial-gc&#34;&gt;1. Serial GC&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：单线程，回收时全部 STW&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;为什么存在&lt;/strong&gt;：单核 CPU 或小型应用场景，多线程反而有额外开销&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;新生代：复制算法（Copying）&lt;/li&gt;&#xA;&lt;li&gt;老年代：标记-整理算法（Mark-Compact）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：客户端应用、小型服务端、单核环境&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;-XX:+UseSerialGC&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时选它&lt;/strong&gt;：堆小于 100MB、单核 CPU。其他情况不推荐。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;2-parallel-gcthroughput-gc&#34;&gt;2. Parallel GC（Throughput GC）&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：多线程，目标是最大化吞吐量，但 STW 时间较长&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;设计目标&lt;/strong&gt;：&amp;ldquo;只要暂停时间在可接受范围内，吞吐量越高越好&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;新生代：复制算法（多线程）&lt;/li&gt;&#xA;&lt;li&gt;老年代：标记-整理算法（多线程）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：多核 CPU、对延迟要求不高的批处理/数据分析任务&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;重要参数&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseParallelGC&lt;/code&gt;：启用&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:ParallelGCThreads&lt;/code&gt;：并行 GC 线程数（默认 = CPU 核心数）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:MaxGCPauseMillis&lt;/code&gt;：期望最大暂停时间（不是硬保证，默认无）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:GCTimeRatio&lt;/code&gt;：吞吐量目标（默认 99，即允许 1% 时间花在 GC 上）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;3-cms-gcconcurrent-mark-sweep&#34;&gt;3. CMS GC（Concurrent Mark Sweep）&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：并发收集老年代，尽量减少 STW 时间&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;为什么选 CMS&lt;/strong&gt;：对于 Web 服务等延迟敏感应用，每次 Full GC 暂停几秒钟不可接受。CMS 让大部分 GC 工作与应用线程并发。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;初始标记（STW）&lt;/strong&gt;：标记 GC Roots 直接关联的对象——&lt;strong&gt;快速&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发标记&lt;/strong&gt;：从 GC Roots 开始遍历所有引用——&lt;strong&gt;与应用并发&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;重新标记（STW）&lt;/strong&gt;：修正并发标记期间引用变化导致的漏标——&lt;strong&gt;较快&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发清除&lt;/strong&gt;：清除不可达对象——&lt;strong&gt;与应用并发&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;核心问题&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;内存碎片&lt;/strong&gt;：使用标记-清除算法，老年代越来越碎片化 → 最终触发 Full GC 且无法避免长时间 STW&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;CPU 敏感&lt;/strong&gt;：并发阶段抢占 CPU，应用吞吐量下降&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;浮动垃圾&lt;/strong&gt;：并发标记期间新产生的垃圾本次无法回收&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Promotion Failed&lt;/strong&gt;：新生代对象晋升老年代时无连续空间 → Full GC&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;重要参数&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseConcMarkSweepGC&lt;/code&gt;：启用（JDK 9 后已废弃，JDK 14 移除）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:CMSInitiatingOccupancyFraction&lt;/code&gt;：触发 CMS 的老年代使用率阈值（默认 68%）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseCMSCompactAtFullCollection&lt;/code&gt;：Full GC 时整理内存&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:CMSFullGCsBeforeCompaction&lt;/code&gt;：多少次 Full GC 后进行一次整理&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;为什么被废弃&lt;/strong&gt;：G1 在延迟和吞吐量之间的平衡表现更好，且 CMS 无法解决碎片化和浮动垃圾问题。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;4-g1-gcgarbage-first&#34;&gt;4. G1 GC（Garbage First）&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：面向大堆、多核的服务端 GC。将堆划分为多个 Region，优先回收垃圾最多的 Region（&amp;ldquo;Garbage First&amp;rdquo; 名字的由来）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;设计思路&lt;/strong&gt;：不要求一次回收整个堆，而是分批次回收每个 Region，通过可预测的 pause time 模型控制停顿&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;初始标记(STW) → 并发标记 → 最终标记(STW) → 筛选回收(STW)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;初始标记&lt;/strong&gt;：标记 GC Roots + 修改 TAMS（Next Top at Mark Start）指针&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;并发标记&lt;/strong&gt;：从 GC Roots 遍历所有对象，记录引用变更到 SATB（Snapshot At The Beginning）队列&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;最终标记&lt;/strong&gt;：处理 SATB 队列中的漏标对象&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;筛选回收&lt;/strong&gt;：计算每个 Region 的回收价值（回收能释放多少空间 × 回收耗时），优先回收&amp;quot;垃圾最多、回收最快&amp;quot;的 Region&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;关键内部机制&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;机制&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;作用&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;Region&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;堆划分为 1MB~32MB 的 Region，每个 Region 可扮演 Eden/Survivor/Old/Humongous&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;RSet（Remembered Set）&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;记录其他 Region 对本 Region 的引用——避免全堆扫描&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;SATB（Snapshot At The Beginning）&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;并发标记开始时拍堆快照，所有在标记期间变更的引用都记录到 SATB 队列&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;写屏障&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;对象引用变更时触发，更新 RSet + 记录 SATB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：堆大小 4GB+，多核 CPU，延迟要求 100-300ms&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;重要参数&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseG1GC&lt;/code&gt;：启用（JDK 9+ 默认）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:MaxGCPauseMillis&lt;/code&gt;：期望暂停时间目标（默认 200ms，G1 会努力但无法硬保证）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:G1HeapRegionSize&lt;/code&gt;：Region 大小（默认堆/2048，1~32MB）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:InitiatingHeapOccupancyPercent&lt;/code&gt;：触发并发标记的堆使用率（默认 45%）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:G1NewSizePercent&lt;/code&gt;：新生代最小占比（默认 5%）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:G1MaxNewSizePercent&lt;/code&gt;：新生代最大占比（默认 60%）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;何时选 G1&lt;/strong&gt;：JDK 11+、堆 4GB+、延迟要求几百毫秒，G1 是默认选择。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;5-zgcz-garbage-collector&#34;&gt;5. ZGC（Z Garbage Collector）&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：极低延迟（STW &amp;lt; 10ms），与堆大小基本无关（即使是 TB 级堆）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;为什么能这么低延迟&lt;/strong&gt;：几乎所有阶段都并发，只在极少数点 STW（且 STW 阶段不随堆大小增长）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;关键技术&lt;/strong&gt;：&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;技术&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;作用&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;染色指针（Colored Pointers）&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;在 64 位指针的高位（42 位之后）存储 GC 状态信息（Finalizable/Remapped/Mark1/Mark0），无需额外内存&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;读屏障（Load Barrier）&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;每次从堆读取引用时检查指针颜色，如果对象被移动，读屏障在返回前修正引用&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;并发迁移（Relocation）&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;存活对象在应用运行时被迁移到新区域，旧区域清空后回收&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发标记（Concurrent Mark）&lt;/strong&gt;：遍历对象图，标记存活对象。与 G1 不同，ZGC 的标记完全并发（初始标记的 STW 极短）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发迁移预备（Concurrent Prepare for Relocation）&lt;/strong&gt;：确定需要清理的 Region。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发迁移（Concurrent Relocate）&lt;/strong&gt;：将存活对象复制到新 Region，更新引用。读屏障确保应用线程始终访问到正确地址。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;并发重映射（Concurrent Remap）&lt;/strong&gt;：修正所有指向旧位置的引用。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：TB 级堆、延迟要求 &amp;lt; 10ms 的金融交易/实时系统&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;重要参数&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseZGC&lt;/code&gt;：启用（JDK 11 实验性，JDK 15 正式，JDK 18+ 默认）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:ZAllocationSpikeTolerance&lt;/code&gt;：分配尖峰容忍度（默认 2.0）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:ZCollectionInterval&lt;/code&gt;：最大回收间隔（秒）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;：ZGC 的吞吐量略低于 G1（因为读屏障开销），内存占用也更高。选 ZGC 时确认你能接受这些 Trade-off。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;6-shenandoah-gc&#34;&gt;6. Shenandoah GC&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：与 ZGC 类似，目标是极低延迟。与 ZGC 的区别在于实现方式：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ZGC 使用染色指针（需要 64 位系统 + 特定 CPU 特性）&lt;/li&gt;&#xA;&lt;li&gt;Shenandoah 使用 Brooks 指针（对象头中加一个转发指针），不依赖硬件&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;回收流程&lt;/strong&gt;：并发标记 → 并发转移 → 并发更新引用（比 ZGC 多一个并发更新引用阶段）&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：与 ZGC 类似，但不要求特定硬件&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;重要参数&lt;/strong&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:+UseShenandoahGC&lt;/code&gt;：启用（JDK 12+，OpenJDK 发行版不默认包含——需要 Build 时开启）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-XX:ShenandoahGCHeuristics&lt;/code&gt;：启发式策略（adaptive/static/compact/aggressive）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;三gc-选择决策树&#34;&gt;三、GC 选择决策树&#xA;&lt;/h2&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;应用需要什么？&#xA;├─ 批处理/数据分析 → Parallel（要吞吐量）&#xA;├─ Web 服务/微服务&#xA;│  ├─ 堆 &amp;lt; 4GB → G1 或 Parallel&#xA;│  ├─ 堆 4~16GB → G1（JDK 11+ 默认）&#xA;│  ├─ 堆 &amp;gt; 16GB + 延迟敏感 → G1（延迟要求 &amp;lt; 100ms？）&#xA;│  │  └─ 是 → ZGC（JDK 17+）&#xA;│  └─ 堆 &amp;gt; 100GB + 极致低延迟 → ZGC&#xA;└─ 客户端/小型应用 → Serial&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;四gc-优化实践&#34;&gt;四、GC 优化实践&#xA;&lt;/h2&gt;&lt;h3 id=&#34;基础三步&#34;&gt;基础三步&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;确定目标&lt;/strong&gt;：是提高吞吐量还是降低延迟？&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;收集数据&lt;/strong&gt;：PrintGCDetails + GCViewer/GCEasy 分析 GC 日志&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;调整参数&lt;/strong&gt;：一次只改一个参数，观察效果&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;常用-jvm-参数组合&#34;&gt;常用 JVM 参数组合&#xA;&lt;/h3&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;# 打印 GC 日志（JDK 9+）&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-Xlog:gc*&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-Xlog:gc:gc.log&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&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;# 打印 GC 日志（JDK 8）&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&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;# G1 生产环境常用参数&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-XX:+UseG1GC -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; -XX:+PrintGCDetails&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&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;# 大堆 ZGC 常用参数&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-XX:+UseZGC -Xms64G -Xmx64G -XX:ZAllocationSpikeTolerance&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;内存泄漏排查思路&#34;&gt;内存泄漏排查思路&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;Heap Dump + MAT（Memory Analyzer Tool）分析大对象&lt;/li&gt;&#xA;&lt;li&gt;GC 日志中 &lt;code&gt;Full GC&lt;/code&gt; 频率异常 → 确认是否为晋升失败或 MetaSpace 泄漏&lt;/li&gt;&#xA;&lt;li&gt;Native Memory Tracking（NMT）检查 off-heap 内存&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;五跨代引用与-rset-详解&#34;&gt;五、跨代引用与 RSet 详解&#xA;&lt;/h2&gt;&lt;h3 id=&#34;g1-的-rsetremembered-set&#34;&gt;G1 的 RSet（Remembered Set）&#xA;&lt;/h3&gt;&lt;p&gt;每个 Region 维护一个 RSet，记录其他 Region 对它的引用。为什么需要 RSet？Young GC 时只扫描新生代 Region，但老年代可能有对象引用新生代对象——有了 RSet 就不必全堆扫描。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;RSet 的结构&lt;/strong&gt;：哈希表，key 是来源 Region 地址，value 是来源 Region 中的卡页索引（Card Index）。卡页默认 512 字节。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;写屏障维护 RSet&lt;/strong&gt;：&lt;/p&gt;&#xA;&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;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;writeBarrier&lt;/span&gt;(Field field, Object newObj) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Object oldObj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; field.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;();&#xA;&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; (oldObj &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 style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; isCrossRegionRef(oldObj, newObj)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        addToRSet(oldObj.&lt;span style=&#34;color:#a6e22e&#34;&gt;region&lt;/span&gt;(), newObj.&lt;span style=&#34;color:#a6e22e&#34;&gt;region&lt;/span&gt;(), getCardIndex(oldObj));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    field.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(newObj);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;g1-vs-cms-跨代引用处理对比&#34;&gt;G1 vs CMS 跨代引用处理对比&#xA;&lt;/h3&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;机制&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;G1&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;CMS&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;跨代引用处理&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;每个 Region 维护 RSet&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;全局卡表（Card Table）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;精度&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;精确到 Region + 卡页&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;粗粒度（卡表页）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;内存开销&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;较高（每个 Region 一个 RSet）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;较低（全局卡表）&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;扫描效率&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;仅扫描相关 Region&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;需扫描整个老年代脏卡页&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;</description>
        </item></channel>
</rss>
