<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>虚拟线程 on 文艺技术笔记</title>
        <link>https://wenyiblog.top/tags/%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8B/</link>
        <description>Recent content in 虚拟线程 on 文艺技术笔记</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <copyright>文艺技术笔记 | 软件工程师文艺</copyright>
        <lastBuildDate>Fri, 10 Jul 2026 12:00:00 +0800</lastBuildDate><atom:link href="https://wenyiblog.top/tags/%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8B/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Java 21 虚拟线程生产踩坑：从压测数据到线上 GC 调优的完整记录</title>
        <link>https://wenyiblog.top/2026/07/java21-virtual-threads-gc-tuning/</link>
        <pubDate>Fri, 10 Jul 2026 12:00:00 +0800</pubDate>
        
        <guid>https://wenyiblog.top/2026/07/java21-virtual-threads-gc-tuning/</guid>
        <description>&lt;p&gt;有句话说，新技术落地从来不是一帆风顺的。Java 21 正式引入的虚拟线程（Virtual Threads）在发布时被寄予厚望——用同步代码风格实现高并发，彻底摆脱回调地狱和响应式编程的心智负担。然而在真实的生产环境中，从压测阶段到线上运行，我们遇到了不少&amp;quot;文档里没有写&amp;quot;的问题。本文将完整记录这段踩坑历程，包括压测数据对比、线上故障复现、以及 GC 调优的全过程，希望能为正在评估或已经上线虚拟线程的团队提供一份实战参考。&lt;/p&gt;
&lt;h2 id=&#34;背景为什么要上虚拟线程&#34;&gt;&lt;a href=&#34;#%e8%83%8c%e6%99%af%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e4%b8%8a%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;背景：为什么要上虚拟线程
&lt;/h2&gt;&lt;h3 id=&#34;业务痛点&#34;&gt;&lt;a href=&#34;#%e4%b8%9a%e5%8a%a1%e7%97%9b%e7%82%b9&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;业务痛点
&lt;/h3&gt;&lt;p&gt;我们的核心服务是一个典型的 I/O 密集型网关层，负责聚合下游十几个微服务的调用结果。在高峰期，单实例需要承载约 8000 QPS 的请求量，每个请求平均会发起 3-5 次下游 RPC 调用，每次调用的平均延迟在 20-80ms 之间。&lt;/p&gt;
&lt;p&gt;在传统的平台线程模型下，Tomcat 默认线程池大小为 200。按照经典公式：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;线程数 = QPS × 平均响应时间（秒）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;理论所需线程数 = 8000 × 0.15（假设平均 150ms 完成所有下游调用）= 1200 个线程。这意味着我们需要将线程池扩容到 1200 才能满足吞吐需求，而 1200 个平台线程带来的上下文切换开销和内存占用已经非常可观。&lt;/p&gt;
&lt;h3 id=&#34;虚拟线程的理论优势&#34;&gt;&lt;a href=&#34;#%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e7%9a%84%e7%90%86%e8%ae%ba%e4%bc%98%e5%8a%bf&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;虚拟线程的理论优势
&lt;/h3&gt;&lt;p&gt;虚拟线程的核心价值在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;轻量级&lt;/strong&gt;：一个虚拟线程的初始内存开销约为 1KB，而平台线程通常需要 1MB 的栈空间&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;code&gt;synchronized&lt;/code&gt;、&lt;code&gt;ReentrantLock&lt;/code&gt;、&lt;code&gt;ThreadLocal&lt;/code&gt; 等机制兼容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些特性看起来完美契合我们的场景。于是团队决定在一个非核心的聚合服务上先行试点。&lt;/p&gt;
&lt;h2 id=&#34;压测阶段数据说话&#34;&gt;&lt;a href=&#34;#%e5%8e%8b%e6%b5%8b%e9%98%b6%e6%ae%b5%e6%95%b0%e6%8d%ae%e8%af%b4%e8%af%9d&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;压测阶段：数据说话
&lt;/h2&gt;&lt;h3 id=&#34;测试环境配置&#34;&gt;&lt;a href=&#34;#%e6%b5%8b%e8%af%95%e7%8e%af%e5%a2%83%e9%85%8d%e7%bd%ae&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;测试环境配置
&lt;/h3&gt;&lt;p&gt;为了确保数据的可对比性，我们在隔离环境中进行了严格的 A/B 测试：&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;8 核 32G，CentOS 7.9&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JDK&lt;/td&gt;
					&lt;td&gt;OpenJDK 21.0.2&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;GC&lt;/td&gt;
					&lt;td&gt;G1GC（默认参数）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;应用框架&lt;/td&gt;
					&lt;td&gt;Spring Boot 3.2 + 内嵌 Tomcat&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;压测工具&lt;/td&gt;
					&lt;td&gt;JMeter 5.6，10 台施压机&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;下游模拟&lt;/td&gt;
					&lt;td&gt;WireMock，模拟 50ms 固定延迟&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;对照组设置&#34;&gt;&lt;a href=&#34;#%e5%af%b9%e7%85%a7%e7%bb%84%e8%ae%be%e7%bd%ae&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;对照组设置
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;对照组 A&lt;/strong&gt;：传统平台线程，Tomcat &lt;code&gt;max-threads=400&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对照组 B&lt;/strong&gt;：虚拟线程，通过 &lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt; 开启&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对照组 C&lt;/strong&gt;：虚拟线程 + &lt;code&gt;synchronized&lt;/code&gt; 关键路径（模拟遗留代码）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;吞吐量对比&#34;&gt;&lt;a href=&#34;#%e5%90%9e%e5%90%90%e9%87%8f%e5%af%b9%e6%af%94&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;吞吐量对比
&lt;/h3&gt;&lt;p&gt;逐步加压至单实例 12000 QPS，记录各组的吞吐量和 P99 延迟：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;目标 QPS&lt;/th&gt;
					&lt;th&gt;平台线程 实际QPS&lt;/th&gt;
					&lt;th&gt;平台线程 P99(ms)&lt;/th&gt;
					&lt;th&gt;虚拟线程 实际QPS&lt;/th&gt;
					&lt;th&gt;虚拟线程 P99(ms)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;2000&lt;/td&gt;
					&lt;td&gt;2000&lt;/td&gt;
					&lt;td&gt;85&lt;/td&gt;
					&lt;td&gt;2000&lt;/td&gt;
					&lt;td&gt;72&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4000&lt;/td&gt;
					&lt;td&gt;3980&lt;/td&gt;
					&lt;td&gt;142&lt;/td&gt;
					&lt;td&gt;4000&lt;/td&gt;
					&lt;td&gt;95&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;6000&lt;/td&gt;
					&lt;td&gt;5650&lt;/td&gt;
					&lt;td&gt;310&lt;/td&gt;
					&lt;td&gt;6000&lt;/td&gt;
					&lt;td&gt;118&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;8000&lt;/td&gt;
					&lt;td&gt;6200&lt;/td&gt;
					&lt;td&gt;890&lt;/td&gt;
					&lt;td&gt;8000&lt;/td&gt;
					&lt;td&gt;145&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10000&lt;/td&gt;
					&lt;td&gt;6500&lt;/td&gt;
					&lt;td&gt;2100&lt;/td&gt;
					&lt;td&gt;9800&lt;/td&gt;
					&lt;td&gt;180&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;12000&lt;/td&gt;
					&lt;td&gt;OOM&lt;/td&gt;
					&lt;td&gt;—&lt;/td&gt;
					&lt;td&gt;10500&lt;/td&gt;
					&lt;td&gt;350&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;数据结论非常清晰：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平台线程在 6000 QPS 以上开始明显退化&lt;/strong&gt;，P99 延迟飙升，最终在 12000 QPS 时因无法创建更多线程而 OOM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;虚拟线程的吞吐曲线几乎是线性的&lt;/strong&gt;，直到 10000 QPS 以上才出现轻微退化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P99 延迟方面，虚拟线程的优势在高并发下尤为明显&lt;/strong&gt;——平台线程在 8000 QPS 时 P99 已近 1 秒，而虚拟线程仍保持在 150ms 以内&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;内存占用对比&#34;&gt;&lt;a href=&#34;#%e5%86%85%e5%ad%98%e5%8d%a0%e7%94%a8%e5%af%b9%e6%af%94&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;内存占用对比
&lt;/h3&gt;&lt;p&gt;在 8000 QPS 稳定运行 10 分钟后采样：&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;/td&gt;
					&lt;td&gt;400&lt;/td&gt;
					&lt;td&gt;~8000&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;线程栈内存总量&lt;/td&gt;
					&lt;td&gt;~400MB&lt;/td&gt;
					&lt;td&gt;~8MB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JVM 堆内存使用&lt;/td&gt;
					&lt;td&gt;2.1GB&lt;/td&gt;
					&lt;td&gt;2.8GB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RSS（进程总内存）&lt;/td&gt;
					&lt;td&gt;3.8GB&lt;/td&gt;
					&lt;td&gt;4.2GB&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这里出现了一个值得注意的现象：&lt;strong&gt;虚拟线程的堆内存使用比平台线程高出约 700MB&lt;/strong&gt;。这个差异在当时没有引起足够重视，后来成为线上 GC 问题的导火索。&lt;/p&gt;
&lt;h3 id=&#34;对照组-c-的意外发现&#34;&gt;&lt;a href=&#34;#%e5%af%b9%e7%85%a7%e7%bb%84-c-%e7%9a%84%e6%84%8f%e5%a4%96%e5%8f%91%e7%8e%b0&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;对照组 C 的意外发现
&lt;/h3&gt;&lt;p&gt;当我们在虚拟线程环境中使用 &lt;code&gt;synchronized&lt;/code&gt; 块保护共享资源时，发现了一个被称为 &lt;strong&gt;Pinning（钉住）&lt;/strong&gt; 的现象。虚拟线程在执行 &lt;code&gt;synchronized&lt;/code&gt; 块或本地方法调用时，会被&amp;quot;钉&amp;quot;在其载体线程（carrier thread）上，无法被卸载。&lt;/p&gt;
&lt;p&gt;这意味着：如果 &lt;code&gt;synchronized&lt;/code&gt; 块内部有阻塞操作（如 I/O 调用），载体线程也会被阻塞，虚拟线程的并发优势被严重削弱。&lt;/p&gt;
&lt;p&gt;在对照组 C 的测试中，当关键路径使用了 &lt;code&gt;synchronized&lt;/code&gt; 后，虚拟线程的吞吐量直接退化到与平台线程接近的水平。JFR（Java Flight Recorder）的数据显示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;jdk.VirtualThreadPinned 事件数: 15,230 次/分钟
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;平均 Pin 持续时间: 45ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;载体线程利用率: 98%（几乎全部被钉住）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个发现促使我们对代码库进行了一次全面扫描，将所有热路径上的 &lt;code&gt;synchronized&lt;/code&gt; 替换为 &lt;code&gt;ReentrantLock&lt;/code&gt;。&lt;code&gt;ReentrantLock&lt;/code&gt; 在虚拟线程上不会导致 Pinning，因为它在等待锁时会正确卸载虚拟线程。&lt;/p&gt;
&lt;h2 id=&#34;上线过程从灰度到全量&#34;&gt;&lt;a href=&#34;#%e4%b8%8a%e7%ba%bf%e8%bf%87%e7%a8%8b%e4%bb%8e%e7%81%b0%e5%ba%a6%e5%88%b0%e5%85%a8%e9%87%8f&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;上线过程：从灰度到全量
&lt;/h2&gt;&lt;h3 id=&#34;灰度策略&#34;&gt;&lt;a href=&#34;#%e7%81%b0%e5%ba%a6%e7%ad%96%e7%95%a5&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;灰度策略
&lt;/h3&gt;&lt;p&gt;基于压测数据的信心，我们制定了三阶段灰度计划：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一阶段&lt;/strong&gt;（1 周）：10% 流量切入虚拟线程版本，观察基础指标&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二阶段&lt;/strong&gt;（1 周）：50% 流量，关注 GC 和内存表现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三阶段&lt;/strong&gt;（持续观察）：全量切换，重点监控长尾延迟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一阶段一切正常。第二阶段在 50% 流量下运行了两天后，运维监控开始出现 GC 暂停时间的告警。&lt;/p&gt;
&lt;h2 id=&#34;线上故障gc-暂停时间飙升&#34;&gt;&lt;a href=&#34;#%e7%ba%bf%e4%b8%8a%e6%95%85%e9%9a%9cgc-%e6%9a%82%e5%81%9c%e6%97%b6%e9%97%b4%e9%a3%99%e5%8d%87&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;线上故障：GC 暂停时间飙升
&lt;/h2&gt;&lt;h3 id=&#34;问题表现&#34;&gt;&lt;a href=&#34;#%e9%97%ae%e9%a2%98%e8%a1%a8%e7%8e%b0&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;问题表现
&lt;/h3&gt;&lt;p&gt;全量上线后的第三天，业务方反馈部分请求的响应时间出现&amp;quot;毛刺&amp;quot;。从监控数据来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GC 暂停时间&lt;/strong&gt;：从原来的平均 15ms 上升到 80-200ms，偶发超过 500ms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full GC 频率&lt;/strong&gt;：从每天 1-2 次上升到每小时 3-5 次&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;老年代使用率&lt;/strong&gt;：持续在 85% 以上，回收效率低下&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GC 日志中的典型片段如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[2.145s][info][gc] GC(1234) Pause Young (Normal) (G1 Evacuation Pause) 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Heap before: 3200M(4096M)  Heap after: 2800M(4096M)  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Humongous: 120M  Eden: 800M-&amp;gt;0M(600M)  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Pause: 185.3ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;根因分析&#34;&gt;&lt;a href=&#34;#%e6%a0%b9%e5%9b%a0%e5%88%86%e6%9e%90&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;根因分析
&lt;/h3&gt;&lt;p&gt;问题的根因可以归结为三个方面：&lt;/p&gt;
&lt;h4 id=&#34;1-虚拟线程对象本身的-gc-压力&#34;&gt;&lt;a href=&#34;#1-%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e5%af%b9%e8%b1%a1%e6%9c%ac%e8%ba%ab%e7%9a%84-gc-%e5%8e%8b%e5%8a%9b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;1. 虚拟线程对象本身的 GC 压力
&lt;/h4&gt;&lt;p&gt;每个虚拟线程在 JVM 内部都是一个 &lt;code&gt;java.lang.VirtualThread&lt;/code&gt; 对象，存在于堆中。在 8000 QPS、平均请求处理时间 150ms 的场景下，任意时刻活跃的虚拟线程数量约为 1200 个。这些对象虽然不大（约 1-2KB），但它们持有的 &lt;strong&gt;continuation 栈帧&lt;/strong&gt; 会占用额外的堆空间。&lt;/p&gt;
&lt;p&gt;当一个虚拟线程被卸载（unmount）时，它的栈帧会被复制到堆中的一个 &lt;code&gt;Continuation&lt;/code&gt; 对象里。在高并发下，这些 &lt;code&gt;Continuation&lt;/code&gt; 对象会频繁创建和销毁，给年轻代 GC 带来额外压力。&lt;/p&gt;
&lt;h4 id=&#34;2-threadlocal-的放大效应&#34;&gt;&lt;a href=&#34;#2-threadlocal-%e7%9a%84%e6%94%be%e5%a4%a7%e6%95%88%e5%ba%94&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;2. ThreadLocal 的放大效应
&lt;/h4&gt;&lt;p&gt;虚拟线程与 &lt;code&gt;ThreadLocal&lt;/code&gt; 的组合是一个隐蔽的性能陷阱。我们的服务中使用了 &lt;code&gt;ThreadLocal&lt;/code&gt; 来传递请求上下文（traceId、用户信息等）。在平台线程模型下，400 个线程意味着最多 400 份 &lt;code&gt;ThreadLocal&lt;/code&gt; 副本。而切换到虚拟线程后，同一时间可能有 8000+ 个虚拟线程活跃，&lt;code&gt;ThreadLocal&lt;/code&gt; 的副本数量暴增 20 倍。&lt;/p&gt;
&lt;p&gt;更关键的是，虚拟线程的生命周期通常很短（处理完一个请求就结束），这意味着这些 &lt;code&gt;ThreadLocal&lt;/code&gt; 对象会随着虚拟线程的销毁而变成垃圾。在高并发下，这导致了大量的短生命周期大对象涌入年轻代。&lt;/p&gt;
&lt;h4 id=&#34;3-连接池配置不匹配&#34;&gt;&lt;a href=&#34;#3-%e8%bf%9e%e6%8e%a5%e6%b1%a0%e9%85%8d%e7%bd%ae%e4%b8%8d%e5%8c%b9%e9%85%8d&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;3. 连接池配置不匹配
&lt;/h4&gt;&lt;p&gt;虚拟线程改变了并发模型，但下游连接池的配置没有相应调整。我们原来使用 HikariCP，最大连接数设为 50。在平台线程时代，400 个线程竞争 50 个连接是合理的——大部分线程会在等待连接时被阻塞，但由于线程数有限，不会造成资源耗尽。&lt;/p&gt;
&lt;p&gt;切换到虚拟线程后，8000 个虚拟线程同时竞争 50 个数据库连接。虽然虚拟线程在等待连接时不会占用载体线程，但大量等待中的虚拟线程持有的上下文对象（请求参数、中间结果等）全部存活在堆中，进一步加剧了 GC 压力。&lt;/p&gt;
&lt;h2 id=&#34;gc-调优三轮迭代&#34;&gt;&lt;a href=&#34;#gc-%e8%b0%83%e4%bc%98%e4%b8%89%e8%bd%ae%e8%bf%ad%e4%bb%a3&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;GC 调优：三轮迭代
&lt;/h2&gt;&lt;h3 id=&#34;第一轮调整堆大小和新生代比例&#34;&gt;&lt;a href=&#34;#%e7%ac%ac%e4%b8%80%e8%bd%ae%e8%b0%83%e6%95%b4%e5%a0%86%e5%a4%a7%e5%b0%8f%e5%92%8c%e6%96%b0%e7%94%9f%e4%bb%a3%e6%af%94%e4%be%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;第一轮：调整堆大小和新生代比例
&lt;/h3&gt;&lt;p&gt;最直觉的反应是&amp;quot;堆不够大&amp;quot;。我们将堆从 4GB 扩到 8GB，并调整了新生代比例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-Xms8g -Xmx8g &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:NewRatio&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1NewSizePercent&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;40&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1MaxNewSizePercent&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;60&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+UseG1GC
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;：Full GC 频率下降 50%，但 Young GC 的暂停时间反而增加了——更大的新生代意味着每次 Young GC 需要扫描更多的存活对象。P99 延迟没有明显改善。&lt;/p&gt;
&lt;h3 id=&#34;第二轮切换到-zgc&#34;&gt;&lt;a href=&#34;#%e7%ac%ac%e4%ba%8c%e8%bd%ae%e5%88%87%e6%8d%a2%e5%88%b0-zgc&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;第二轮：切换到 ZGC
&lt;/h3&gt;&lt;p&gt;既然 G1GC 在虚拟线程场景下表现不佳，我们尝试了 ZGC——它的亚毫秒级暂停时间在理论上可以完全解决 GC 暂停问题：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-Xms8g -Xmx8g &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+UseZGC &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+ZGenerational &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:SoftMaxHeapSize&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;6g &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:ZCollectionInterval&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;：GC 暂停时间确实降到了 1ms 以内，但出现了新的问题——ZGC 的并发标记和转移阶段会持续占用 CPU 资源。在我们的 8 核机器上，ZGC 的后台线程占用了约 1.5 个核的 CPU，导致业务请求的处理速度整体下降 15%。&lt;/p&gt;
&lt;p&gt;此外，ZGC 的内存占用更高（需要额外的染色指针和读屏障），RSS 从 4.2GB 涨到了 5.8GB，部分机器的 cgroup 内存限制被触及。&lt;/p&gt;
&lt;h3 id=&#34;第三轮g1gc-精细化调优--应用层优化&#34;&gt;&lt;a href=&#34;#%e7%ac%ac%e4%b8%89%e8%bd%aeg1gc-%e7%b2%be%e7%bb%86%e5%8c%96%e8%b0%83%e4%bc%98--%e5%ba%94%e7%94%a8%e5%b1%82%e4%bc%98%e5%8c%96&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;第三轮：G1GC 精细化调优 + 应用层优化
&lt;/h3&gt;&lt;p&gt;最终，我们回到了 G1GC，但进行了更精细的参数调优，同时配合应用层的改动：&lt;/p&gt;
&lt;h4 id=&#34;jvm-参数优化&#34;&gt;&lt;a href=&#34;#jvm-%e5%8f%82%e6%95%b0%e4%bc%98%e5%8c%96&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;JVM 参数优化
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-Xms6g -Xmx6g &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+UseG1GC &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:MaxGCPauseMillis&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1HeapRegionSize&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4m &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:InitiatingHeapOccupancyPercent&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;35&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1ReservePercent&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;15&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1MixedGCCountTarget&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;16&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:G1HeapWastePercent&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:ConcGCThreads&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:ParallelGCThreads&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+ParallelRefProcEnabled &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-XX:+G1UseAdaptiveIHOP
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&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;th&gt;调优逻辑&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MaxGCPauseMillis&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;50ms&lt;/td&gt;
					&lt;td&gt;明确告诉 G1 暂停时间目标，让它自适应调整回收策略&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;G1HeapRegionSize&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;4MB&lt;/td&gt;
					&lt;td&gt;增大 Region 大小，减少 Humongous 对象的产生&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;InitiatingHeapOccupancyPercent&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;35%&lt;/td&gt;
					&lt;td&gt;降低并发标记的触发阈值，提前开始回收&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;G1MixedGCCountTarget&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;16&lt;/td&gt;
					&lt;td&gt;增加 Mixed GC 的次数，每次回收更少的 Region，降低单次暂停&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;G1HeapWastePercent&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;3%&lt;/td&gt;
					&lt;td&gt;降低可容忍的堆浪费比例，更积极地触发 Mixed GC&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ConcGCThreads&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;2&lt;/td&gt;
					&lt;td&gt;限制并发 GC 线程数，为业务线程保留 CPU&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ParallelGCThreads&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;STW 阶段的并行度，与核心数匹配&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;应用层优化&#34;&gt;&lt;a href=&#34;#%e5%ba%94%e7%94%a8%e5%b1%82%e4%bc%98%e5%8c%96&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;应用层优化
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;1. 用 Scoped Values 替代 ThreadLocal&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 21 引入的 &lt;code&gt;ScopedValue&lt;/code&gt;（预览特性）是虚拟线程场景下 &lt;code&gt;ThreadLocal&lt;/code&gt; 的理想替代方案。它在语义上类似，但有以下优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不可变，消除了线程安全问题&lt;/li&gt;
&lt;li&gt;有明确的作用域边界，不会产生泄漏&lt;/li&gt;
&lt;li&gt;虚拟线程可以高效地继承父线程的 Scoped Value&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 改造前：ThreadLocal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RequestContext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONTEXT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 改造后：ScopedValue&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ScopedValue&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RequestContext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONTEXT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ScopedValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 使用方式&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ScopedValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;runWhere&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONTEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RequestContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;traceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 在此作用域内可以通过 CONTEXT.get() 访问上下文&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;processRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这一改动直接将上下文对象的内存占用降低了约 60%。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 连接池参数重新设计&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们重新计算了连接池的大小。核心思路是：虚拟线程让&amp;quot;等待&amp;quot;变得廉价，但数据库连接本身是有限的。过多的等待者虽然不会阻塞载体线程，但会增加堆内存压力。&lt;/p&gt;
&lt;p&gt;新策略：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;datasource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hikari&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;maximum-pool-size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;minimum-idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;connection-timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 关键：使用 Semaphore 限制并发请求数&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 防止过多虚拟线程同时进入数据库等待队列&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;同时在应用层增加了一个 &lt;code&gt;Semaphore&lt;/code&gt; 作为入口限流器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Semaphore&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;concurrentLimit&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Semaphore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;2000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InterruptedException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;concurrentLimit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;tryAcquire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MILLISECONDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;tooManyRequests&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;doHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;finally&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;concurrentLimit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个 &lt;code&gt;Semaphore&lt;/code&gt; 将同时活跃的虚拟线程数量限制在 2000 以内，超出的请求快速失败。虽然看起来限制了并发能力，但实际上避免了&amp;quot;雪崩效应&amp;quot;——当系统已经过载时，更多的并发请求只会让所有人都等得更久。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 消除不必要的 Pinning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过 JFR 持续监控 &lt;code&gt;jdk.VirtualThreadPinned&lt;/code&gt; 事件，逐一排查并修复了剩余的 Pinning 点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据库驱动中的 &lt;code&gt;synchronized&lt;/code&gt; 块：升级到支持虚拟线程的驱动版本&lt;/li&gt;
&lt;li&gt;第三方 SDK 中的本地方法调用：用 &lt;code&gt;ReentrantLock&lt;/code&gt; 包装或移至独立的平台线程池&lt;/li&gt;
&lt;li&gt;日志框架中的同步写入：改为异步日志（Log4j2 AsyncAppender）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;调优效果前后对比&#34;&gt;&lt;a href=&#34;#%e8%b0%83%e4%bc%98%e6%95%88%e6%9e%9c%e5%89%8d%e5%90%8e%e5%af%b9%e6%af%94&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&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;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;平均 GC 暂停&lt;/td&gt;
					&lt;td&gt;85ms&lt;/td&gt;
					&lt;td&gt;12ms&lt;/td&gt;
					&lt;td&gt;-86%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;P99 GC 暂停&lt;/td&gt;
					&lt;td&gt;520ms&lt;/td&gt;
					&lt;td&gt;35ms&lt;/td&gt;
					&lt;td&gt;-93%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Full GC 频率&lt;/td&gt;
					&lt;td&gt;4次/小时&lt;/td&gt;
					&lt;td&gt;0.2次/天&lt;/td&gt;
					&lt;td&gt;-99%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;P99 请求延迟&lt;/td&gt;
					&lt;td&gt;350ms&lt;/td&gt;
					&lt;td&gt;95ms&lt;/td&gt;
					&lt;td&gt;-73%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;堆内存使用峰值&lt;/td&gt;
					&lt;td&gt;5.2GB&lt;/td&gt;
					&lt;td&gt;3.8GB&lt;/td&gt;
					&lt;td&gt;-27%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CPU 利用率&lt;/td&gt;
					&lt;td&gt;78%&lt;/td&gt;
					&lt;td&gt;62%&lt;/td&gt;
					&lt;td&gt;-16%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;最大可承载 QPS&lt;/td&gt;
					&lt;td&gt;8500&lt;/td&gt;
					&lt;td&gt;13000&lt;/td&gt;
					&lt;td&gt;+53%&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;特别值得注意的是，调优后的系统不仅 GC 表现更好，整体吞吐量也提升了 53%。这说明 GC 问题本身也在拖慢业务处理——当 GC 暂停频繁发生时，大量虚拟线程被暂停，它们持有的请求上下文无法释放，进一步加剧了内存压力，形成恶性循环。&lt;/p&gt;
&lt;h2 id=&#34;虚拟线程使用注意事项清单&#34;&gt;&lt;a href=&#34;#%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e4%bd%bf%e7%94%a8%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9%e6%b8%85%e5%8d%95&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;虚拟线程使用注意事项清单
&lt;/h2&gt;&lt;p&gt;基于这段时间的实践经验，整理一份虚拟线程上线的检查清单：&lt;/p&gt;
&lt;h3 id=&#34;代码层面&#34;&gt;&lt;a href=&#34;#%e4%bb%a3%e7%a0%81%e5%b1%82%e9%9d%a2&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;代码层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;检查 &lt;code&gt;synchronized&lt;/code&gt; 使用&lt;/strong&gt;：在热路径上，将 &lt;code&gt;synchronized&lt;/code&gt; 替换为 &lt;code&gt;ReentrantLock&lt;/code&gt;。可以用 JFR 的 &lt;code&gt;VirtualThreadPinned&lt;/code&gt; 事件来定位问题点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;审查 &lt;code&gt;ThreadLocal&lt;/code&gt;&lt;/strong&gt;：评估是否可以用 &lt;code&gt;ScopedValue&lt;/code&gt; 替代，或者至少为 &lt;code&gt;ThreadLocal&lt;/code&gt; 设置合理的清理策略&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免在虚拟线程中执行 CPU 密集型任务&lt;/strong&gt;：虚拟线程适合 I/O 等待，不适合纯计算。CPU 密集型任务应使用传统的 &lt;code&gt;ForkJoinPool&lt;/code&gt; 或固定大小的线程池&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意 &lt;code&gt;native&lt;/code&gt; 方法调用&lt;/strong&gt;：JNI 调用同样会导致 Pinning&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;配置层面&#34;&gt;&lt;a href=&#34;#%e9%85%8d%e7%bd%ae%e5%b1%82%e9%9d%a2&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;配置层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;连接池大小&lt;/strong&gt;：不要因为&amp;quot;虚拟线程很便宜&amp;quot;就无限放大连接池。数据库和下游服务的承载能力是有限的&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;入口限流&lt;/strong&gt;：使用 &lt;code&gt;Semaphore&lt;/code&gt; 或类似机制限制同时活跃的虚拟线程数量，防止雪崩&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GC 参数&lt;/strong&gt;：虚拟线程场景下需要更低的 &lt;code&gt;InitiatingHeapOccupancyPercent&lt;/code&gt; 和更积极的 Mixed GC 策略&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控 JFR 事件&lt;/strong&gt;：将 &lt;code&gt;jdk.VirtualThreadPinned&lt;/code&gt; 和 &lt;code&gt;jdk.VirtualThreadSubmitFailed&lt;/code&gt; 纳入监控告警&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;框架层面&#34;&gt;&lt;a href=&#34;#%e6%a1%86%e6%9e%b6%e5%b1%82%e9%9d%a2&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;框架层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spring Boot 3.2+&lt;/strong&gt;：通过 &lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt; 一键开启，但需要确认所有依赖库都兼容&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库驱动&lt;/strong&gt;：PostgreSQL JDBC 42.7+、MySQL Connector/J 8.3+ 对虚拟线程有较好的支持&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP 客户端&lt;/strong&gt;：Java 内置的 &lt;code&gt;HttpClient&lt;/code&gt; 天然支持虚拟线程，RestTemplate 需要额外配置&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;常见误区与陷阱&#34;&gt;&lt;a href=&#34;#%e5%b8%b8%e8%a7%81%e8%af%af%e5%8c%ba%e4%b8%8e%e9%99%b7%e9%98%b1&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;常见误区与陷阱
&lt;/h2&gt;&lt;h3 id=&#34;误区一虚拟线程越多越好&#34;&gt;&lt;a href=&#34;#%e8%af%af%e5%8c%ba%e4%b8%80%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e8%b6%8a%e5%a4%9a%e8%b6%8a%e5%a5%bd&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;误区一：&amp;ldquo;虚拟线程越多越好&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;虚拟线程虽然轻量，但不是免费的。每个虚拟线程都需要堆内存来存储其 continuation 栈帧。在某些极端场景下（如虚拟线程内部嵌套大量局部变量），单个虚拟线程的堆占用可以达到几十 KB。如果同时有 10 万个这样的虚拟线程活跃，仅 continuation 就需要数 GB 的堆空间。&lt;/p&gt;
&lt;h3 id=&#34;误区二虚拟线程可以替代异步编程&#34;&gt;&lt;a href=&#34;#%e8%af%af%e5%8c%ba%e4%ba%8c%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e5%8f%af%e4%bb%a5%e6%9b%bf%e4%bb%a3%e5%bc%82%e6%ad%a5%e7%bc%96%e7%a8%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;误区二：&amp;ldquo;虚拟线程可以替代异步编程&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;虚拟线程解决的是&amp;quot;同步阻塞代码的并发效率&amp;quot;问题，但它不能替代所有异步场景。例如：&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;：Reactive Streams 的背压机制在虚拟线程模型下需要额外设计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时推送&lt;/strong&gt;：WebSocket 长连接的管理逻辑并不因虚拟线程而简化&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;误区三开启虚拟线程后不需要调优&#34;&gt;&lt;a href=&#34;#%e8%af%af%e5%8c%ba%e4%b8%89%e5%bc%80%e5%90%af%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e5%90%8e%e4%b8%8d%e9%9c%80%e8%a6%81%e8%b0%83%e4%bc%98&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;误区三：&amp;ldquo;开启虚拟线程后不需要调优&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;这是最危险的误区。虚拟线程改变了并发模型，但没有改变物理资源的限制——CPU 核心数、内存带宽、数据库连接数、网络带宽都是固定的。虚拟线程只是让&amp;quot;等待&amp;quot;变得更高效，但当所有虚拟线程都不需要等待时（CPU 密集型场景），它不会比平台线程更快。&lt;/p&gt;
&lt;h3 id=&#34;陷阱虚拟线程--completablefuture&#34;&gt;&lt;a href=&#34;#%e9%99%b7%e9%98%b1%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b--completablefuture&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;陷阱：虚拟线程 + &lt;code&gt;CompletableFuture&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;一个容易忽略的陷阱是：&lt;code&gt;CompletableFuture.supplyAsync()&lt;/code&gt; 默认使用 &lt;code&gt;ForkJoinPool.commonPool()&lt;/code&gt;，这个池的大小等于 CPU 核心数减一。如果在虚拟线程中调用 &lt;code&gt;CompletableFuture&lt;/code&gt; 的异步方法，实际上是在平台线程池上执行，而不是在虚拟线程上。&lt;/p&gt;
&lt;p&gt;正确做法是显式指定一个虚拟线程执行器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ExecutorService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vtExecutor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Executors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;CompletableFuture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;supplyAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;callRemoteService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vtExecutor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;性能监控体系搭建&#34;&gt;&lt;a href=&#34;#%e6%80%a7%e8%83%bd%e7%9b%91%e6%8e%a7%e4%bd%93%e7%b3%bb%e6%90%ad%e5%bb%ba&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;性能监控体系搭建
&lt;/h2&gt;&lt;p&gt;在调优过程中，我们逐步建立了一套针对虚拟线程的监控体系，以下是核心监控项：&lt;/p&gt;
&lt;h3 id=&#34;jvm-层监控&#34;&gt;&lt;a href=&#34;#jvm-%e5%b1%82%e7%9b%91%e6%8e%a7&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;JVM 层监控
&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;/td&gt;
					&lt;td&gt;JMX &lt;code&gt;ThreadMXBean&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&amp;gt; 5000&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VirtualThreadPinned 事件&lt;/td&gt;
					&lt;td&gt;JFR 持续录制&lt;/td&gt;
					&lt;td&gt;&amp;gt; 100次/分钟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;载体线程利用率&lt;/td&gt;
					&lt;td&gt;JFR + 自定义指标&lt;/td&gt;
					&lt;td&gt;&amp;gt; 80%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Continuation 对象堆占用&lt;/td&gt;
					&lt;td&gt;JMAP 分析&lt;/td&gt;
					&lt;td&gt;&amp;gt; 1GB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;GC 暂停时间 P99&lt;/td&gt;
					&lt;td&gt;GC 日志解析&lt;/td&gt;
					&lt;td&gt;&amp;gt; 50ms&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;应用层监控&#34;&gt;&lt;a href=&#34;#%e5%ba%94%e7%94%a8%e5%b1%82%e7%9b%91%e6%8e%a7&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;应用层监控
&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;Semaphore 等待队列长度&lt;/td&gt;
					&lt;td&gt;自定义 Micrometer Gauge&lt;/td&gt;
					&lt;td&gt;&amp;gt; 500&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;连接池等待数&lt;/td&gt;
					&lt;td&gt;HikariCP Metrics&lt;/td&gt;
					&lt;td&gt;&amp;gt; 20&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;请求快速失败率&lt;/td&gt;
					&lt;td&gt;应用层 Counter&lt;/td&gt;
					&lt;td&gt;&amp;gt; 5%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;下游调用 P99 延迟&lt;/td&gt;
					&lt;td&gt;链路追踪&lt;/td&gt;
					&lt;td&gt;&amp;gt; 200ms&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这套监控体系让我们能够在问题恶化之前及时发现。例如，当活跃虚拟线程数异常增长时，通常意味着下游某个服务响应变慢，导致虚拟线程积压。此时可以通过 &lt;code&gt;Semaphore&lt;/code&gt; 的快速失败机制保护系统不被压垮，同时触发告警通知值班人员排查下游服务。&lt;/p&gt;
&lt;h2 id=&#34;虚拟线程与协程的对比思考&#34;&gt;&lt;a href=&#34;#%e8%99%9a%e6%8b%9f%e7%ba%bf%e7%a8%8b%e4%b8%8e%e5%8d%8f%e7%a8%8b%e7%9a%84%e5%af%b9%e6%af%94%e6%80%9d%e8%80%83&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;虚拟线程与协程的对比思考
&lt;/h2&gt;&lt;p&gt;在调研阶段，团队内部曾有过一次关于技术选型的讨论：既然 Kotlin 协程和 Go 的 goroutine 已经验证了用户态调度的可行性，为什么还要选择虚拟线程？&lt;/p&gt;
&lt;p&gt;核心考量有三个方面：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;生态兼容性&lt;/strong&gt;。我们的技术栈以 Java 为主，大量使用了 Spring、MyBatis、各种 SDK 等依赖。这些库的 API 都是同步阻塞风格。虚拟线程的最大优势在于&amp;quot;不改代码就能享受高并发&amp;quot;，而协程通常需要语言层面的 &lt;code&gt;suspend&lt;/code&gt;/&lt;code&gt;async&lt;/code&gt; 关键字支持，与现有 Java 库的兼容性有限。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;迁移成本&lt;/strong&gt;。从平台线程切换到虚拟线程，核心改动只有一行配置。而从同步代码迁移到协程，需要重写大量的方法签名和调用链。对于一个有几十万行代码的系统来说，这个成本是不可接受的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;团队认知&lt;/strong&gt;。Java 开发者对线程模型有深入的理解，虚拟线程的学习曲线几乎为零。而引入协程意味着团队需要掌握新的并发原语和思维模式。&lt;/p&gt;
&lt;p&gt;当然，如果是一个全新的 Kotlin 项目，协程可能是更好的选择。技术选型没有绝对的优劣，只有适合与不适合。&lt;/p&gt;
&lt;h2 id=&#34;后续演进方向&#34;&gt;&lt;a href=&#34;#%e5%90%8e%e7%bb%ad%e6%bc%94%e8%bf%9b%e6%96%b9%e5%90%91&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;后续演进方向
&lt;/h2&gt;&lt;p&gt;当前系统稳定运行后，我们还在探索几个方向：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured Concurrency（结构化并发）&lt;/strong&gt;。Java 21 中的预览特性，可以将一组相关的虚拟线程组织为一个工作单元，统一管理和取消。这对于&amp;quot;扇出调用多个下游服务并聚合结果&amp;quot;的场景非常有用——任何一个子任务失败，都可以立即取消其他子任务，避免资源浪费。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ZGC 的 Generational 模式&lt;/strong&gt;。随着 JDK 22/23 中 Generational ZGC 的成熟，它的内存效率已经大幅改善。在虚拟线程场景下，大量的短生命周期 continuation 对象正好适合分代回收。计划在 JDK 23 LTS 发布后重新评估 ZGC。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;虚拟线程 + Project Loom 的 ScopedValue 稳定版&lt;/strong&gt;。当 &lt;code&gt;ScopedValue&lt;/code&gt; 从预览特性正式转正后，可以彻底消除 &lt;code&gt;ThreadLocal&lt;/code&gt; 在虚拟线程场景下的性能和安全隐患。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;虚拟线程不是银弹，但它确实是 Java 并发编程十年来最重要的改进之一。关键在于理解它的运行机制——虚拟线程是&amp;quot;廉价的等待&amp;quot;，而不是&amp;quot;廉价的计算&amp;quot;。当你的系统瓶颈在于 I/O 等待（数据库查询、远程调用、文件读写）时，虚拟线程能带来显著的吞吐提升；当瓶颈在于 CPU 计算时，虚拟线程不会带来任何魔法。&lt;/p&gt;
&lt;p&gt;每一次技术升级都是一次重新认识系统的机会。从这次虚拟线程的落地实践中，我们不仅获得了性能提升，更重要的是建立了一套从压测到监控、从发现问题到调优优化的完整方法论。这套方法论的价值，远超过某一次具体的参数调整。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
