<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; Java</title>
	<atom:link href="http://tonybai.com/tag/java/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Fri, 17 Apr 2026 00:21:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>从 1960 到 2026：一文看透 Java、Go、Python 垃圾回收器的原理与演进</title>
		<link>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/</link>
		<comments>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/#comments</comments>
		<pubDate>Tue, 07 Apr 2026 00:17:15 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ColoredPointers]]></category>
		<category><![CDATA[ConcurrentMarking]]></category>
		<category><![CDATA[CopyingCollector]]></category>
		<category><![CDATA[CPython]]></category>
		<category><![CDATA[Cycles]]></category>
		<category><![CDATA[EscapeAnalysis]]></category>
		<category><![CDATA[G1GC]]></category>
		<category><![CDATA[GarbageCollection]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[GenerationalHypothesis]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HybridWriteBarrier]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JVM]]></category>
		<category><![CDATA[latency]]></category>
		<category><![CDATA[MarkAndSweep]]></category>
		<category><![CDATA[MemoryManagement]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[ReferenceCounting]]></category>
		<category><![CDATA[StopTheWorld]]></category>
		<category><![CDATA[STW]]></category>
		<category><![CDATA[Throughput]]></category>
		<category><![CDATA[TriColorMarking]]></category>
		<category><![CDATA[WriteBarrier]]></category>
		<category><![CDATA[ZGC]]></category>
		<category><![CDATA[三色标记]]></category>
		<category><![CDATA[全线停顿]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[写屏障]]></category>
		<category><![CDATA[分代假说]]></category>
		<category><![CDATA[吞吐量]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[复制算法]]></category>
		<category><![CDATA[并发标记]]></category>
		<category><![CDATA[延迟]]></category>
		<category><![CDATA[引用计数]]></category>
		<category><![CDATA[循环引用]]></category>
		<category><![CDATA[有色指针]]></category>
		<category><![CDATA[标记清除]]></category>
		<category><![CDATA[混合写屏障]]></category>
		<category><![CDATA[逃逸分析]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6154</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/07/garbage-collectors-deep-dive 大家好，我是Tony Bai。 为什么 Java 的 G1GC 需要设置停顿目标？Go 的混合写屏障是如何消除栈重扫的？Python 又是如何解决引用计数无法处理的循环引用？ 垃圾回收（GC）不仅是语言运行时的核心，更是理解高性能系统绕不开的坎。 本文翻译自Shubham Raizada的文章《Garbage Collection: From First Principles to Modern Collectors in Java, Go and Python》。 此文通过对历史经典论文的溯源和对现代主流语言底层实现的拆解，构建了一套完整的 GC 知识体系。 文章涵盖了从基础的标记-清除、复制与整理算法，到复杂的三色标记抽象、写屏障机制以及有色指针技术。 无论你是想调优 JVM 性能，还是试图理解 Go 并发垃圾收集的吞吐成本，这篇文章都将为你提供从理论支撑到代码实现的全景视角。 以下是译文全文： 在过去的几年里，我的技术栈经历了从 Java 到 Go，再到 Rust，现在又回到了 Java 的过程。 在这些语言之间切换时，一直绕不开的一个话题就是垃圾回收（Garbage Collection, GC）。Java 和 Go 有 GC，而 Rust 没有。 在基准测试、延迟讨论以及“为什么这个服务变慢了”的对话中，GC 总会出现在某个角落。我经常听到关于 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/07/garbage-collectors-deep-dive">本文永久链接</a> &#8211; https://tonybai.com/2026/04/07/garbage-collectors-deep-dive</p>
<p>大家好，我是Tony Bai。</p>
<p>为什么 Java 的 G1GC 需要设置停顿目标？Go 的混合写屏障是如何消除栈重扫的？Python 又是如何解决引用计数无法处理的循环引用？</p>
<p>垃圾回收（GC）不仅是语言运行时的核心，更是理解高性能系统绕不开的坎。</p>
<p>本文翻译自Shubham Raizada的文章《<a href="https://shbhmrzd.github.io/systems/garbage-collection/memory-management/2026/04/01/garbage-collectors-deep-dive.html">Garbage Collection: From First Principles to Modern Collectors in Java, Go and Python</a>》。</p>
<p>此文通过对历史经典论文的溯源和对现代主流语言底层实现的拆解，构建了一套完整的 GC 知识体系。</p>
<p>文章涵盖了从基础的标记-清除、复制与整理算法，到复杂的三色标记抽象、写屏障机制以及有色指针技术。</p>
<p>无论你是想调优 JVM 性能，还是试图理解 Go 并发垃圾收集的吞吐成本，这篇文章都将为你提供从理论支撑到代码实现的全景视角。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<p>以下是译文全文：</p>
<hr />
<p>在过去的几年里，我的技术栈经历了从 Java 到 Go，再到 Rust，现在又回到了 Java 的过程。</p>
<p>在这些语言之间切换时，一直绕不开的一个话题就是垃圾回收（Garbage Collection, GC）。Java 和 Go 有 GC，而 Rust 没有。</p>
<p>在基准测试、延迟讨论以及“为什么这个服务变慢了”的对话中，GC 总会出现在某个角落。我经常听到关于 GC pauses（GC 停顿）、throughput overhead（吞吐量开销）和 write barriers（写屏障）的讨论，但我并不完全理解底层发生了什么。</p>
<p>在追溯起源时，我读到了 McCarthy 1960 年的论文，这篇论文因引入 Lisp 而闻名，但它也是首次描述 mark-and-sweep（标记-清除）的地方。</p>
<p>这又引导我阅读了 Wilson 1992 年的综述《Uniprocessor Garbage Collection Techniques》，该文将随后的所有发展组织成了一个清晰的分类学。</p>
<p>阅读这两篇文献让我更容易理解现代垃圾收集器，因为 G1GC、ZGC、Go 的并发收集器以及 CPython 的混合方案全都是这些论文所描述思想的变体。我还用 Go 编写了一个简单的玩具级 GC，以便亲自观察其机制。</p>
<p>以下是我在这一过程中的笔记。</p>
<h2>起源论文</h2>
<h3>McCarthy (1960): <a href="https://dl.acm.org/doi/10.1145/367177.367199">Recursive Functions of Symbolic Expressions and Their Computation by Machine</a></h3>
<p>这篇论文因引入 Lisp 而闻名，但垃圾回收器几乎是作为实现细节被埋藏在其中的。McCarthy 需要一种方法来管理符号表达式的内存。Lisp 程序操作的是嵌套的列表（lists of lists of lists），这种递归结构使得要求程序员手动释放内存变得不切实际。因此，他描述了一种自动执行此操作的机制。</p>
<p>该机制分为两个阶段。首先，从程序正在活跃使用的 root（根）变量开始，遍历它们引用的每一个对象，将每个对象标记为 reachable（可达）。其次，扫描所有内存。任何未被标记的对象都是垃圾。将它们重新添加回 free list（空闲列表）。</p>
<p>这就是 mark-and-sweep（标记-清除）。它能自然地处理 cycles（循环引用，因为不可达的循环永远不会被标记），不需要逐个对象的簿记工作，并让程序员可以完全忽略内存管理。</p>
<p>其代价是程序在收集器运行时必须完全停止。每一次分配、每一次计算，所有一切都会冻结，直到标记和清除完成。对于 McCarthy 在 1960 年编写的程序来说，这完全是合理的。</p>
<p>随着程序规模变大并进入对延迟敏感的环境（如处理每秒数千次请求的 Web 服务器），stop-the-world（全线停顿）成了一个难以接受的权衡。现代 GC 研究产生的大部分成果都是为了回答一个问题：如何在不停止世界的情况下进行垃圾内存回收？</p>
<h3>Wilson (1992): <a href="https://dl.acm.org/doi/10.5555/645648.664824">Uniprocessor Garbage Collection Techniques</a></h3>
<p>到 1992 年，三十年的 GC 研究已经产生了许多想法，但缺乏统一的词汇。Wilson 的综述论文将这一切组织了起来。它不是一种新算法，而是一个分类学，为散落在几十年论文中的思想赋予了名称和结构。</p>
<p>Wilson 正式确立了所有后续算法构建其上的三种经典算法。</p>
<p>第一种是 <strong>mark-and-sweep</strong>（标记-清除），即 McCarthy 的原始算法。从 roots 开始，遍历对象图，标记你能触达的所有内容，然后扫过堆并释放任何未标记的内容。它自然处理循环引用，实现简单。缺点是经过足够多的分配和回收循环后，堆会变得 fragmented（碎片化）。存活对象最终散落在各处，中间夹杂着细小的空闲间隙，分配器(allocator)必须更费力地寻找空间。</p>
<p>第二种是 <strong>copying</strong>（复制算法），有时被称为 semi-space（半空间）。其想法是将堆分成两个相等的部分。你在其中一半进行分配，当它填满时，将所有存活对象拷贝到另一半，然后将第一半完全丢弃。碎片消失了，因为存活对象在拷贝过程中被紧密排列在一起。分配速度很快，因为你只需移动一个 bump pointer（碰撞指针）。代价是有一半的内存始终处于空闲状态，等待成为下一次拷贝的目标。</p>
<p>第三种是 <strong>reference counting</strong>（引用计数）。每个对象都记录有多少个指针指向它。当创建一个新引用时，计数增加；当移除一个引用时，计数减少。当计数归零时，对象立即被释放。没有追踪过程，没有停顿，销毁是确定性的。问题在于 cycles（循环引用）。如果两个对象相互指向，即使程序中没有任何其他部分可以触达它们，它们的计数也至少为 1。仅靠引用计数，它们永远不会被释放。</p>
<p>除了这三种算法，Wilson 还探讨了现代垃圾回收器赖以生存的两个观察结果。</p>
<p>第一个是 <strong>generational hypothesis</strong>（分代假说）：大多数对象死得早。在实践中，程序分配的临时对象（中间值、请求作用域的缓冲区、循环变量）往往很快变成垃圾，而只有一小部分对象会贯穿整个程序生命周期。如果你频繁回收年轻对象，偶尔回收老对象，你就能将大部分工作集中在堆中主要是垃圾的部分，这比每次都扫描所有内容的代价要小得多。</p>
<p>第二个是 <strong>tricolor marking</strong>（三色标记），这是一种用于增量和并发收集的抽象。你不再简单地将对象标记为已访问或未访问，而是使用三种颜色：white（白色，尚未见到）、grey（灰色，已见到但子节点尚未扫描）和 black（黑色，已完全处理）。收集器一次处理一个灰色对象。结束时，白色对象即为垃圾。这种抽象使得收集器和应用程序可以同时运行，而不会破坏彼此对堆的视图。Go 的并发 mark-and-sweep 和 ZGC 的并发标记都是这一思想的直接后裔。</p>
<p>本文“现代 GC”部分中的所有内容都可以映射回 Wilson 的分类。工程实现已经变得更加复杂，但底层结构依然如故。</p>
<h2>两种基本方法</h2>
<p>几乎所有的垃圾回收器要么是 reference counting（引用计数），要么是 tracing（追踪），或者是两者的某种结合。Wilson 的论文围绕这一划分进行组织，三十年后依然成立。</p>
<h3>Reference Counting (引用计数)</h3>
<p>每个对象维护一个指向它的引用计数。当引用创建时，计数增加。当引用移除时，计数减少。当计数归零时，对象立即被释放。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-2.png" alt="" /></p>
<p>这是 CPython 所使用的其主要机制。它很简单，并能提供确定性的销毁。当指向文件句柄的最后一个引用消失时，<strong>del</strong> 运行，文件当场关闭，而不是在以后的某个 GC cycle中。</p>
<p>有两个问题使得引用计数无法独立胜任。</p>
<p><strong>Cycles (循环引用)。</strong> 如果对象 A 指向对象 B，且对象 B 指向 A，那么即使程序中没有任何其他部分能触达它们，两者的计数也至少为 1。两者都不会被释放。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-3.png" alt="" /></p>
<p>这并非理论上的边缘案例。循环引用在链表数据结构、父子关系、观察者模式和缓存中自然出现。稍后在介绍 CPython 的 GC 时，我将讨论 Python 如何处理这个问题。</p>
<p><strong>Per-mutation overhead (每次修改的开销)。</strong> 每次指针赋值都需要更新引用计数。在多线程程序中，这些必须是 atomic（原子）操作，成本昂贵得多。每当你将对象传递给函数、返回它或将其赋值给字段时，你都要支付这种代价。</p>
<h3>Tracing (追踪式，即 Mark-and-Sweep)</h3>
<p>追踪式收集器不跟踪单个引用，而是从一组已知的存活引用（称为 root set，根集合）开始，遍历整个对象图。它能触达的每个对象都被标记为存活。其他所有对象都被释放。</p>
<p>Root set 是起点，因此什么算作 root（根）至关重要。不同语言的答案是相同的：root 是 runtime（运行时）无需追踪就能找到的任何引用。这些指针锚定在程序当前的执行状态中，是在任何遍历开始之前你就知道是存活的东西。</p>
<p>在实践中，roots 分为以下几类。</p>
<p>每个活跃 stack frame（栈帧）中的 <strong>local variables</strong>（局部变量）和函数参数都是 roots。程序正在活跃地运行这些函数，因此它们引用的任何内容定义上都是在使用中的。</p>
<p><strong>Global and static variables</strong>（全局变量和静态变量）是 roots，因为它们在程序的整个生命周期内都存在。</p>
<p><strong>CPU registers</strong>（CPU 寄存器）是 roots。因为当 JIT 编译器优化一个热点方法时，它可能会将频繁访问的对象引用保留在 CPU 寄存器中，而不是写回栈。如果 GC 此时运行，寄存器保存着该对象的唯一存活引用。如果 GC 不扫描寄存器，它就会释放一个仍在使用中的对象。为了防止这种情况，运行时在代码中定义了 safe points（安全点），GC 只能在这些点发生，并且在这些点，它会快照寄存器状态以寻找持有的任何引用。</p>
<p><strong>Runtime（运行时）本身</strong>也持有与用户代码无关的 roots。在 JVM 中，class loaders 是 roots：你加载的每个类都由其类加载器引用，只要类加载器存活，它加载的每个类（包括它们的静态字段）就保持存活。Interned strings（常量池字符串）是 roots，因为 String.intern() 将字符串存储在 JVM 维护的共享池中。JNI handles 是 roots，因为当原生 C 或 C++ 代码通过 Java Native Interface 持有 Java 对象的引用时，该引用存在于 Java 堆外的句柄表中，GC 必须扫描它。每个活跃线程都是一个 root，其整个调用栈帧都是 root set 的一部分。</p>
<p>Go 的运行时遵循同样的原则。每个 goroutine 都有自己的栈，必须扫描所有 goroutine 栈以寻找 roots。运行时还跟踪自己的内部数据结构，例如 finalizer 队列，作为 root set 的一部分。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-4.png" alt="" /></p>
<p>核心见解是：roots 是由运行时在无需追踪的情况下就已经知道是存活的东西定义的。其他所有东西必须通过从 root 可达来证明自己的生存权。这就是为什么这个概念是与语言无关的。Java、Go 和 Python 之间的具体 roots 集合有所不同，但原则是一样的：从你知道是存活的地方开始，向外追踪，并回收其余部分。</p>
<p>循环引用被自然处理。如果 A 和 B 相互指向，但都无法从任何 root 到达，则标记阶段永远不会访问它们。它们保持未标记状态并被清除。</p>
<p>代价：朴素的 mark-and-sweep 必须在追踪堆时暂停整个程序。这种 stop-the-world（全线停顿）是早期垃圾回收器的核心问题，也是现代 GC 几十年来工程化改进的重点。</p>
<h3>为什么大多数现代 GC 都是追踪式的</h3>
<p>在具有高分配速率的服务器工作负载中，引用计数的逐次修改成本会积少成多。每次指针写入都会增减计数。在多线程程序中，这些更新必须是原子的，而原子操作很昂贵。在数十个线程中每秒进行数千次分配时，这种开销变得可衡量。此外，循环引用问题无论如何都需要一个补充的追踪步骤。而且追踪式收集器可以做成并发的，在应用程序运行的同时运行，只有简短的停顿。</p>
<p>Java 和 Go 使用追踪式收集器。Python 是一个显著的例外，它以引用计数为基础，并在此之上增加了一层用于追踪循环引用的检测器。</p>
<h2>追踪式的变体</h2>
<p>Wilson 的论文描述了实现追踪的四种方式，每种方式都有不同的权衡。</p>
<h3>Mark-Sweep (标记-清除)</h3>
<p>最简单的追踪式收集器。分为两个阶段：</p>
<ol>
<li><strong>Mark (标记)：</strong> 从 roots 开始，遍历对象图并在每个可达对象上设置标记位。</li>
<li><strong>Sweep (清除)：</strong> 遍历整个堆。任何没有标记位的对象都是垃圾。释放它并将内存添加回空闲列表。</li>
</ol>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-5.png" alt="" /></p>
<p>Mark-sweep 的主要问题是 fragmentation（碎片化）。经过足够的回收周期后，堆看起来就像瑞士奶酪：存活对象散布其间，中间有很小的空闲间隙。你总共可能有 100MB 空闲内存，但没有一个连续的块大到足以满足一次新分配。分配器必须维护一个 free list 并搜索合适的空间，随着堆变得碎片化，这会变慢。</p>
<h3>Copying (Semi-Space，复制算法/半空间)</h3>
<p>堆被分成两个相等的一半：from-space（源空间）和 to-space（目标空间）。分配发生在 from-space，使用简单的 bump pointer（碰撞指针）。当 from-space 填满时，收集器将所有存活对象拷贝到 to-space，更新所有指针，然后交换两者的角色。旧的 from-space 被完全丢弃。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-6.png" alt="" /></p>
<p>分配速度极快，因为它只是一个指针移动。Compaction（压缩）自然发生。代价是任何时候只有一半的堆可用。</p>
<h3>Mark-Compact (标记-整理)</h3>
<p>标记阶段与 mark-sweep 相同，但收集器不是简单地释放未标记的对象，而是将所有存活对象滑动到堆的一端。这消除了碎片，且没有复制算法 50% 的内存开销。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-7.png" alt="" /></p>
<p>缺点是整理需要对堆进行多次扫描：一次标记，一次计算新地址，一次更新所有指针，一次移动对象。</p>
<h3>The Generational Hypothesis (分代假说)</h3>
<p>Wilson 论文中最具影响力的观察之一是弱分代假说：大多数对象死得早。</p>
<p>在典型的 Web 服务器中，每个请求都会创建临时对象（解析器、中间字符串、响应构建器），它们只存活几毫秒。配置对象、连接池和缓存则贯穿整个应用程序生命周期。</p>
<p>分代收集器利用这一点，将堆划分为 generations（代）。新对象进入 young generation（年轻代）。如果它们在几次回收中幸存下来，就会被提升到 old generation（老年代）。年轻代回收频繁且速度快，因为那里的大多数对象已经死了。老年代回收较少发生。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-8.png" alt="" /></p>
<p><strong>Eden</strong> 是所有新对象出生的地方。每一个 new Object() 都去这里。它很快就会填满，因为大多数程序分配速率很高。</p>
<p><strong>S0 和 S1</strong> 是两个较小的 survivor spaces（幸存者空间）。当 Eden 填满并运行 minor GC（次要回收）时，收集器将 Eden 中的每个存活对象拷贝到其中一个空间（比如 S0）。下一次回收时，来自 Eden 和 S0 的幸存者被拷贝到 S1。再下一次，回到 S0。它们在每个周期轮换。这是年轻代中的复制算法：没有碎片，没有空闲列表，只有两半空间轮流充当目标。代价是你需要两个幸存者空间，但它们保持得很小，因为到回收运行时，Eden 中的大多数对象都已经死了。</p>
<p><strong>Promotion to old generation (提升到老年代)。</strong> 在对象在 S0 和 S1 之间反弹足够多次之后（JVM 中的默认阈值是 15 次），收集器认定它已赢得了一席之地，并将其提升到老年代。老年代回收频率低得多，并且使用更重的算法（标记-整理而非复制），因为那里的对象庞大且长寿。</p>
<p>关键的实现挑战是跟踪从老对象到新对象的引用。如果一个老对象指向一个年轻对象，即使没有年轻代 root 指向它，该年轻对象也绝不能被回收。这通过 write barrier（写屏障）解决，即在每次指针写入时注入的一小段代码，用于在 remembered set（记录集）中记录跨代引用。</p>
<h2>用 Go 构建一个玩具级 Mark-and-Sweep GC</h2>
<p>我写了一个极简的 mark-and-sweep 收集器来使这些概念具体化。它大约有 70 行代码，演示了完整循环：分配对象、构建对象图、从 roots 标记以及清除不可达对象。</p>
<pre><code>package main

import "fmt"

// Object 代表一个在堆上分配的对象。
type Object struct {
    name     string
    marked   bool
    children []*Object
}

// VM 是一个带有垃圾回收器的微型虚拟机。
type VM struct {
    heap  []*Object
    roots []*Object // 模拟栈变量和全局变量
}

// NewObject 在 VM 的堆上分配一个对象。
func (vm *VM) NewObject(name string) *Object {
    obj := &amp;Object{name: name}
    vm.heap = append(vm.heap, obj)
    return obj
}

// mark 从每个 root 开始遍历并标记所有可达对象。
func (vm *VM) mark() {
    for _, root := range vm.roots {
        vm.markObject(root)
    }
}

func (vm *VM) markObject(obj *Object) {
    if obj == nil || obj.marked {
        return
    }
    obj.marked = true
    for _, child := range obj.children {
        vm.markObject(child)
    }
}

// sweep 释放未标记的对象并重置幸存者的标记。
func (vm *VM) sweep() {
    alive := []*Object{}
    for _, obj := range vm.heap {
        if obj.marked {
            obj.marked = false // 为下一个 GC 周期重置
            alive = append(alive, obj)
        } else {
            fmt.Printf("  collected: %s\n", obj.name)
        }
    }
    vm.heap = alive
}

// GC 运行一次完整的 mark-and-sweep 回收。
func (vm *VM) GC() {
    fmt.Printf("gc: heap has %d objects\n", len(vm.heap))
    vm.mark()
    vm.sweep()
    fmt.Printf("gc: %d objects remain\n\n", len(vm.heap))
}

func main() {
    vm := &amp;VM{}

    a := vm.NewObject("A")
    b := vm.NewObject("B")
    c := vm.NewObject("C")
    _ = vm.NewObject("D") // 已分配但从未链接到任何东西

    // 构建图: A -&gt; B -&gt; C
    a.children = append(a.children, b)
    b.children = append(b.children, c)

    // 只有 A 是 root
    vm.roots = append(vm.roots, a)

    fmt.Println("=== GC #1: D is unreachable ===")
    vm.GC()

    // 创建循环: C -&gt; A, 然后移除所有 roots
    c.children = append(c.children, a)
    vm.roots = nil

    fmt.Println("=== GC #2: A-&gt;B-&gt;C-&gt;A cycle, no roots ===")
    vm.GC()
}
</code></pre>
<p>运行结果：</p>
<pre><code>=== GC #1: D is unreachable ===
gc: heap has 4 objects
  collected: D
gc: 3 objects remain

=== GC #2: A-&gt;B-&gt;C-&gt;A cycle, no roots ===
gc: heap has 3 objects
  collected: A
  collected: B
  collected: C
gc: 0 objects remain
</code></pre>
<p>第一次回收：A、B 和 C 通过 root A 可达。D 没有任何 root 路径，因此被回收。</p>
<p>第二次回收：A、B 和 C 形成了一个循环（A->B->C->A），但没有 roots。标记阶段从未访问过它们中的任何一个。所有三个都被清除了。这正是击败引用计数的场景。循环中的每个对象都有非零的引用计数，但没有一个能从 root 到达。</p>
<p><strong>追踪式 GC 不关心循环。它们只关心从 roots 开始的可达性。</strong></p>
<p>有一点需要注意：markObject 函数使用了递归，这在深层对象图上会耗尽栈空间。真实的垃圾回收器使用显式的 worklist（工作列表）而不是调用栈。</p>
<h2>现代 GC 实现</h2>
<p>上面的玩具收集器为了整个标记和清除过程停止了世界。现代 GC 已经进化到在应用程序持续运行的同时并发完成大部分工作。</p>
<h3>Go: 三色并发标记-清除 (Tri-Color Concurrent Mark-and-Sweep)</h3>
<p>Go 的垃圾回收器是非分代的、非整理的且并发的。它不按年龄区分对象，也不在内存中移动对象。其重点是保持低停顿时间。</p>
<p>收集器使用三色抽象（tri-color abstraction）进行并发标记。每个对象处于三种状态之一：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-9.png" alt="" /></p>
<ul>
<li><strong>White (白色)</strong>: 尚未访问。标记结束时仍为白色的任何东西都是垃圾。</li>
<li><strong>Grey (灰色)</strong>: 已访问，但其子节点尚未全部扫描。遍历的前沿（frontier）。</li>
<li><strong>Black (黑色)</strong>: 已访问，所有子节点已扫描。确定存活。</li>
</ul>
<p>收集器开始时将所有对象设为白色，然后将 roots 设为灰色，并处理灰色对象直到不再剩余。所有仍为白色的内容都被清除。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-10.png" alt="" /></p>
<pre><code>开始: 所有对象为白色，roots 为灰色

步骤 1: 选取一个灰色对象，扫描其子节点
        - 将子节点标为灰色
        - 将扫描过的对象标为黑色

步骤 2: 重复直到没有灰色对象剩余

步骤 3: 所有白色对象都是垃圾

示例:

  Roots: [A]

  开始:      A(grey) --&gt; B(white) --&gt; D(white)
             A(grey) --&gt; C(white)

  扫描 A:    A(black) --&gt; B(grey) --&gt; D(white)
             A(black) --&gt; C(grey)

  扫描 B:    A(black) --&gt; B(black) --&gt; D(grey)
             A(black) --&gt; C(grey)

  扫描 C:    A(black) --&gt; B(black) --&gt; D(grey)
             A(black) --&gt; C(black)

  扫描 D:    A(black) --&gt; B(black) --&gt; D(black)
             A(black) --&gt; C(black)

  结果: 任何剩余的白色对象都是垃圾并被释放
</code></pre>
<p>难点在于应用程序在收集器遍历时持续运行并修改指针。这造成了一个需要仔细处理的正确性问题。</p>
<p>收集器认为黑色对象已完成。一旦对象变黑，收集器就不会再扫描它。它的所有子节点都已被访问并设为灰色。但是，如果应用程序在收集器仍在运行时，将一个指向白色对象的指针写入黑色对象，收集器就有麻烦了。黑色对象已经处理完了。该白色对象也无法从任何灰色对象触达。当标记阶段结束并清除运行时，该白色对象将被释放，即便有一个存活的黑色对象指向它。</p>
<p>这被称为 <strong>tricolor invariant</strong>（三色不变性）：黑色对象绝不能直接指向白色对象。如果发生了这种情况，白色对象对收集器是不可见的，会被错误释放。write barrier（写屏障）的存在专门用于在并发标记期间应用程序修改对象图时维护这一不变性。</p>
<p>Go 通过 <strong>hybrid write barrier</strong>（混合写屏障，Go 1.8 引入）解决了这个问题。要理解它为什么有效，看看它结合的两种旧屏障会有所帮助。</p>
<p><strong>Dijkstra’s 插入屏障 (1978)</strong>：每当一个指针被写入对象时，将新的被引用者设为灰色。如果一个黑色对象存储了对白色对象的引用，该白色对象会在收集器错过它之前变灰。这维护了三色不变性。</p>
<p>问题在于 goroutine 栈与堆对象不同。编译器在堆指针写入处注入写屏障，例如写入结构体字段或切片元素。栈写入是局部变量赋值，编译器对其分别处理。在每一个局部变量赋值上放屏障会使函数调用和基本操作变得极其昂贵，所以屏障不覆盖它们。这意味着在并发标记期间，goroutine 可以自由地将指向白色对象的指针写入局部变量，而没有屏障触发。收集器不知道发生了这事。</p>
<p>为了修复这一点，在并发标记结束时，Go 曾经必须停止世界并从头重新扫描每个 goroutine 的整个栈。重新扫描时发现的任何指向白色对象的指针都会变灰，防止它们被错误释放。此步骤的停顿时间随着 goroutine 数量和其栈大小而增加。拥有成千上万个 goroutine 的程序可能会看到数毫秒的 STW 停顿，仅仅是为了这次重新扫描。这是 Go 1.8 之前主要的 STW 停顿来源。</p>
<p><strong>Yuasa’s 删除屏障 (1990)</strong> 采取相反的方法：每当一个指针即将被覆盖时，在旧引用消失前将其变灰。这确保了在标记开始时可达的任何东西直到结束都保持可达，即便应用程序在标记期间丢弃了它的引用。缺点是标记期间死亡的一些对象会存活到下一个周期（floating garbage，浮动垃圾），因为屏障保守地让它们活着。</p>
<p><strong>Go 的混合屏障</strong>结合了两者。在堆写入时，它同时应用两种屏障：将旧引用变灰（Yuasa）并将新引用变灰（Dijkstra）。在栈写入时，不运行屏障，但栈上新分配的对象开始时就是黑色而不是白色。这种组合赋予了收集器足够强的不变性，使其在标记结束时永远不需要重新扫描栈。STW 停顿从几十毫秒降到了不到一毫秒。</p>
<pre><code>// 混合屏障在堆指针写入时的逻辑:
// *slot = new_ptr

shade(*slot)   // 将旧引用变灰 (Yuasa: 不要丢掉之前在那里的内容)
shade(new_ptr) // 将新引用变灰 (Dijkstra: 不要错过新到来的内容)
*slot = new_ptr
</code></pre>
<p>这就是并发垃圾回收的吞吐量成本：标记阶段的每一次堆指针写入都要运行此 shade 逻辑。单次操作开销虽小，但在高分配速率下会累积。权衡的结果是你获得了亚毫秒级的 STW 停顿，而不是几十毫秒。</p>
<p>Go 仅简短地停止世界以扫描 goroutine 栈并切换写屏障的开关。实际的标记和清除与应用程序并发进行。</p>
<p><strong>No compaction (无整理)。</strong> Go 在分配后不移动对象。相反，Go 使用 tcmalloc 风格的分配器，将内存划分为 size classes（大小类），并从每个处理器的缓存（per-processor caches）中分配。对象被分组为固定的大小类（8 字节、16 字节、32 字节，最高达 32 KB）。分配时从空闲列表中选取合适大小的槽。这减少了碎片而无需移动对象，但并不能完全消除碎片。</p>
<p><strong>No generational collection (无分代收集)。</strong> Go 团队的理由是，考虑到 Go 典型的带有 goroutine 和并发工作负载的分配模式，分代 GC 增加的复杂性（用于跟踪老到新指针的写屏障、提升逻辑、分代大小调优）带来的收益是不确定的。Go 通过使其并发标记器足够快来补偿，从而使额外的回收频率变得可以接受。</p>
<p><strong>关键里程碑：</strong></p>
<ul>
<li>Go 1.5 (2015)：引入并发 GC。在此之前，Go 使用全停顿收集器，停顿时间达 10-100ms 或更多。此版本使 Go 能够胜任延迟敏感型服务。</li>
<li>Go 1.8 (2017)：混合写屏障。降低了在并发标记期间维护三色不变性的开销。</li>
<li>Go 1.19 (2022)：GOMEMLIMIT。使 Go 程序能在容器环境的内存预算内工作。</li>
</ul>
<p><strong>GOGC 调节旋钮。</strong> Go 提供了一个主要的调优参数：GOGC。它控制在下一次 GC 触发之前堆可以增长多少。默认值是 100，意味着当堆自上次回收以来翻倍时触发 GC。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-11.png" alt="" /></p>
<pre><code>GOGC=100 (默认):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 100/100) = 1000MB

GOGC=50 (更激进):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 50/100) = 750MB

GOGC=200 (较保守):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 200/100) = 1500MB
</code></pre>
<p>更低的 GOGC 意味着更频繁的回收（更低的内存占用，更高的 CPU 开销）。更高的 GOGC 意味着较少的回收（更高的内存占用，更低的 CPU 开销）。</p>
<p>Go 1.19 增加了 GOMEMLIMIT，这是一个软内存限制。在具有硬性内存预算的容器环境中，GOMEMLIMIT 告诉 GC pacer（步调算法）在内存使用接近限制时变得更加激进。</p>
<p><strong>亲自尝试：</strong></p>
<pre><code>package main

import (
    "fmt"
    "runtime"
    "time"
)

var longLived []*[1024 * 1024]byte

func main() {
    fmt.Println("Go version:", runtime.Version())

    for round := 0; round &lt; 50; round++ {
        // 短寿对象: 分配小对象，让它们死亡
        for i := 0; i &lt; 5000; i++ {
            _ = make([]byte, 1024)
        }

        // 长寿对象: 每 10 轮保留一个
        if round%10 == 0 {
            arr := new([1024 * 1024]byte)
            longLived = append(longLived, arr)
        }

        time.Sleep(50 * time.Millisecond)
    }

    var stats runtime.MemStats
    runtime.ReadMemStats(&amp;stats)
    fmt.Printf("Total GC cycles: %d\n", stats.NumGC)
    fmt.Printf("Total STW pause: %v\n", time.Duration(stats.PauseTotalNs))
    fmt.Printf("Long-lived objects: %d\n", len(longLived))
}
</code></pre>
<p>运行并开启 GC 追踪：</p>
<pre><code>GODEBUG=gctrace=1 go run gcdemo.go
</code></pre>
<p>观察输出内容：</p>
<pre><code>gc 1 @0.011s 1%: 0.044+0.56+0.13 ms clock, 0.62+0.21/0.57/0+1.8 ms cpu, 3-&gt;4-&gt;0 MB, 4 MB goal, 0 MB stacks, 0 MB globals, 14 P
</code></pre>
<p>从左到右阅读：</p>
<ul>
<li>gc 1: GC 周期编号</li>
<li>@0.011s: 自程序启动的时间</li>
<li>1%: 到目前为止 GC 消耗的 CPU 百分比</li>
<li>
<p>0.044+0.56+0.13 ms clock: GC 周期的三个阶段：STW 标记开始 (0.044ms) + 并发标记和扫描 (0.56ms) + STW 标记结束 (0.13ms)。STW 停顿是 clock 字段中的第一个和第三个数字。在此例中，应用程序被冻结的总墙钟时间是 0.044 + 0.13 = 0.174ms。中间的 0.56ms 是并发的：你的应用程序一直在运行。在 Go 中，STW 停顿通常在 1ms 以下，往往远低于 0.1ms。</p>
</li>
<li>
<p>0.62+0.21/0.57/0+1.8 ms cpu: CPU 时间细目。格式为：STW-开始 + 辅助/背景/空闲 + STW-结束。每个数字代表：</p>
<ul>
<li>0.62ms — STW 标记开始时所有核心的 CPU 总时间。高于墙钟时间 (0.044ms)，因为 Go 会在多个核心上并行化初始栈扫描。</li>
<li>0.21ms — 应用程序 goroutine 执行 mutator assists（赋值器辅助）所花费的 CPU 时间。当某个 goroutine 分配速度超过 GC 处理速度时，它会被“征税”，必须在允许其分配之前自己做一些标记工作。</li>
<li>0.57ms — 专用背景 GC 工作 goroutine 执行并发标记所使用的 CPU 时间。</li>
<li>0 — 空闲 GC 工作者的 CPU 时间（仅在调度器没有其他任务运行时才领取 GC 任务的 goroutine）。此处为零意味着专用工作者处理了所有事情。</li>
<li>1.8ms — STW 标记结束时所有核心的 CPU 总时间。高于墙钟 (0.13ms)，因为多个核心并行工作以排空剩余任务并禁用写屏障。</li>
</ul>
</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-12.png" alt="" /></p>
<p>当多个核心并行工作时，CPU 时间可以超过墙钟时间。并发阶段的 CPU 时间可能少于墙钟时间，因为 GC 与你的应用程序共享核心。</p>
<ul>
<li>3->4->0 MB: GC 开始时的堆大小、GC 触发点的堆大小、GC 完成后的存活堆大小</li>
<li>4 MB goal: 下次 GC 触发前的目标堆大小（基于 GOGC 和当前存活堆）</li>
<li>0 MB stacks: goroutine 栈使用的内存</li>
<li>0 MB globals: 标记期间扫描的全局变量使用的内存</li>
<li>14 P: 逻辑处理器数量 (GOMAXPROCS)</li>
</ul>
<h3>Java: G1GC (Garbage First Collector)</h3>
<p>G1GC 自 JDK 9 以来一直是 Java 的默认垃圾回收器。它是一个分代的、基于区域（region）的收集器。它进行追踪、标记和整理，但它是增量式进行的，而不是一次性完成。</p>
<p><strong>Region layout (区域布局)。</strong> G1 将堆划分为大小相等的区域，通常每个区域为 1MB 到 32MB，取决于堆的大小。每个区域在任何时候扮演四种角色之一：Eden（伊甸园）、Survivor（幸存者）、Old（老年代）或 Humongous（巨型对象，用于超过半个区域大小的对象）。区域的角色可以在不同回收周期之间改变。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-13.png" alt="" /></p>
<p><strong>Young collection (次要 GC)。</strong> Eden 区域填满。G1 停止世界，使用并行多线程标记器标记 Eden 和 Survivor 区域中的存活对象，将幸存者拷贝到新的 Survivor 区域或提升到 Old 区域，并完全丢弃旧的 Eden 区域。这是一个并行的 STW 停顿，但很短，因为年轻代区域较小且年轻对象大多已死。</p>
<p><strong>Mixed collection (混合回收)。</strong> G1 周期性地运行并发标记周期，以找出哪些老年代区域包含的垃圾最多。然后运行混合回收：同时疏散（evacuating）年轻代区域和最具“盈利价值”的老年代区域。这就是“Garbage First”名称的由来。G1 总是优先选取垃圾密度最高的老年代区域，从而在单位停顿时间内实现最大的回收量。</p>
<p><strong>SATB (Snapshot-At-The-Beginning，起始快照)。</strong> 在并发标记期间，应用程序持续运行并修改对象图。G1 使用 SATB 维护正确性。在标记开始时，G1 对哪些对象存活进行逻辑快照。该快照中存活的对象在此周期被视为存活，即使应用程序在标记期间丢弃了它们。写屏障将修改字段的旧值记录到 SATB 队列中。这种做法是保守的（一些垃圾会存活到下个周期），但是正确的。</p>
<pre><code>并发标记正在运行。应用程序执行：
  obj.field = null   (原本指向 X)

没有 SATB: X 可能没有其他引用，未被标记，在使用中被释放。
有 SATB:    写屏障记录“此处曾有 X”，将 X 标为灰色。安全。
</code></pre>
<p><strong>Pause target (停顿目标)。</strong> 你可以通过 -XX:MaxGCPauseMillis 配置 G1 的目标最大停顿时间。默认值是 200ms。G1 通过调整区域数量、回收集合大小和时机，尝试将停顿保持在目标范围内。它并不总是能成功，特别是在 Full GC 期间，但它是主要的调优旋钮。</p>
<p><strong>亲自尝试：</strong></p>
<pre><code>import java.util.ArrayList;
import java.util.List;

public class GCDemo {
  static List&lt;byte[]&gt; longLived = new ArrayList&lt;&gt;();

  public static void main(String[] args) throws InterruptedException {
    System.out.println("Starting GC demo...");

    for (int round = 0; round &lt; 50; round++) {
      // 短寿对象：创建并立即丢弃
      for (int i = 0; i &lt; 1000; i++) {
        byte[] tmp = new byte[10 * 1024]; // 每个 10KB
      }

      // 长寿对象：保留一些对象以构建老年代
      if (round % 5 == 0) {
        longLived.add(new byte[1024 * 1024]); // 1MB
      }

      Thread.sleep(50);
    }

    System.out.println("Done. Long-lived objects: " + longLived.size());
  }
}
</code></pre>
<p>使用 G1GC 日志运行：</p>
<pre><code># 编译
javac GCDemo.java

# 使用 G1GC (Java 9+ 默认) 并开启 GC 日志
java -Xmx256m \
     -XX:+UseG1GC \
     "-Xlog:gc*:file=gc_g1.log:time,uptime,level,tags" \
     GCDemo

# 或者，使用简洁的一行输出
java -Xmx256m -Xlog:gc GCDemo
</code></pre>
<p>观察日志：</p>
<pre><code>[0.005s][info][gc] Using G1
[0.135s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 26M-&gt;3M(256M) 0.644ms
[0.812s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 132M-&gt;7M(256M) 0.707ms
[1.710s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 165M-&gt;13M(256M) 1.019ms
[2.528s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 171M-&gt;19M(256M) 0.964ms
</code></pre>
<p>阅读日志：</p>
<ul>
<li>Using G1: 确认 G1GC 是活跃收集器</li>
<li>Pause Young (Normal): 回收 Eden 和 Survivor 区域的次要 GC</li>
<li>G1 Evacuation Pause: G1 正在将存活对象从回收区域拷贝（疏散）到新区域</li>
<li>26M->3M(256M) 0.644ms: 堆之前是 26MB，之后是 3MB，总堆容量 256MB，停顿耗时 0.644ms</li>
<li>在 2.5 秒的运行时中进行了四个 GC 周期，每个周期在 1.1ms 内完成。大多数分配的对象都是短寿的，并在年轻代被回收。</li>
</ul>
<h3>Java: ZGC (Z Garbage Collector)</h3>
<p>ZGC 自 Java 11 起可用，并在 Java 15 中达到生产就绪状态。扩展了分代收集的 Generational ZGC 在 Java 21 中引入。ZGC 的目标是无论堆大小如何（包括数百 GB 的堆），停顿时间均保持在亚毫秒级。</p>
<p>G1 在年轻代回收时停顿较短，但随着堆的增长，在并发标记设置和混合回收期间会有更长的停顿。ZGC 的方法不同：它几乎将所有工作（标记、重定位、引用处理）并发进行，将 STW 工作降至最低。</p>
<p><strong>Colored pointers (有色指针)。</strong> ZGC 直接在指针位中编码 GC 元数据。在 64 位平台上，指针宽度为 64 位，但你实际上并不需要所有 64 位来寻址内存。2^42 就能给你 4TB 的可寻址空间，这超出了大多数应用程序的使用范围。这使得每个指针中留有超过 20 位空闲。ZGC 重新利用其中一些空闲位，直接在指针内部存储 GC 状态。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-14.png" alt="" /></p>
<p>每个元数据位都有特定用途：</p>
<ul>
<li>
<p><strong>M0 和 M1 (标记位)：</strong> 用于跟踪对象是否已被标记为存活。ZGC 在每个 GC 周期中交替使用 M0 和 M1。在周期 1，收集器对每个可达对象设置 M0。在周期 2，它改用 M1。这样收集器就能区分“本周期标记”和“上个周期标记”，而无需在周期之间清除所有标记位。</p>
</li>
<li>
<p><strong>Remap (R，重映射)：</strong> 此位跟踪在对象重定位（relocated）后指针是否已更新。在并发重定位期间，ZGC 将对象移动到新地址，但并不立即更新堆中的每一个指针。相反，它保留旧指针，并使 remap 位处于未设置状态。当应用程序加载这些过时指针之一时，load barrier（读屏障/加载屏障）会注意到 remap 位未设置，并对其进行修正。</p>
</li>
<li>
<p><strong>Finalizable (F)：</strong> 表示该对象具有一个需要在释放前运行的 finalizer。</p>
</li>
</ul>
<p>巧妙之处在于元数据随指针移动。GC 不需要一个单独的侧表来查找对象的 GC 状态。每个指针都已经携带了它。</p>
<p><strong>Load barriers (加载屏障)。</strong> 每次应用程序从堆加载引用时，ZGC 都会插入一个加载屏障。屏障检查指针的颜色位，如果它们不处于预期状态，则采取行动。</p>
<p>以下是实际操作中的情况。假设收集器在并发重定位阶段将一个对象从地址 0&#215;1000 移动到了 0&#215;2000。应用程序仍然持有一个地址为 0&#215;1000 且 remap 位未设置的指针。</p>
<pre><code>应用程序代码:
  Object x = obj.field;

实际执行的内容:
  raw_ptr = load obj.field           // raw_ptr = 0x1000, remap bit = 0
  if (raw_ptr.color != expected) {   // remap bit 为 0, expected 为 1 → 进入 slow path
      new_addr = forwarding_table[0x1000]  // 查找: 对象已移动到 0x2000
      raw_ptr = set_address(raw_ptr, 0x2000)
      raw_ptr = set_remap_bit(raw_ptr)
      obj.field = raw_ptr            // 就地修正指针，以便下次使用
  }
  x = raw_ptr                       // x 现在指向 0x2000
</code></pre>
<p>下次任何线程加载 obj.field 时，remap 位已经设置好了。屏障检查通过 fast path，没有额外工作。过时指针在第一次访问时被惰性修正。</p>
<p>这是关键机制。与其像 G1 在疏散期间那样让 GC 停止世界以一次性更新所有指向重定位对象的指针，ZGC 让应用程序在遇到指针时逐个修正。代价是每次指针加载都要支付屏障检查的开销，即便没有任何东西被重定位。在实践中，fast path（检查几位）执行代价足够小，与避免 STW 重定位停顿带来的收益相比，开销很小。</p>
<p><strong>Concurrent relocation (并发重定位)。</strong> G1 停止世界以将存活对象从回收区域中疏散。ZGC 在应用程序运行的同时重定位对象。它能做到这一点是因为加载屏障处理了指针修正。在启动和结束每个阶段（标记开始、标记结束、重定位开始）时有简短的 STW 停顿，但这些通常远低于 1ms。拷贝对象和修正指针的实际工作是并发发生的。</p>
<p><strong>Generational ZGC (Java 21+)。</strong> 最初的 ZGC 不按年龄划分堆。分代 ZGC 增加了年轻代和老年代，同时保留了亚毫秒级停顿的保证。它更频繁地回收年轻区域（垃圾最多的地方），较少回收老年代区域。加载屏障和有色指针机制被扩展以处理分代写屏障。</p>
<p><strong>何时使用 ZGC vs G1：</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-15.png" alt="" /></p>
<p><strong>亲自尝试：</strong></p>
<pre><code># 使用 ZGC 运行
java -Xmx256m \
     -XX:+UseZGC \
     "-Xlog:gc*:file=gc_zgc.log:time,uptime,level,tags" \
     GCDemo

# 使用分代 ZGC (Java 21+)
java -Xmx256m \
     -XX:+UseZGC -XX:+ZGenerational \
     -Xlog:gc \
     GCDemo
</code></pre>
<p>观察日志：</p>
<pre><code>[0.318s] GC(0) Garbage Collection (Warmup) 28M(11%)-&gt;12M(5%)
[0.321s] GC(0) Pause Mark Start 0.023ms
[0.489s] GC(0) Concurrent Mark 168.123ms
[0.491s] GC(0) Pause Mark End 0.019ms
[0.492s] GC(0) Concurrent Select Relocation Set 1.234ms
[0.502s] GC(0) Concurrent Relocate 10.456ms
</code></pre>
<p>STW 停顿是标记为“Pause”的行。其他所有内容都是并发的。将此处的停顿持续时间与 G1 的输出进行对比。</p>
<h3>Python: 引用计数加循环 GC</h3>
<p>CPython（Python 的参考实现）是“追踪式收集器占主导”模式的主要例外。它使用引用计数作为主要机制，并在之上增加了一层用于追踪循环引用的检测器。</p>
<p><strong>CPython 中的引用计数。</strong></p>
<p>每个 Python 对象都有一个 ob_refcnt 字段。Python 的 C API 在 Py_INCREF 时增加，在 Py_DECREF 时减少。当计数归零时，对象在 _Py_Dealloc 中被立即释放。这赋予了 Python 确定性的销毁：<strong>del</strong> 方法和上下文管理器的 <strong>exit</strong> 调用在最后一个引用掉落的那一刻发生。</p>
<pre><code>import sys

x = []
print(sys.getrefcount(x))  # 2: 1个来自x，1个来自getrefcount参数本身的临时引用

y = x
print(sys.getrefcount(x))  # 3: 1个x, 1个y, 1个getrefcount参数

del y
print(sys.getrefcount(x))  # 2: 回到1个x, 1个getrefcount参数
</code></pre>
<p><strong>循环引用问题。</strong> 仅靠引用计数无法回收循环垃圾。</p>
<pre><code>import gc

# 创建循环引用
class Node:
    def __init__(self, name):
        self.name = name
        self.ref = None

a = Node("A")
b = Node("B")
a.ref = b
b.ref = a   # cycle: A -&gt; B -&gt; A

# a 和 b 的计数都 &gt;= 1（由于相互引用）。
# 仅靠引用计数，两者都不会被释放。

del a
del b
# a 和 b 依然存活！Refcount: A 为 1 (来自 b.ref), B 为 1 (来自 a.ref)

# 显式触发循环检测器
collected = gc.collect()
print(f"Collected {collected} objects")  # 收集了 4 个对象 (2个node + 2个dict)
</code></pre>
<p>引用计数处理了常见情况，但它无法收集循环引用。CPython 的答案是运行在引用计数系统之上的独立循环检测器。其实现在 Modules/gcmodule.c 中。</p>
<p>循环检测器是一个追踪式收集器，但它并不追踪整个堆。它仅跟踪能够参与循环引用的对象：如列表、字典、集合及用户自定义类实例等容器对象。字符串和整数无法持有对其他对象的引用，因此无需跟踪它们。</p>
<p>与 Java 的收集器一样，循环检测器使用分代方法。共有三代，编号为 0、1 和 2。思路与我们之前讨论的分代假说相同：大多数对象死得早，所以经常检查年轻对象，少打扰老对象。默认阈值硬编码在 CPython 的 <a href="https://github.com/python/cpython/blob/v3.9.6/Modules/gcmodule.c#L137">Modules/gcmodule.c</a> 中：</p>
<pre><code>struct gc_generation generations[NUM_GENERATIONS] = {
    /* PyGC_Head,                                    threshold,    count */
    { {(uintptr_t)_GEN_HEAD(0), (uintptr_t)_GEN_HEAD(0)},   700,        0},
    { {(uintptr_t)_GEN_HEAD(1), (uintptr_t)_GEN_HEAD(1)},   10,         0},
    { {(uintptr_t)_GEN_HEAD(2), (uintptr_t)_GEN_HEAD(2)},   10,         0},
};
</code></pre>
<p>你可以验证你的运行时实际使用的是什么：</p>
<pre><code>python3 -c "import gc; print(gc.get_threshold())"
# (700, 10, 10)
</code></pre>
<p>请注意，某些框架和发行版会在启动时通过 gc.set_threshold() 覆盖这些默认值，因此你的环境可能显示不同的值。</p>
<p>第 0 代持有新分配的容器对象。当自上次回收以来的新分配数量超过阈值（默认 700）时，回收第 0 代。幸存的对象被提升到第 1 代。在第 0 代被回收 10 次后，第 1 代被回收一次。幸存者移至第 2 代。在第 1 代被回收 10 次后，第 2 代被回收一次。</p>
<p>效果是第 0 代大约每 700 次分配回收一次，第 1 代大约每 7,000 次，第 2 代大约每 70,000 次。进入第 2 代的长寿对象几乎永远不会被打扰。检测器将其大部分时间花在最年轻的对象上，这些对象最有可能最近变成了垃圾。</p>
<p>你可以看到这些计数：</p>
<pre><code>import gc

# 当前各代阈值
print(gc.get_threshold())  # (700, 10, 10)

# 当前分配计数: (gen0分配, 自上次gen1回收以来的gen0回收数, 自上次gen2回收以来的gen1回收数)
print(gc.get_count())  # 例如 (342, 8, 2)

# 强制进行全量回收
gc.collect()

# 完全禁用循环检测器 (如果你确定代码中没有循环引用)
gc.disable()
</code></pre>
<p>当检测器在某一代码代上运行时，它需要找出哪些对象仅被循环引用保持存活。通过一个例子更容易理解算法。</p>
<p>假设检测器正在查看三个被跟踪的对象：X、Y 和 Z。X 指向 Y 和 Z。Y 指回 X。还有一个局部变量持有对 X 的引用。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-16.png" alt="" /></p>
<p>步骤 1：拷贝引用计数。X=2, Y=1, Z=1。</p>
<p>步骤 2：减去内部引用。Y 指向 X，所以从 X 的副本中减 1 (X 从 2 变为 1)。X 指向 Y，所以从 Y 的副本中减 1 (Y 从 1 变为 0)。X 指向 Z，所以从 Z 的副本中减 1 (Z 从 1 变为 0)。</p>
<p>步骤 3：检查剩余部分。X 的调整后计数为 1。被跟踪集合之外的某些东西（局部变量）仍然指向它。X 存活。Y 和 Z 虽然调整后计数为 0，但它们可以从 X 到达，因此它们也幸存下来。</p>
<p>现在想象局部变量消失了。X 的引用计数掉到 1 (只有 Y 指向它)。运行相同算法：拷贝 X=1, Y=1, Z=1。减去内部引用：X 变为 0, Y 变为 0, Z 变为 0。每个调整后的计数都是零。被跟踪集合之外没有任何东西指向它们。它们仅因彼此而存在。三者都是垃圾。</p>
<p>这就是核心思想。算法寻找那些存在的唯一理由是同一集合中其他对象的目标。</p>
<p>有一个边缘案例困扰了多年：finalizers（终结器）。</p>
<p>终结器是运行时在对象被销毁前调用的方法，给予其清理外部资源（如文件句柄或网络连接）的机会。在 Python 中，这就是 <strong>del</strong> 方法。</p>
<p>假设 A 和 B 处于循环中，且两者都有 <strong>del</strong> 方法。检测器知道它们是垃圾，但要释放它们，它需要打破循环。问题是：哪个 <strong>del</strong> 先运行？如果你先运行 A 的终结器，而它尝试使用 B，但 B 已经正在被销毁，你就会崩溃。如果你先运行 B 的，而它使用 A，同样的问题。没有安全的顺序。</p>
<p>在 Python 3.4 之前，CPython 直接放弃处理这些对象。它将它们放入名为 gc.garbage 的列表中，且永远不释放它们。如果你的代码创建了带有 <strong>del</strong> 的循环引用，你就会有一个静默的内存泄漏。PEP 442 通过在打破任何引用之前调用终结器修复了这个问题。当 A 和 B 的 <strong>del</strong> 运行时，两者都保持完整。只有在所有终结器执行完毕后，检测器才会打破循环并释放对象。</p>
<p>关于 CPython 的内存模型还有一件事值得了解。每当 Python 执行类似 x = some_object 的操作时，它会增加 some_object 的引用计数（C 语言中的 Py_INCREF）。每当变量超出作用域时，它减少计数 (Py_DECREF)。在 C 中这些是普通的整数操作：refcount += 1, refcount -= 1。没有锁，没有原子指令。</p>
<p>在多线程程序中，这是一个问题。两个线程可能同时增加同一个对象的引用计数。如果没有同步，一个增加操作会丢失（经典的竞态条件），之后该对象可能会在有人仍在使用时被释放。</p>
<p>GIL (全局解释器锁) 防止了这种情况。一次只有一个线程执行 Python 字节码，因此两个线程永远不会同时修改同一个引用计数。GIL 免费使所有引用计数操作变得安全，而无需任何原子指令。</p>
<p>这也是移除 GIL 如此困难的原因。如果拿掉它，整个代码库中的每一个 Py_INCREF 和 Py_DECREF 都需要变成原子操作。原子操作比普通整数增量要昂贵得多。Python 3.13 开始附带实验性的 free-threaded 模式，它使用 biased reference counting（偏向引用计数）来降低这种成本：创建对象的线程可以对引用计数进行廉价的非原子更新，只有访问该对象的其他线程才支付原子操作的代价。</p>
<h2>映射回 Wilson：全景图</h2>
<p>每一种现代垃圾回收器都可以映射回 Wilson 在 1992 年描述的两个家族。它们之间的区别在于关于如何最小化停顿、处理并发以及高效管理内存的工程决策。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-17.png" alt="" /></p>
<p>从这一对比中可以观察到几点：</p>
<p><strong>Wilson 的追踪式家族在服务器运行时占据主导地位。</strong> 引用计数用于 Swift、Python 和 Rust 的 Arc，但对于具有高分配速率的托管运行时，追踪式收集器是标准做法。循环引用问题无论如何都需要补充追踪步骤，这增加了复杂性，且无法消除每次修改时的引用计数开销。</p>
<p><strong>分代收集除 Go 以外随处可见。</strong> Java 重度利用了分代假说。Python 的循环检测器使用了三代。Go 最初选择不使用分代收集，因为跨代指针写屏障的开销对 Go 的典型工作负载来说不划算。这种情况可能正在改变：最近的 Go 版本中已经开发了实验性的分代支持。</p>
<p><strong>Compaction (整理) vs No compaction 是一个真正的设计分歧点。</strong> Java 收集器进行整理，这允许 bump-pointer 分配（非常快）并消除碎片。Go 不整理，这意味着它永远不需要更新指向已移动对象的指针（更简单的写屏障，无需读屏障以保证正确性）。Go 通过大小类分配器（size-class allocator）来补偿。这是经典的 Wilson 权衡：拷贝和整理收集器以内存开销和指针更新成本换取分配速度和碎片消除。</p>
<p><strong>ZGC 的有色指针是 Wilson 指针标记 (pointer-tagging) 思想的现代实现。</strong> Wilson 提到过在指针中使用位来存储 GC 元数据。ZGC 将此进一步发展，将标记状态、重映射状态和终结状态直接嵌入 64 位指针。在每次指针加载时检查这些位的加载屏障是 ZGC 为亚毫秒级停顿支付的代价。</p>
<p><strong>基本问题从未改变。</strong> 从 roots 开始追踪，标记存活内容，回收其余部分。自 1960 年以来的所有发展都是对 McCarthy 原始洞察的工程改进。</p>
<h2>参考资料</h2>
<ul>
<li><a href="https://dl.acm.org/doi/10.1145/367177.367199">McCarthy, J. (1960). Recursive functions of symbolic expressions and their computation by machine, Part I</a></li>
<li><a href="https://www.cs.rice.edu/~javaplt/311/Readings/wilson92uniprocessor.pdf">Wilson, P. R. (1992). Uniprocessor Garbage Collection Techniques. IWMM ‘92</a></li>
<li><a href="https://tip.golang.org/doc/gc-guide">A Guide to the Go Garbage Collector</a></li>
<li><a href="https://go.dev/blog/ismmkeynote">Getting to Go: The Journey of Go’s Garbage Collector</a></li>
<li><a href="https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md">Proposal: Eliminate STW stack re-scanning &#8211; Austin Clements (2016)</a></li>
<li><a href="https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html">Java Garbage Collection: The G1 Garbage Collector</a></li>
<li><a href="https://openjdk.org/jeps/333">ZGC: The Z Garbage Collector &#8211; JEP 333</a></li>
<li><a href="https://openjdk.org/jeps/439">Generational ZGC &#8211; JEP 439</a></li>
<li><a href="https://peps.python.org/pep-0442/">PEP 442: Safe object finalization</a></li>
</ul>
<hr />
<p><strong>你的“停顿”时刻</strong></p>
<p>GC 的艺术在于平衡。在你的开发生涯中，是否遇到过因为 GC 停顿导致的生产事故？你是倾向于 Go 的极致低延迟，还是 Java G1GC 的高吞吐？<br />
欢迎在评论区分享你的调优经历或吐槽！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2026 编程语言“饱和度”榜单出炉：JavaScript/Python 已“烂大街”，Go/Rust 成最大赢家？</title>
		<link>https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners/</link>
		<comments>https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners/#comments</comments>
		<pubDate>Thu, 02 Apr 2026 00:08:43 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AIEra]]></category>
		<category><![CDATA[AI时代]]></category>
		<category><![CDATA[Architecturalthinking]]></category>
		<category><![CDATA[Datadriven]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HorizontalCompetition]]></category>
		<category><![CDATA[Involution]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[ProgrammingLanguages]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[SaturationRankings]]></category>
		<category><![CDATA[Shipping]]></category>
		<category><![CDATA[SupplyDemand]]></category>
		<category><![CDATA[SurvivorBias]]></category>
		<category><![CDATA[SystemArchitect]]></category>
		<category><![CDATA[TechDividend]]></category>
		<category><![CDATA[TechStack]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[UnderlyingPrinciples]]></category>
		<category><![CDATA[VerticalDeepening]]></category>
		<category><![CDATA[交付]]></category>
		<category><![CDATA[供需关系]]></category>
		<category><![CDATA[内卷]]></category>
		<category><![CDATA[幸存者偏差]]></category>
		<category><![CDATA[底层原理]]></category>
		<category><![CDATA[技术栈]]></category>
		<category><![CDATA[技术红利]]></category>
		<category><![CDATA[数据驱动]]></category>
		<category><![CDATA[架构思维]]></category>
		<category><![CDATA[横向竞争]]></category>
		<category><![CDATA[系统架构师]]></category>
		<category><![CDATA[纵向深耕]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[饱和度榜单]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6132</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners 大家好，我是Tony Bai。 在这个技术浪潮汹涌、AI 随时可能掀翻牌桌的时代，每一个程序员心中都悬着一个终极问题： “我现在的技术栈，还能吃几年饭？” 我们每天都在焦虑地刷着各种技术文章，试图从 Google、Anthropic、OpenAI、Nvidia等的风向中，窥探下一个技术红利期。但这些信息往往零散、矛盾，甚至充满了各种培训机构的“幸存者偏差”。 就在半个多月前，X 平台上的一位技术博主 Mojisola Alegbe，基于 Stack Overflow、GitHub Trends、JetBrains 等多方数据，整理并发布了一份极其残酷的私房版《2026 编程语言“饱和度”榜单》。 这篇推文就像一颗深水炸弹，在短短几天内获得了 41.2 万的惊人阅读量。大批开发者涌入评论区，有人哀嚎，有人庆幸，有人愤怒，有人不屑。这张榜单之所以能引爆全网，因为它赤裸裸地揭示了我们这个行业最真实的“供需关系”和“内卷现状”。 今天，我们就来深度扒开这张榜单背后的血泪与真相。看看你我手中的“锤子”，到底还能敲几年钉子。 榜单冲击：你的技术栈，在鄙视链的哪一层？ 让我们先深吸一口气，看看这份令人心跳加速的榜单： JavaScript (66%): 极度饱和 (Extremely Saturated) Python (58%): 非常饱和 (Very Saturated) SQL (49%): 非常饱和 (Very Saturated) TypeScript (35-40%): 高度饱和，且仍在快速增长 Java (26%): 成熟/稳定饱和 C# (18%): 中度饱和 PHP (10-11%): 正在衰退，但仍很普遍 C++ (6-7%): 小众，但用于关键系统 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/2026-programming-language-saturation-rankings-go-rust-winners-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners">本文永久链接</a> &#8211; https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners</p>
<p>大家好，我是Tony Bai。</p>
<p>在这个技术浪潮汹涌、AI 随时可能掀翻牌桌的时代，每一个程序员心中都悬着一个终极问题：</p>
<p><strong>“我现在的技术栈，还能吃几年饭？”</strong></p>
<p>我们每天都在焦虑地刷着各种技术文章，试图从 Google、Anthropic、OpenAI、Nvidia等的风向中，窥探下一个技术红利期。但这些信息往往零散、矛盾，甚至充满了各种培训机构的“幸存者偏差”。</p>
<p>就在半个多月前，X 平台上的一位技术博主 Mojisola Alegbe，基于 Stack Overflow、GitHub Trends、JetBrains 等多方数据，整理并发布了一份极其残酷的私房版《<a href="https://x.com/yehhmisi/status/2031715243622015239">2026 编程语言“饱和度”榜单</a>》。</p>
<p>这篇推文就像一颗深水炸弹，在短短几天内获得了 <strong>41.2 万</strong>的惊人阅读量。大批开发者涌入评论区，有人哀嚎，有人庆幸，有人愤怒，有人不屑。这张榜单之所以能引爆全网，因为它赤裸裸地揭示了我们这个行业最真实的“供需关系”和“内卷现状”。</p>
<p>今天，我们就来深度扒开这张榜单背后的血泪与真相。看看你我手中的“锤子”，到底还能敲几年钉子。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/ai-engineer-gpu-introduction-course-qr.png" alt="" /></p>
<h2>榜单冲击：你的技术栈，在鄙视链的哪一层？</h2>
<p>让我们先深吸一口气，看看这份令人心跳加速的榜单：</p>
<ul>
<li>JavaScript (66%): 极度饱和 (Extremely Saturated)</li>
<li>Python (58%): 非常饱和 (Very Saturated)</li>
<li>SQL (49%): 非常饱和 (Very Saturated)</li>
<li>TypeScript (35-40%): 高度饱和，且仍在快速增长</li>
<li>Java (26%): 成熟/稳定饱和</li>
<li>C# (18%): 中度饱和</li>
<li>PHP (10-11%): 正在衰退，但仍很普遍</li>
<li>C++ (6-7%): 小众，但用于关键系统</li>
<li>Go (4-5%): 低饱和，需求增长中</li>
<li>Kotlin (4-5%): 中度小众 (安卓)</li>
<li>Swift (2%): 小型但专业的生态系统</li>
<li>Rust (2-3%): 低饱和，但正在崛起</li>
</ul>
<p>看完这张图，我猜很多人的第一反应是：</p>
<ul>
<li><strong>前端/Python 工程师</strong>：完了，彻底“烂大街”了，明天就去送外卖。</li>
<li><strong>Java 工程师</strong>：稳如老狗，任你风吹雨打，我自岿然不动。</li>
<li><strong>Go/Rust 工程师</strong>：心中窃喜，果然选对了赛道，未来可期！</li>
<li><strong>PHP 工程师</strong>：……（我 PHP 是最好的语言！）</li>
</ul>
<p>但如果事情真的这么简单，那我们这个行业也未免太无趣了。这张榜单真正有价值的地方，在于它炸出了评论区里无数资深架构师和一线开发者的“人间清醒”。</p>
<h2>社区百态：饱和、内卷与“幸存者偏差”</h2>
<p>在这张榜单的评论区，你可以看到整个技术圈最真实的生态缩影。</p>
<p><strong>阵营一：饱和焦虑派</strong></p>
<p>“完了，我刚想学编程，这可怎么办？”<br />
“怪不得现在工作这么难找……”</p>
<p><strong>阵营二：不屑一顾派</strong></p>
<p>“语言只是工具，解决问题才是关键。”<br />
“这种指标毫无意义。”</p>
<p><strong>阵营三：人间清醒派（重点看这里！）</strong></p>
<p>这部分评论，往往来自那些穿越了数个技术周期的老炮。他们的观点，破具含金量。</p>
<p>一位开发者一针见血地指出：</p>
<blockquote>
<p>“语言的饱和度是个误导性指标。真正的问题不是有多少开发者懂它，而是有多少开发者能用它构建出真正有价值的系统。”</p>
</blockquote>
<p>另一位开发者则更加直接：</p>
<blockquote>
<p>“饱和度百分比毫无意义。重要的是：你能交付吗（Can you ship）？我只看三个信号：1. 真实的生产环境部署（而不是教程）；2. 系统设计的深度（而不只是 CRUD）；3. 在压力下调试复杂问题的能力。JavaScript 饱和度 66%？那又怎样，其中 90% 的人连一个可扩展的架构都设计不出来。”</p>
</blockquote>
<p>而一位博主，更是给出了顶级玩家的“搞钱思路”：</p>
<blockquote>
<p>“聪明的开发者从不追逐‘流行’的语言，他们追逐的是‘高价值’的行业<br />
  &#8211; <strong>Python → AI</strong><br />
  &#8211; <strong>C++ → 高性能系统（游戏、金融）</strong><br />
  &#8211; <strong>Rust → 安全基础设施（区块链、操作系统）</strong><br />
  &#8211; <strong>Go → 云平台（K8s、Docker）</strong><br />
  <strong>追逐金钱，而不是追逐炒作（Follow the money, not the hype）。”</strong></p>
</blockquote>
<h2>架构师的破局之道：从“横向内卷”到“纵向深耕”</h2>
<p>扒开社区的口水战，我们可以总结出三条极其宝贵的“反内卷”生存法则。</p>
<p><strong>第一条：停止在“语言层”的低水平竞争</strong></p>
<p>如果你是一个 Python 开发者，你的核心竞争力绝对不是“比别人多会几个 itertools 的函数”。</p>
<p>评论区里的一条建议非常中肯：</p>
<blockquote>
<p>“不要只学 Python 的语法。<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=4446480684395986947#wechat_redirect">去学它底层的 C++ 和 CUDA</a>。这才是 2026 年 AI 热潮中真正值钱的地方。”</p>
</blockquote>
<p>同样的道理，如果你是一个前端开发者，让你在面试中脱颖而出的，绝不是多会几个 CSS 动画技巧，而是你对 V8 引擎的内存管理、对大规模前端项目的架构设计、对 WebAssembly 的底层原理的深刻理解。</p>
<p>饱和的永远是“表层应用”，而“底层原理”的护城河，深不见底。</p>
<p><strong>第二条：将你的技术栈，锚定在高价值的“产业赛道”</strong></p>
<p>你选择的语言，决定了你的“工具”；而你选择的行业，决定了你“工具”的价值。</p>
<p>如果你用 Go，但每天只是在写一些简单的 CRUD 业务，那你和用 PHP 的同行并没有本质区别。</p>
<p>但如果你用 Go，去深耕 <strong>Kubernetes Operator 开发、去搞 Service Mesh、去做 eBPF 的底层监控</strong>，那你将进入一个截然不同的“高价值稀缺区”。</p>
<p>对于大多数开发者来说，最好的策略不是去学一门全新的、不饱和的语言（比如 Zig 或 OCaml），而是在你现有的、最熟悉的语言生态里，找到那个与<strong>“高利润、高壁垒”</strong>行业结合最紧密的纵深方向，然后一头扎进去。</p>
<p><strong>第三条：从“语言专家”进化为“系统架构师”</strong></p>
<p>评论区里，有一个非常有趣的现象：初级开发者在讨论“哪个语言好”，而资深开发者在讨论“如何交付（Ship）”。</p>
<p>当一个系统变得复杂时，瓶颈往往早已不在于某个语言的语法特性，而在于：</p>
<ul>
<li>你如何设计一套可观测的日志与监控体系？</li>
<li>你如何在不同的微服务之间做好 <a href="https://mp.weixin.qq.com/s/MUb3r1juqWIBtzlODbbQ-Q">API 的版本管理与兼容性</a>？</li>
<li>你如何<a href="https://mp.weixin.qq.com/s/evEOYgMe2lZmG2bkpoBe5Q">设计数据库的 Schema</a>，才能在未来两年内扛住 10 倍的流量增长？</li>
</ul>
<p>这些“跨语言”的系统设计能力，才是拉开普通程序员和架构师之间收入差距的根本原因。</p>
<p><strong>语言的红利期是短暂的，而架构的复利是终身的。</strong></p>
<h2>小结：你的价值，由你定义</h2>
<p>这张“饱和度”榜单，与其说是一份“死亡通知单”，不如说是一张“体检报告”。它提醒我们，如果你安于现状，只停留在语言的表层舒适区，那么无论你现在用的是 Go 还是 Python，你都随时可能被更便宜、更年轻的开发者所取代。别忘了还有不断“蚕食”初级甚至中高级程序员工作的AI！</p>
<p>在这个充满不确定性的时代，真正的安全感，来源于：</p>
<ol>
<li><strong>向下扎根</strong>，掌握技术栈的底层原理。</li>
<li><strong>向高处走</strong>，将你的能力锚定在高价值的产业。</li>
<li><strong>向外看</strong>，建立跨越语言鸿沟的系统架构思维。</li>
</ol>
<p>不要再为“哪个语言是宇宙第一”而进行无意义的口水战了。</p>
<p>你的价值，从来不是由你用什么语言决定的，而是由你能用这门语言，解决多大、多复杂、多有价值的问题决定的。</p>
<p>资料链接：https://x.com/yehhmisi/status/2031715243622015239</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>看完这份榜单，你对自己目前的技术栈感到了焦虑，还是庆幸？在你看来，一个语言的“饱和”是危机，还是意味着更成熟的生态和机会？</p>
<p>欢迎在评论区分享你的看法！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/02/2026-programming-language-saturation-rankings-go-rust-winners/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>硬核测评：哪门语言最受 AI 宠爱？13 种语言横向对比，Go 表现如何？</title>
		<link>https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance/</link>
		<comments>https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance/#comments</comments>
		<pubDate>Mon, 09 Mar 2026 00:02:44 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[AIProgramming]]></category>
		<category><![CDATA[AI智能体]]></category>
		<category><![CDATA[AI编程]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[BorrowChecker]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[CodeGeneration]]></category>
		<category><![CDATA[CompilationSpeed]]></category>
		<category><![CDATA[DeveloperExperience]]></category>
		<category><![CDATA[DynamicLanguages]]></category>
		<category><![CDATA[FunctionalLanguages]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[InferenceCost]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[mini-git]]></category>
		<category><![CDATA[Ocaml]]></category>
		<category><![CDATA[PerformanceReview]]></category>
		<category><![CDATA[ProgrammingLanguages]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[StaticLanguages]]></category>
		<category><![CDATA[TypeChecking]]></category>
		<category><![CDATA[代码生成]]></category>
		<category><![CDATA[借用检查器]]></category>
		<category><![CDATA[函数式语言]]></category>
		<category><![CDATA[动态语言]]></category>
		<category><![CDATA[基准测试]]></category>
		<category><![CDATA[开发者体验]]></category>
		<category><![CDATA[性能测评]]></category>
		<category><![CDATA[推理成本]]></category>
		<category><![CDATA[类型检查]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[编译速度]]></category>
		<category><![CDATA[静态语言]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6010</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance 大家好，我是Tony Bai。 随着 Claude Code、Gemini Cli、Codex 等 AI 编程工具的全面普及，“让 AI 写代码”已经从极客的玩具变成了日常的生产力。随之而来的是一个触及灵魂的问题：哪种编程语言最适合交给 AI 去写？ 作为 Gopher，我们一直为 Go 语言的“极简语法”、“极速编译”和“强类型安全”感到自豪。我们理所当然地认为，这种没有任何隐式魔法、像白开水一样的语言，绝对是 LLM 的最爱。 然而，现实总是比直觉更骨感。近日，Ruby 核心提交者 Yusuke Endoh（@mame）发布了一份名为 ai-coding-lang-bench 的硬核定量测评报告。他使用 Claude Code（Opus 4.6 模型）对 13 种主流编程语言进行了系统性横向对比。 在这场涵盖了动态语言、静态语言和函数式语言的混战中，Go 语言的表现究竟如何？ 是力压群雄，还是黯然失色？那些备受人类推崇的静态类型系统，在 AI 面前是否成了累赘？ 本文和大家一起阅读和拆解这份报告，为你揭晓 AI 时代的语言偏好图谱。 实验设计：让 AI 写一个 Mini-Git 在评价这份报告之前，我们先来看看它的实验设计，这是目前业内少见的、针对 AI Agent 的工程化能力的量化评估。 任务目标：让 Claude Code (Opus 4.6) [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/hardcore-review-13-languages-ai-favorite-go-performance-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance">本文永久链接</a> &#8211; https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance</p>
<p>大家好，我是Tony Bai。</p>
<p>随着 <a href="http://gk.link/a/12EPd">Claude Code</a>、<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=4067128336651386882#wechat_redirect">Gemini Cli</a>、Codex 等 AI 编程工具的全面普及，“让 AI 写代码”已经从极客的玩具变成了日常的生产力。随之而来的是一个触及灵魂的问题：<strong>哪种编程语言最适合交给 AI 去写？</strong></p>
<p>作为 Gopher，我们一直为 Go 语言的“极简语法”、“极速编译”和“强类型安全”感到自豪。我们理所当然地认为，这种没有任何隐式魔法、像白开水一样的语言，绝对是 LLM 的最爱。</p>
<p>然而，现实总是比直觉更骨感。近日，Ruby 核心提交者 Yusuke Endoh（@mame）发布了<a href="https://github.com/mame/ai-coding-lang-bench">一份名为 ai-coding-lang-bench 的硬核定量测评报告</a>。他使用 Claude Code（Opus 4.6 模型）对 13 种主流编程语言进行了系统性横向对比。</p>
<p>在这场涵盖了动态语言、静态语言和函数式语言的混战中，<strong>Go 语言的表现究竟如何？</strong> 是力压群雄，还是黯然失色？那些备受人类推崇的静态类型系统，在 AI 面前是否成了累赘？</p>
<p>本文和大家一起阅读和拆解这份报告，为你揭晓 AI 时代的语言偏好图谱。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-software-engineering-qr.png" alt="" /></p>
<h2>实验设计：让 AI 写一个 Mini-Git</h2>
<p>在评价这份报告之前，我们先来看看它的实验设计，这是目前业内少见的、针对 AI Agent 的工程化能力的量化评估。</p>
<p><strong>任务目标</strong>：让 Claude Code (Opus 4.6) 从零开始实现一个 mini-git（简化版的 Git）。这是一个极具代表性的任务，它包含了文件 I/O、哈希计算、数据结构操作以及命令行接口，足以考验模型对语言生态的综合运用能力。</p>
<p>测试被巧妙地分为两个阶段，模拟了真实的软件生命周期：</p>
<ul>
<li>v1 (新项目构建)：实现基础的 init, add, commit 和 log。</li>
<li>v2 (特性扩展)：在 v1 的基础上，增加 status, diff, checkout, reset, rm, show 等复杂指令。</li>
</ul>
<p><strong>提示词（Prompt）极其极简</strong>：“阅读 SPEC-v1.txt，实现它，并确保 test-v1.sh 测试通过。”这种设计最大程度地减少了人类指令的干预，纯粹考验 AI 代理在闭环环境下的自主编码、调试和测试能力。</p>
<p><strong>参赛选手（13种语言/15种配置）</strong>：</p>
<ul>
<li>动态语言：Python, Ruby, JavaScript, Perl, Lua</li>
<li>动态+类型检查器：Python/mypy, Ruby/Steep</li>
<li>静态语言：TypeScript, Go, Rust, C, Java</li>
<li>函数式语言：Scheme (动态), OCaml (静态), Haskell (静态)</li>
</ul>
<p>每种语言配置运行 <strong>20 次</strong>，以消除 LLM 生成的随机性带来的误差，并统计其耗时（Time）、成本（Cost，即 Token 消耗）和代码行数（LOC）。</p>
<h2>核心发现：动态语言逆袭，Go 位居第二梯队</h2>
<p>如果仅看总耗时和总成本（v1 + v2），测试结果呈现出了令人瞩目的阶梯式分布。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/hardcore-review-13-languages-ai-favorite-go-performance-2.png" alt="" /></p>
<h3>第一梯队：Ruby, Python, JavaScript 的绝对统治</h3>
<p>在这场 AI 编程竞速中，Ruby（73.1s）、Python（74.6s）和 JavaScript（81.1s）组成了无可争议的第一阵营。</p>
<p>它们不仅生成速度最快、消耗 API 成本最低（均在 $0.40 以下），而且在 20 次测试中表现出了极高的稳定性（标准差极小）。</p>
<p>对于 AI 来说，生成这三种语言的代码就像呼吸一样自然。它们无需繁琐的项目初始化配置（如 Cargo.toml 或 package.json），可以做到“建个文件直接跑”，这种极简的工作流在 v1 阶段（新项目构建）优势极大。</p>
<h3>第二梯队：被“强类型”拖慢脚步的 Go 与 Java</h3>
<p>现在，来回答大家最关心的问题：Go 表现如何？</p>
<p>答案是：位居第二梯队。Go 的总耗时为 101.6s，平均成本 $0.50。中规中矩。Go 虽然在语法上非常克制，但依然落后于 Python 和 JS 等动态语言。与之类似，Java（115.4s）也因为繁琐的语法结构和强类型约束，留在了这一梯队。</p>
<p>尽管如此，Go 在整个 20 次测试中没有出现一次失败（0 次 fail），这证明了 <a href="https://tonybai.com/2026/01/04/stop-lying-to-the-compiler">Go 的编译器在防止 AI 产生“幻觉 Bug”方面，发挥了极其可靠的安全网作用</a>。</p>
<h3>“后进生”阵营：Rust 与 C 的挣扎</h3>
<p>备受人类极客推崇的 Rust（113.7s，且有 2 次失败）和底层的 C（155.8s）在测试中显得步履维艰。</p>
<p>尤为值得注意的是，在总共 600 次的独立运行中，只有 Rust (2次) 和 Haskell (1次) 出现了测试失败（未能最终跑通 Shell 脚本）的情况。这两门语言都以其极高的心智负担和“编译器教你做人”的严格程度而闻名。</p>
<p>这也是将Rust列入“后进生”阵营的主要原因。如果用《飞驰人生》的拉力赛来比喻，Rust 相当于在40站的赛季中，有两站没能完赛！</p>
<h2>深度剖析：为什么 AI 更偏爱动态语言？</h2>
<p>在传统的工程视角中，“静态类型防止低级 Bug”、“动态语言难以维护”是金科玉律。但在 LLM 驱动的 Agent 开发流中，这个逻辑为何失效了？作者 Yusuke Endoh 提出了几个关键的解释维度。</p>
<h3>训练数据的“虹吸效应”</h3>
<p>LLM 的能力直接取决于训练语料的规模和质量。Python、JavaScript 和 Ruby 是过去十几年 Web 开发的绝对主流。GitHub 上海量的这三种语言的开源代码、StackOverflow 上的问答，为 Claude Code 提供了极其丰富的“预训练肌肉记忆”。</p>
<p>当 AI 需要实现一个功能时，它在 Python 的隐空间（Latent Space）中寻找最优解的路径，远比在 Haskell 甚至 Rust 中要清晰、笔直得多。</p>
<h3>静态类型的“双刃剑”与重构阻力</h3>
<p>静态类型系统的初衷是约束人类，防止我们在重构时犯错。但在 AI 的“ Prompt -> 生成 -> 测试报错 -> 思考 -> 再生成”的迭代循环中，严格的类型检查反而成了巨大的“摩擦力”。</p>
<ul>
<li>编译成本与调试死锁：在 Rust 或 C 中，当 AI 生成的代码出现类型不匹配时，它需要花费大量的 Token 去阅读复杂的编译器报错信息。有时，为了解决一个简单的借用检查器（Borrow Checker）报错，AI 可能会陷入漫长的、无休止的“试错-编译失败”死循环。</li>
<li>重构牵一发而动全身：在 v2 特性扩展阶段，往往需要修改现有的数据结构。对于 Python，AI 只需要在字典里加个 key；而对于 Rust 或 Java，这可能意味着需要重构一系列的 Struct、更新类型签名、甚至修改与之相关的无数个函数的参数声明。这种“爆炸半径”极大地增加了耗时。</li>
</ul>
<h3>“附加类型检查”的巨大损耗</h3>
<p>报告中一个非常有意思的对照组是：原生动态语言 vs 附加类型检查器的动态语言。</p>
<ul>
<li>Python (74.6s) vs Python/mypy (125.3s) —— 变慢了 1.6~1.7 倍。</li>
<li>Ruby (73.1s) vs Ruby/Steep (186.6s) —— 变慢了 2.0~3.2 倍！</li>
</ul>
<p>这证明了，迫使 AI 在动态语言中编写严谨的类型注解（Type Annotations），是一项极其昂贵的任务。模型需要耗费额外的算力去推导类型、生成类型声明文件，并且在类型检查器报错时，还要去修复那些在纯动态模式下可能根本不影响运行的“伪 Bug”。</p>
<h3>代码量（LOC）的迷思：越短越好吗？</h3>
<p>我们通常认为，写得越少，跑得越快。但数据打破了这个迷思。</p>
<p>Haskell 和 OCaml 生成的最终代码行数（224行和 216行）是所有语言中最少的，甚至少于 Python 和 Ruby。然而，它们在生成时间上的表现却排在倒数（Haskell 耗时最长，达 174s）。</p>
<p>这表明，对于 AI 来说，函数式语言那种高度抽象、信息密度极大的代码，生成和推理的成本远高于像 Python、Go 那种稍微啰嗦但逻辑平铺直叙的“大白话”代码。浓缩的未必是精华，对于 LLM 来说，高度浓缩往往意味着更高的生成熵和更高的试错概率。</p>
<h2>行业启示：我们需要重新思考 AI 时代的技术栈选型</h2>
<p>面对这份详实的基准测试报告，无论你是 CTO 还是普通开发者，都必须开始重新审视未来的技术选型逻辑。</p>
<h3>动态语言是快速原型的“绝对王者”</h3>
<p>如果你正在启动一个新项目，或者需要用 AI Agent 快速验证一个业务流程，Python 和 TypeScript 是首选（报告中 JavaScript 表现优于 TS，但在实际工程中 TS 的综合权衡更佳）。</p>
<p>不要迷信“大型项目必须一开始就上强类型编译语言”。在需求快速变化的初期，让 AI 用动态语言狂飙突进，是获取业务反馈最高效的手段。</p>
<h3>性能王者们的困境：Go 与 Rust 在 AI 时代掉队了吗？</h3>
<p>看到测评数据，很多 Gopher 可能会感到失落：难道注重工程严谨性和系统级性能的静态语言，真的在 AI 时代掉队了吗？</p>
<p>结论并非如此悲观。我们需要明确一点：Agent 测评的速度，不等于软件最终运行的速度。</p>
<ul>
<li>业务试错 vs 基础设施：AI Agent 目前最擅长、也最快速能完成的，是写“胶水逻辑”和“业务 CRUD”。在这些领域，Python 确实快。但当你的系统涉及到高并发、内存精细控制、或者需要打包为轻量级容器部署时，人类依然需要 Go。</li>
<li>容错的底线：在这场 600 次的庞大测试中，只有 Rust 和 Haskell 出现了最终测试失败，而 Go 保持了完美的 100% 成功率。这恰恰说明，Go 在“极度灵活（易幻觉）”与“极度严格（难生成）”之间，找到了一个非常微妙的平衡点。它可能不是 AI 写得最快的，但它一定是 AI 写出来最让人放心的系统级语言。</li>
</ul>
<p>我们不应期待 AI Agent 能够像写 Python 脚本一样，如德芙般丝滑地生成出一个复杂的 Go 并发系统。但在 AI 给出的初稿之上，Go 语言极佳的可读性和统一的规范，将为人类工程师的最终审查（Code Review）节省巨大的精力。</p>
<h2>小结：下一个十年的编程语言，长什么样？</h2>
<p>ai-coding-lang-bench 给我们上了生动的一课。它揭示了当前 LLM 的偏好：<strong>它们喜欢有海量训练数据的、灵活的、不需要应对死板编译器的语言。</strong></p>
<p>但我们必须认识到，这只是一份基于 2026 年初模型（Claude Opus 4.6）的快照。未来的 AI 编程语言形态，可能会朝着两个方向演进：</p>
<ol>
<li>AI Native 语言的诞生：抛弃目前设计给人类阅读的语法，出现一种专门为了降低 LLM 生成 Token 成本、且天然抗幻觉的机器中间语言。</li>
<li>现有静态语言的“Agent 友好化”编译模式：Go 和 Rust 可能会进化出一种特殊的编译模式。在这个模式下，编译器不仅是冷冰冰地报错，还能以结构化的、对 LLM 更友好的方式提供“修复建议”，从而大幅缩短 Agent 修复编译错误的反馈回路。</li>
</ol>
<p>无论如何，浪潮已经来临。在 AI 主导代码生成的新时代，我们评价一门编程语言的标准，正在从“它对人类大脑是否友好”，悄然转变为<strong>“它对大模型推理是否友好”</strong>。</p>
<p>而在这场新赛道上，动态语言们，已经抢跑了。</p>
<p>本文核心数据与图表均来源于 GitHub 项目 <a href="https://github.com/mame/ai-coding-lang-bench">mame/ai-coding-lang-bench</a>。</p>
<hr />
<p><strong>你的 AI 编程初体验</strong></p>
<p>看完这个排名，你是感到意外，还是早已感同身受？在你日常使用 AI 编程时，你觉得它写哪种语言最让你省心？你是否也曾为了修一个 AI 写的编译错误而陷入“死循环”？</p>
<p>欢迎在评论区分享你的“AI 协作”红黑榜！</p>
<hr />
<p>“语言的严格性正在变成 AI 的摩擦力？在 AI 时代，掌握一套能驱动 Agent 自动化、自修复的‘工作流’比死磕语法更重要。我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将教你如何利用 Claude Code 结合 Spec 驱动开发，构建真正高产出的‘软件工厂’。”</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/03/09/hardcore-review-13-languages-ai-favorite-go-performance/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>“棘手”难题：为什么 Go、Rust 与 Java 等语言的包管理永远无法达到完美？</title>
		<link>https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages/</link>
		<comments>https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages/#comments</comments>
		<pubDate>Tue, 03 Mar 2026 23:37:42 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[BackwardCompatibility]]></category>
		<category><![CDATA[ChecksumDatabase]]></category>
		<category><![CDATA[Decentralization]]></category>
		<category><![CDATA[DeveloperExperience]]></category>
		<category><![CDATA[DomainResurrection]]></category>
		<category><![CDATA[FlatNamespaces]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HierarchicalNamespaces]]></category>
		<category><![CDATA[HistoricalBaggage]]></category>
		<category><![CDATA[HyrumsLaw]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Lockfile]]></category>
		<category><![CDATA[ModuleProxy]]></category>
		<category><![CDATA[namespaces]]></category>
		<category><![CDATA[packagemanagement]]></category>
		<category><![CDATA[ProgrammingLanguages]]></category>
		<category><![CDATA[Repojacking]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[ScopedNamespaces]]></category>
		<category><![CDATA[SemanticConsistency]]></category>
		<category><![CDATA[SemanticVersioning]]></category>
		<category><![CDATA[semver]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[Stakeholders]]></category>
		<category><![CDATA[sumdb]]></category>
		<category><![CDATA[SupplyChainAttacks]]></category>
		<category><![CDATA[Typosquatting]]></category>
		<category><![CDATA[URLBasedIdentifiers]]></category>
		<category><![CDATA[WickedProblem]]></category>
		<category><![CDATA[仓库劫持]]></category>
		<category><![CDATA[作用域命名空间]]></category>
		<category><![CDATA[供应链攻击]]></category>
		<category><![CDATA[利益相关者]]></category>
		<category><![CDATA[包管理]]></category>
		<category><![CDATA[历史包袱]]></category>
		<category><![CDATA[去中心化]]></category>
		<category><![CDATA[向后兼容性]]></category>
		<category><![CDATA[命名空间]]></category>
		<category><![CDATA[域名复活]]></category>
		<category><![CDATA[基于URL的标识符]]></category>
		<category><![CDATA[层级命名空间]]></category>
		<category><![CDATA[开发者体验]]></category>
		<category><![CDATA[扁平命名空间]]></category>
		<category><![CDATA[拼写抢注]]></category>
		<category><![CDATA[棘手问题]]></category>
		<category><![CDATA[模块代理]]></category>
		<category><![CDATA[海勒姆定律]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[语义一致性]]></category>
		<category><![CDATA[语义化版本]]></category>
		<category><![CDATA[语义化版本控制]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[锁文件]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5976</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages 大家好，我是Tony Bai。 每天，全世界的开发者敲击下数以亿计的 npm install、go get、cargo build 或是 pip install。我们将这些包管理器视作理所当然的基础设施，仿佛它们就像水龙头一样，拧开就有源源不断的开源代码流出。然而，在这些看似简单的命令行背后，隐藏着计算机科学中最复杂、最容易引发争议，且永远无法找到“完美答案”的深水区。 近期，一篇名为《Package Management is a Wicked Problem》（包管理是一个“棘手”问题）的文章在技术社区引发了广泛关注，其姊妹篇《Package Management Namespaces》更是深度拆解了包命名的底层逻辑。作者以其多年参与包管理器数据和工具开发的经验，向我们揭示了一个残酷的真相：包管理不仅仅是一个纯粹的计算机科学问题，它是一个融合了社会工程学、经济学、安全性和向后兼容性的无底洞。 任何在这个层面上的微小改进，都会引发波及全球数千万个项目、数亿个版本的海啸。 本文将结合这两篇深度文章的核心观点，带你全景式地审视现代主流编程语言（如 Go、Rust、Python、JavaScript、Java）在包管理与“命名空间”上的激烈博弈与艰难演进。 包管理为何是一个“棘手问题”（Wicked Problem）？ 为了准确描述包管理的困境，原作者借用了 1973 年社会规划学者 Horst Rittel 和 Melvin Webber 提出的“棘手问题”（Wicked Problem）这一经典概念。 在城市规划或公共政策领域，“棘手问题”通常指的是那些没有明确边界、没有唯一正确答案、且试图解决它的行为本身就会改变问题定义的问题。作者指出，在涉及万亿次下载和全球协作的今天，包管理完美地契合了 Rittel 和 Webber 提出的“棘手问题”的多个核心特征： 问题的表述与解决方案是同一件事 当我们谈论“包管理”时，我们到底在谈论什么？ 作者敏锐地指出，这个词本身就是模棱两可的。对于前端开发者，它可能意味着用 npm 管理构建时的依赖树；对于系统管理员，它可能意味着用 apt 或 Homebrew 在操作系统上安装已编译好的二进制工具。 即使在同一个生态系统中，命名也充满了争议：我们称其为 package（包）、module（模块）、crate（板条箱）还是 distribution（发行版）？这些并非简单的同义词替换，它们各自编码了对“什么东西被版本化”、“什么东西被发布”以及“什么东西被安装”的深刻假设。正如作者所说：“包管理一路向下都关乎命名，而命名众所周知是计算机科学中的两大难题之一。” 当你决定引入 Lockfile（锁文件）时，你并不是在解决一个大家事先都同意的问题，你实际上是在重新定义“安装依赖”这个行为本身。这正是“棘手问题”的典型特征：解决方案定义了问题。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/package-management-unsolvable-problem-programming-languages-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages">本文永久链接</a> &#8211; https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages</p>
<p>大家好，我是Tony Bai。</p>
<p>每天，全世界的开发者敲击下数以亿计的 npm install、go get、cargo build 或是 pip install。我们将这些包管理器视作理所当然的基础设施，仿佛它们就像水龙头一样，拧开就有源源不断的开源代码流出。然而，在这些看似简单的命令行背后，隐藏着计算机科学中最复杂、最容易引发争议，且永远无法找到“完美答案”的深水区。</p>
<p>近期，一篇名为《<a href="https://nesbitt.io/2026/01/23/package-management-is-a-wicked-problem.html">Package Management is a Wicked Problem</a>》（包管理是一个“棘手”问题）的文章在技术社区引发了广泛关注，其姊妹篇《<a href="https://nesbitt.io/2026/02/14/package-management-namespaces.html">Package Management Namespaces</a>》更是深度拆解了包命名的底层逻辑。作者以其多年参与包管理器数据和工具开发的经验，向我们揭示了一个残酷的真相：<strong>包管理不仅仅是一个纯粹的计算机科学问题，它是一个融合了社会工程学、经济学、安全性和向后兼容性的无底洞。</strong> 任何在这个层面上的微小改进，都会引发波及全球数千万个项目、数亿个版本的海啸。</p>
<p>本文将结合这两篇深度文章的核心观点，带你全景式地审视现代主流编程语言（如 Go、Rust、Python、JavaScript、Java）在包管理与“命名空间”上的激烈博弈与艰难演进。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-software-engineering-qr.png" alt="" /></p>
<h2>包管理为何是一个“棘手问题”（Wicked Problem）？</h2>
<p>为了准确描述包管理的困境，原作者借用了 1973 年社会规划学者 Horst Rittel 和 Melvin Webber 提出的<strong>“棘手问题”（Wicked Problem）</strong>这一经典概念。</p>
<p>在城市规划或公共政策领域，“棘手问题”通常指的是那些没有明确边界、没有唯一正确答案、且试图解决它的行为本身就会改变问题定义的问题。作者指出，在涉及万亿次下载和全球协作的今天，包管理完美地契合了 Rittel 和 Webber 提出的“棘手问题”的多个核心特征：</p>
<h3>问题的表述与解决方案是同一件事</h3>
<p>当我们谈论“包管理”时，我们到底在谈论什么？</p>
<p>作者敏锐地指出，这个词本身就是模棱两可的。对于前端开发者，它可能意味着用 npm 管理构建时的依赖树；对于系统管理员，它可能意味着用 apt 或 Homebrew 在操作系统上安装已编译好的二进制工具。</p>
<p>即使在同一个生态系统中，命名也充满了争议：我们称其为 package（包）、module（模块）、crate（板条箱）还是 distribution（发行版）？这些并非简单的同义词替换，它们各自编码了对“什么东西被版本化”、“什么东西被发布”以及“什么东西被安装”的深刻假设。正如作者所说：“包管理一路向下都关乎命名，而命名众所周知是计算机科学中的两大难题之一。”</p>
<p>当你决定引入 Lockfile（锁文件）时，你并不是在解决一个大家事先都同意的问题，你实际上是在重新定义“安装依赖”这个行为本身。这正是“棘手问题”的典型特征：解决方案定义了问题。</p>
<h3>根本没有绝对的“对与错”，只有“好与坏”</h3>
<p>在包管理的世界里，几乎没有任何技术决策可以被客观地评判为“真”或“假”。</p>
<p>作者以 Homebrew 为例：早期 Homebrew 选择直接使用 Git 作为其软件包数据库。这在当时是一个绝妙的设计，降低了门槛，极大促进了早期的繁荣。但随着规模的爆炸，Git 仓库的拉取成了巨大的性能瓶颈。那么，当初选择 Git 是错的吗？这取决于你是看重早期的简单性，还是看重长期的扩展性。</p>
<p>作者还深入剖析了语义化版本控制（SemVer）的困境。SemVer 试图将版本更新变成一种“非黑即白”的契约：引入破坏性变更（Breaking Change）就必须升级主版本号。但在实际操作中，这完全沦为了主观判断。</p>
<p>这里作者引入了著名的<a href="https://tonybai.com/2025/04/26/13-laws-of-software-engineering">海勒姆定律（Hyrum&#8217;s Law）</a>：“当一个 API 拥有足够多的用户时，你在契约中承诺什么已经不重要了，你系统的所有可观测行为都将被某些用户所依赖。”</p>
<p>这意味着，对于某个开发者来说仅仅是修复了一个底层的 Bug，但对于恰好依赖这个 Bug 特性运行的另一个用户来说，这就是一次彻头彻尾的破坏性变更。版本号的跳动永远无法客观地评估对所有人的影响，它只能是“对特定人群好”或“对特定人群坏”。</p>
<h3>不可逆的深远后果与试错的代价</h3>
<p>在科学研究中，你可以提出假设并在实验室中进行 A/B 测试。但在包管理器设计中，你没有这种奢侈。作者强调：“每一个实施的解决方案都会留下无法消除的痕迹。”</p>
<p>当年 Python 的包索引（PyPI）决定接受无命名空间的扁平包名时，拼写抢注（Typosquatting）攻击就成为了这个生态不可避免的宿命。即便 PyPI 明天决定强制引入层级命名空间，它也无法改变全球数以千万计的存量 requirements.txt 文件，更不能直接使那些旧代码失效。</p>
<p>同样，RubyGems 至今仍托管着自 2007 年以来就未曾更新的古老包。在这个领域，<strong>没有推倒重来的机会（No do-overs）</strong>。</p>
<p>当年 npm 社区发生的 left-pad 事件（作者因为不满而撤下了一个仅有 11 行代码的基础库，导致全球无数基于 Babel、React 的项目构建失败），就是一个惨痛的教训。当你允许“取消发布”时，你不仅是在做一个功能，你是在制定一项将永久塑造开发者行为的政策。</p>
<h3>利益相关者的根本冲突与多重因果</h3>
<p>包管理到底应该优化什么？作者为我们罗列了一系列相互冲突的诉求：</p>
<ul>
<li>注册中心运营者想要极简的存储和极致的稳定性。</li>
<li>安全研究员想要可审计性和不可变性。</li>
<li>库作者想要发布时的灵活性。</li>
<li>企业应用开发者想要绝对的构建可重复性。</li>
</ul>
<p>这些目标是内在矛盾的。一个允许库作者轻松推送更新的系统，必然也是一个更容易受到供应链攻击的系统；一个能够捕获每一层深层依赖的 Lockfile，必然也是一个在执行安全升级时更痛苦的组件。</p>
<p>npm、Yarn 和 pnpm 能够在前端生态中三足鼎立，正是因为它们对这些冲突的诉求做出了不同的妥协。Yarn 的诞生是因为 Facebook 迫切需要 npm 早期未能提供的绝对可重复性；而 pnpm 的崛起则是因为开发者对磁盘空间和安装速度的渴望压倒了对传统 node_modules 结构的兼容性需求。</p>
<h2>命名空间之战——安全与便利的生死博弈</h2>
<p>在理解了包管理的“棘手”本质后，原作者将目光投向了包管理的核心战场：“命名机制”。你如何为一个包赋予一个全球唯一的标识符？这不仅决定了开发者的使用体验，更直接决定了整个生态的安全性架构。</p>
<p>作者在其姊妹篇《Package Management Namespaces》中，详细梳理了主流语言生态演化出的四种截然不同的命名范式。</p>
<h3>扁平命名空间（Flat Namespaces）：“先到先得”的蛮荒时代</h3>
<p><strong>代表生态：</strong> RubyGems, PyPI (Python), crates.io (Rust)</p>
<p>这是历史最悠久、设计最直观的模式：一个巨大的、全局共享的名称池。规则很简单：先到先得。如果你抢到了 requests，那就是你的。</p>
<ul>
<li>开发者的蜜月期：在生态初期，这种模式极度舒适。名称简短、好记，在命令行里敲下 gem install rails 或 cargo add serde 时，体验极其顺滑。</li>
<li>作者指出的致命缺陷：命名稀缺与安全梦魇。</li>
</ul>
<p>随着生态规模的爆炸式增长（如 PyPI 目前已有超过 60 万个项目），好名字很快被耗尽。许多简短的、有意义的词汇被一些只有个位数下载量的废弃项目永久“占坑”。新开发者被迫使用 python-dateutil 或 beautifulsoup4 这样带有笨拙前缀或数字后缀的名称。</p>
<p>更严重的是，这种模式为<strong>拼写错误抢注（Typosquatting）</strong>提供了完美的温床。攻击者只需注册 reqeusts（对应合法的 requests）然后守株待兔。因为在用户的键盘敲击和注册表查找之间没有任何组织层级的校验，也没有层级结构需要导航，这种基于简单字符串匹配的攻击防不胜防。</p>
<h3>作用域命名空间（Scoped Namespaces）：组织的介入与权力的转移</h3>
<p><strong>代表生态：</strong> npm (JavaScript), Packagist (PHP)</p>
<p>为了解决扁平命名的稀缺和冲突，npm 在 2014 年引入了作用域（Scopes）。你可以发布 @babel/core 而不是去争抢早已被占用的 babel-core。PHP 的 Packagist 更是从一开始就强制要求使用 vendor/package 的格式（如 symfony/console）。</p>
<ul>
<li>空间的释放：这极大地缓解了命名冲突。不同的组织可以安全地使用相同的叶子节点名称，例如 @types/node 和 @anthropic/node 可以和平共处，互不干扰。</li>
<li>作者提示的挑战：治理成本的飙升与“上移的占坑”。</li>
</ul>
<p>作用域引入了复杂的治理问题。谁有权决定 @babel 属于 Babel 团队？这就需要平台提供账号管理、所有权转移机制甚至处理商标纠纷的流程。</p>
<p>此外，作者犀利地指出，在 Packagist 这种强制模式中，虽然包名（package）不冲突了，但“供应商（Vendor）”名称本身依然是先到先得的。如果有人提前在 Packagist 上抢注了 google 这个供应商名称，那么 Google 官方的所有包都会被拦截在生态之外。这等于是把“占坑”的问题向上推了一个维度，其潜在的破坏力实际上更大。</p>
<h3>层级命名空间（Hierarchical Namespaces）：绑定全球 DNS 体系</h3>
<p><strong>代表生态：</strong> Maven Central (Java, Clojure)</p>
<p>Java 生态极其聪明地将包命名的治理权“外包”给了全球最大的、已经建立共识的分布式治理系统——DNS（域名系统）。你必须拥有 example.com 的域名所有权，才能发布前缀为 com.example 的包。</p>
<ul>
<li>秩序的建立：这几乎彻底消除了无意义的恶意占坑。像 Apache、Google 这样的庞大组织拥有了极其清晰、权威的代码家园。</li>
<li>作者揭示的致命隐患：MavenGate 与域名复活攻击。</li>
</ul>
<p>这种看似无懈可击的设计，依然存在致命的盲区。域名的所有权并不是永恒的，公司会倒闭，域名会过期。作者引用了安全公司 Oversecured 在 2024 年初发布的 “MavenGate” 报告：在与 Maven 关联的 3 万多个域名中，有近 18%（约 6170 个）域名已经过期或重新流入市场挂牌出售！</p>
<p>这其中甚至包含了被广泛使用的 co.fs2、com.opencsv 等知名库的根域名。这意味着，恶意攻击者只需花费极低的成本（几十美元）买下这些过期的域名，就能顺利通过 Maven Central 的 DNS TXT 记录验证，以合法原作者的身份接管整个命名空间，并发布带有后门的恶意新版本。由于大多数自动化构建工具倾向于拉取最新版本，这种基于<strong>“域名复活”</strong>的供应链攻击将具有毁灭性的穿透力和隐蔽性。</p>
<h3>基于 URL 的标识符：去中心化的乌托邦与残酷现实</h3>
<p><strong>代表生态：</strong> Go, SwiftPM</p>
<p>Go 模块（Go Modules）做出了一个在当时看来非常激进的选择：<strong>直接使用代码托管地址（如 github.com/gorilla/mux）作为包名标识符，彻底取消中心化的“注册（Registry）”步骤。</strong></p>
<ul>
<li>优雅的直达：这实现了零注册摩擦。URL 在结构上天然保证了全球唯一性，且通过对 Git 仓库的所有权，自然而然地确立了对代码包的所有权。</li>
<li>作者分析的隐藏代价：被基础设施绑架与脆弱的信任链。</li>
</ul>
<p>这种模式将包的命运与底层的托管平台（特别是 GitHub）进行了深度且危险的绑定。如果一个 GitHub 组织改名了，或者一个生气的开发者删除了他的仓库，所有依赖这些路径的下游系统都会瞬间崩溃。</p>
<p>为了弥补这个“去中心化”带来的巨大可用性缺陷，<strong>Go 团队不得不花费数年时间，在核心机制之外构建了极其庞大的辅助基础设施：</strong></p>
<ol>
<li>Module Proxy（模块代理）：用于持久化缓存源码。这样即使 GitHub 上的原仓库被彻底删除，只要代理中有缓存，全球的 Go 构建就不会中断。</li>
<li>Checksum Database (SumDB)：这是一个基于透明日志（Transparency Log）的校验和数据库。它提供了一个不可篡改的全局信任锚点，保证了任何人、在任何时间、从任何代理拉取同一个版本的 Go 模块，得到的哈希值必须绝对一致。它防止了作者恶意 force-push 篡改代码，甚至连运营该数据库的 Google 自己也无法在不被察觉的情况下篡改历史记录。</li>
</ol>
<p>作者通过对比指出，苹果生态的 <strong>SwiftPM</strong> 起初也采用了类似的 Git URL 模式，但并未配套建立 Proxy 和校验数据库。这导致如果 GitHub 仓库消失，Swift 的构建就会直接面临失败。更糟糕的是，2022 年的安全研究发现，大量 Go 和 Swift 包容易受到<strong>“仓库劫持”（Repo-jacking）</strong>攻击（即攻击者重新注册已注销的 GitHub 用户名，并重建同名的旧仓库）。Go 因为有强悍的 Proxy 和 SumDB 作为护城河，成功抵御了此类攻击；而 SwiftPM 至今仍暴露在巨大的软件供应链风险之中。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/the-ultimate-guide-to-go-module-qr.png" alt="" /></p>
<h2>深重历史包袱下的“痛苦迁徙”</h2>
<p>我们现在已经通过学术分析和前车之鉴，知道了理想的包管理应该是什么样。但原作者指出了一个无情的现实：<strong>大部分语言的包管理器早在十多年前就已经定型，它们带着最初的缺陷狂奔至今，积累了如同天文数字般的历史包袱。</strong> 如今想要修复这些缺陷，无异于给一架正在高速飞行的跨洋客机在空中更换引擎。</p>
<p>作者以 <strong>Rust (Cargo/crates.io)</strong> 的演进为例，生动地展示了这种深度重构的痛苦与艰难。</p>
<p>Rust 社区作为一个极其注重工程严谨性的生态，早在 2014 年起就在讨论引入命名空间。由于一开始选择了扁平命名，优质的单词已被大量占用。直到 2025 年，Rust 社区才终于正式推进了由 Manish Goregaokar 起草的 <strong>RFC 3243（可选命名空间）</strong> 提案。</p>
<p>他们的过渡方案设计得极其精妙且克制：<strong>不引入新的顶级前缀，而是将现有的合法包名升级为潜在的命名空间根节点。</strong></p>
<p>这意味着，如果你当前拥有 serde 这个基础包的所有权，你就可以顺理成章地发布 serde::derive（使用双冒号 :: 是为了与 Rust 原生的模块语法保持高度一致）。这种设计完美地做到了向后兼容：现有的扁平命名继续有效，新的层级命名以一种非常“Rust”的方式平滑引入。</p>
<p>但这依然无法避免阵痛。</p>
<p>作者举例说，像 tokio-macros 这样已经广泛存在于扁平空间中的包，如果未来想将其规范化迁移到 tokio::macros，所有依赖它的下游用户的代码都需要跟着进行繁琐的改写。而对于那些名字被别人占用的项目（比如知名的异步运行时 async-std 团队，其实并不拥有 async 这个基础包名的所有权），这个优雅的方案对他们来说依然是无解的。</p>
<p>Rust 社区作为一个资源充足、治理严密的顶级生态，依然需要花费数年的时间、跨越编译器、Cargo 工具和注册中心三大团队来协调设计和实施这个补救方案。</p>
<p>这充分印证了作者的观点：<strong>如果在发布 Registry 的第一天，你没有保留哪怕一丁点命名空间的扩展性（比如预留一个特殊的分隔符），那么一旦生态成型，后续的重构成本将是难以估量的。</strong> 同样，Python 的 PyPI 目前也在通过 PEP 752 提案如履薄冰般地尝试让大厂保留包名前缀（如 google-cloud-），但这只对未来的新包有效，对于存量系统依然是一笔难以理清的糊涂账。</p>
<h2>小结——放弃“完美”，拥抱“演进”</h2>
<p><img src="https://tonybai.com/wp-content/uploads/2026/package-management-unsolvable-problem-programming-languages-2.png" alt="" /></p>
<p>纵观这两篇深度探讨，无论是 npm 为了处理历史包袱而维护的并行命名系统，还是 Go 利用强大的 SumDB 来硬核弥补 URL 导入天然缺陷的工程奇迹，亦或是 Rust 正在小心翼翼进行的痛苦命名空间迁移，所有的现象都在向我们诉说同一个真理：</p>
<p><strong>包管理（Package Management）作为一个“棘手问题”，永远不会被真正“解决”。</strong></p>
<p>我们无法像推导一个数学定理那样，给出一个让所有人都满意的、完美的包管理公式。我们所能做的，是在不断变化的安全性要求、开发者的灵活性需求、系统的可用性以及沉重的历史包袱之间，寻找属于这个时代的<strong>最优解（Trade-offs）</strong>。</p>
<p>对于语言和工具的设计者而言，在系统上线的第一天保持足够的克制和选项预留价值千金；而对于广大的应用开发者而言，正如作者所呼吁的，我们需要深刻理解这些构建工具背后的“棘手”本质。</p>
<p>当我们面对依赖冲突或奇怪的版本解析时，少一些诸如“为什么这个工具这么蠢”的情绪化抱怨，多一些对供应链安全的审慎态度（如定期审查依赖树、使用内部可信代理、开启严格的校验和哈希验证），才是面对现代软件工程深水区时，我们应有的专业素养与敬畏之心。</p>
<p>下一次，当你敲下那行习以为常的 go get、npm install 或 cargo build 时，不妨停下来思考一秒钟：为了将这几 KB 的代码安全、无误地送到你的硬盘里，背后那套由无数妥协与智慧构筑的庞大机器，是如何在无声中疯狂运转的。</p>
<p>资料链接：</p>
<ul>
<li>https://nesbitt.io/2026/01/23/package-management-is-a-wicked-problem.html</li>
<li>https://nesbitt.io/2026/02/14/package-management-namespaces.html</li>
</ul>
<hr />
<p><strong>你最想吐槽哪家的包管理？</strong></p>
<p>每一个“依赖地狱”的背后，都有一位在深夜叹气的程序员。在你的开发经历中，哪门语言的包管理最让你感到顺手？哪门又最让你抓狂？你认为 Go 的“URL 导入+校验和数据库”模式是目前的终极答案吗？</p>
<p>欢迎在评论区分享你的“包管理血泪史”！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/03/04/package-management-unsolvable-problem-programming-languages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>20 年 Java 老店的“背叛”：WSO2 为何高呼“Goodbye Java, Hello Go”？</title>
		<link>https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift/</link>
		<comments>https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift/#comments</comments>
		<pubDate>Wed, 28 Jan 2026 23:15:10 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AOT]]></category>
		<category><![CDATA[APIManagement]]></category>
		<category><![CDATA[API管理]]></category>
		<category><![CDATA[ArchitectureShift]]></category>
		<category><![CDATA[BackendDevelopment]]></category>
		<category><![CDATA[Ballerina]]></category>
		<category><![CDATA[BFF]]></category>
		<category><![CDATA[cloudnative]]></category>
		<category><![CDATA[CNCF]]></category>
		<category><![CDATA[ConcurrencyPrimitives]]></category>
		<category><![CDATA[Containerization]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[GarbageCollection]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[GraalVM]]></category>
		<category><![CDATA[IdentityAuthentication]]></category>
		<category><![CDATA[infrastructure]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JIT]]></category>
		<category><![CDATA[JVM]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[MemoryBloat]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[middleware]]></category>
		<category><![CDATA[OpenChoreo]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[ProjectLoom]]></category>
		<category><![CDATA[serverless]]></category>
		<category><![CDATA[SoftwareServer]]></category>
		<category><![CDATA[StartupSpeed]]></category>
		<category><![CDATA[StaticBinary]]></category>
		<category><![CDATA[VirtualThreads]]></category>
		<category><![CDATA[WSO2]]></category>
		<category><![CDATA[中间件]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[内存膨胀]]></category>
		<category><![CDATA[后端开发]]></category>
		<category><![CDATA[启动速度]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[基础设施]]></category>
		<category><![CDATA[容器化]]></category>
		<category><![CDATA[并发原语]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[提前编译]]></category>
		<category><![CDATA[架构变迁]]></category>
		<category><![CDATA[虚拟线程]]></category>
		<category><![CDATA[身份认证]]></category>
		<category><![CDATA[软件服务器]]></category>
		<category><![CDATA[静态二进制]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5785</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift 大家好，我是Tony Bai。 “当我们 2005 年创办 WSO2 时，开发服务端企业级基础设施的正确语言毫无疑问是：Java。然而，当我们走过第 20 个年头并展望未来时，情况已经变了。” 近日，全球知名的开源中间件厂商 WSO2 发布了一篇震动技术圈的博文——《Goodbye Java, Hello Go!》。 这是企业级软件在云原生时代技术风向标的一次重要偏转。作为 Java 时代的既得利益者，WSO2 曾在 API 管理、集成中间件领域构建了庞大的 Java 帝国。为何在今天，他们会做出如此激进的转向？Java 真的不适合未来了吗？Go 到底赢在哪里？ 让我们深入剖析这背后的技术逻辑、架构变迁与社区的激烈争议。 时代的变迁——从“服务器”到“函数” WSO2 的转向并非一时冲动，而是基于对过去 15 年基础设施软件形态深刻变化的洞察。其博文中极其精准地总结了这一变迁： “服务器”概念的消亡 在 2010 年代之前，中间件是以独立“服务器”（Server）的形式交付的。 应用服务器 (App Servers)：如 WebLogic, WebSphere, Tomcat。 企业服务总线 (ESB)：集成了各种协议适配器的庞然大物。 业务流程服务器 (Process Servers)：管理长周期的业务状态。 那是一个“重量级”的时代。你部署一个服务器，然后把你的业务逻辑（WAR 包、JAR 包）扔进去运行。这正是 Java 和 JVM [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/wso2-goodbye-java-hello-go-tech-stack-shift-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift">本文永久链接</a> &#8211; https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift</p>
<p>大家好，我是Tony Bai。</p>
<p>“当我们 2005 年创办 WSO2 时，开发服务端企业级基础设施的正确语言毫无疑问是：Java。然而，当我们走过第 20 个年头并展望未来时，情况已经变了。”</p>
<p>近日，全球知名的开源中间件厂商 WSO2 发布了一篇震动技术圈的博文——《<a href="https://wso2.com/library/blogs/goodbye-java-hello-go">Goodbye Java, Hello Go!</a>》。</p>
<p>这是企业级软件在云原生时代技术风向标的一次重要偏转。作为 Java 时代的既得利益者，WSO2 曾在 API 管理、集成中间件领域构建了庞大的 Java 帝国。为何在今天，他们会做出如此激进的转向？Java 真的不适合未来了吗？Go 到底赢在哪里？</p>
<p>让我们深入剖析这背后的技术逻辑、架构变迁与<a href="https://www.reddit.com/r/golang/comments/1qomr6g/goodbye_java_hello_go/">社区的激烈争议</a>。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/distributed-system-guide-qr.png" alt="" /></p>
<h2>时代的变迁——从“服务器”到“函数”</h2>
<p>WSO2 的转向并非一时冲动，而是基于对过去 15 年基础设施软件形态深刻变化的洞察。其博文中极其精准地总结了这一变迁：</p>
<h3>“服务器”概念的消亡</h3>
<p>在 2010 年代之前，中间件是以独立“服务器”（Server）的形式交付的。</p>
<ul>
<li>应用服务器 (App Servers)：如 WebLogic, WebSphere, Tomcat。</li>
<li>企业服务总线 (ESB)：集成了各种协议适配器的庞然大物。</li>
<li>业务流程服务器 (Process Servers)：管理长周期的业务状态。</li>
</ul>
<p>那是一个“重量级”的时代。你部署一个服务器，然后把你的业务逻辑（WAR 包、JAR 包）扔进去运行。这正是 Java 和 JVM 的黄金时代——JVM 作为一个强大的运行时环境，提供了热加载、动态管理、JIT 优化等一系列高级功能，完美匹配了这种“长时间运行、多应用共享”的服务器模式。</p>
<p>然而，容器化时代终结了这一切。</p>
<p>现在的“服务器”不再是一个独立的实体，而变成了一个<strong>库 (Library)</strong>。</p>
<ul>
<li>你的业务逻辑不再是“寄生”在服务器里，而是包含了服务器。</li>
<li>整个应用打包成一个 Docker 镜像，作为一个独立的进程运行。</li>
<li>任务完成后，容器销毁，进程结束。</li>
</ul>
<p>在 WSO2 看来，“独立软件服务器的时代已经结束了”。这对于 Java 来说，是一个底层逻辑的打击。</p>
<h3>生命周期：从“月”到“毫秒”</h3>
<p>在过去，一个服务器启动慢点没关系，因为它一旦启动，可能会运行数月甚至数年。JVM 的 JIT（即时编译）机制通过预热来换取长期运行的高性能，这是一种非常合理的权衡。</p>
<p>但在 Kubernetes 和 Serverless 主导的今天，服务器变得极度短暂 (Ephemeral)。</p>
<ul>
<li>容器根据负载自动扩缩容，新实例必须瞬间就绪。</li>
<li>Serverless 函数可能只存活几秒钟。</li>
</ul>
<p>在这种场景下，启动时间就是服务质量 (SLA)。</p>
<p>WSO2 指出：“容器应该在毫秒级内准备好起舞，而不是秒级。” Java 庞大的生态依赖（Spring 初始化、类加载、注解扫描）和 JVM 的启动开销，在云原生环境下显得格格不入。内存膨胀（Memory Bloat）也直接推高了云厂商的账单。</p>
<h3>生态位的错位：修补 vs. 原生</h3>
<p>面对挑战，Java 社区并非无动于衷。<strong>GraalVM Native Image</strong> 试图通过 AOT（提前编译）解决启动速度问题；<strong>Project Loom</strong> 试图通过虚拟线程解决并发资源消耗问题。</p>
<p>但在 WSO2 的架构师们看来，这些努力更像是一种<strong>“追赶式的修补”</strong>。</p>
<blockquote>
<p>“这些解决方案感觉就像是在为一个不同时代设计的语言和运行时进行翻新。”</p>
</blockquote>
<p>GraalVM 虽然强大，但带来了构建时间的剧增、反射的限制以及调试的复杂性。相比之下，Go 语言在设计之初就<strong>原生 (Native)</strong> 地考虑了这些问题：编译即二进制，启动即巅峰，并发即协程。这是一种“原生契合”与“后天适配”的本质区别。</p>
<h2>WSO2 的架构重构——前端不动，后端大换血</h2>
<p>WSO2 并没有盲目地全盘推翻，他们对企业级软件的三层架构（前端、中间层、后端）进行了冷静的评估：</p>
<h3>前端 (Frontend)：维持现状</h3>
<ul>
<li><strong>现状</strong>：Web (JS/TS), iOS (Swift/Flutter), Android (Kotlin/Java)。</li>
<li><strong>未来</strong>：<strong>No Change</strong>。</li>
<li><strong>理由</strong>：前端技术栈受限于终端设备（浏览器、手机 OS），且更新换代极快（“fad-driven”，时尚驱动）。目前没有改变的必要。</li>
</ul>
<h3>中间层 (Middle Tier)：Ballerina 的独角戏</h3>
<ul>
<li><strong>现状</strong>：Java, Ballerina。</li>
<li><strong>未来</strong>：<strong>Ballerina</strong>。</li>
<li><strong>核心逻辑</strong>：这一层通常被称为 BFF (Backend for Frontend)，负责 API 聚合、编排。WSO2 自研的 <strong>Ballerina</strong> 语言正是为此而生，它将网络原语（Network Primitives）作为语言的一等公民，极其适合做集成工作。</li>
</ul>
<h3>后端 (Backend)：Go 与 Python 的双雄会</h3>
<ul>
<li><strong>现状</strong>：Java, Go, NodeJS, Python。</li>
<li><strong>未来</strong>：<strong>Go, Python</strong>。</li>
<li><strong>核心逻辑</strong>：这是基础设施逻辑的核心。Python 将继续统治 AI/ML 领域，而 Go 将彻底接管原本属于 Java 的领地，成为构建高性能、高并发基础设施的首选。</li>
</ul>
<h2>为什么是 Go，而不是 Rust？</h2>
<p>这是一个每个技术决策者都会面临的灵魂拷问：既然要追求性能和原生编译，为什么不选 Rust？它不是更快、更安全吗？</p>
<p>WSO2 的回答展现了极高的工程务实精神。他们确实评估了 Rust，但最终选择了 Go。理由如下：</p>
<h3>抽象层级的匹配</h3>
<ul>
<li><strong>Rust 的战场</strong>：操作系统内核、浏览器引擎、嵌入式设备。这些场景需要对内存布局、生命周期做极致的微操，且进程几乎永不重启。</li>
<li><strong>Go 的战场</strong>：中间件、API 网关、编排系统。</li>
</ul>
<p>WSO2 构建的是<strong>中间件基础设施</strong>（如 API Gateway, Identity Server）。在这个层级，“我们总是比裸金属 (Bare Metal) 高那么一点点”。Go 提供的自动垃圾回收 (GC) 和高效的并发原语，恰好处于这个“甜点”位置。</p>
<h3>避免“过度杀伤” (Overkill)</h3>
<p>Rust 的所有权模型 (Ownership) 和借用检查器 (Borrow Checker) 虽然保证了内存安全，但也带来了极高的学习曲线和开发摩擦。对于大多数企业级业务逻辑来说，Rust 提供的控制力是多余的，而为此付出的开发效率代价是昂贵的。</p>
<h3>云原生生态的引力</h3>
<p>这是一个无法忽视的因素。Go 是云原生的“普通话”。</p>
<p>Kubernetes、Docker、Prometheus、etcd、Terraform…… 几乎所有现代基础设施的基石都是用 Go 构建的。选择 Go，意味着：</p>
<ul>
<li><strong>库的复用</strong>：可以直接调用 K8s 的库，而不是通过 API。</li>
<li><strong>人才的复用</strong>：DevOps 工程师和 SRE 通常都懂 Go，可以无缝参与开发。</li>
<li><strong>社区的共鸣</strong>：更容易融入 CNCF 生态，获得社区贡献。</li>
</ul>
<h2>实战验证——WSO2 的 Go 之旅</h2>
<p>WSO2 并非纸上谈兵，他们在过去十年中已经在多个关键项目中验证了 Go 的能力：</p>
<h3>OpenChoreo (CNCF Sandbox Project)</h3>
<p>这是 WSO2 最具野心的项目之一，一个面向 Kubernetes 的开发者平台（IDP）。</p>
<ul>
<li><strong>挑战</strong>：需要深度集成 K8s，处理复杂的 GitOps 流程，且自身必须轻量、快速。</li>
<li><strong>Go 的价值</strong>：作为 K8s 原生语言，Go 让 OpenChoreo 能够像原生组件一样运行在集群中，资源占用极低。</li>
</ul>
<h3>Ballerina 编译器的彻底重写</h3>
<p>这是一个惊人的决定。Ballerina 语言最初是基于 Java 实现的（运行在 JVM 上）。现在，WSO2 正在用 Go 完全重写 Ballerina 编译器。</p>
<ul>
<li><strong>目标</strong>：摆脱 JVM 的束缚，实现瞬间启动。</li>
<li><strong>新架构</strong>：前端编译器用 Go 编写，直接生成基于 Go 的中间表示 (BIR)，这让 CLI 工具的体验得到了质的飞跃。</li>
</ul>
<h3>Thunder：下一代身份认证平台</h3>
<p>身份认证（IAM）通常处于请求链路的关键路径上，对延迟极其敏感。Thunder 利用 Go 的高并发处理能力，实现了在高负载下的低延迟认证，且在容器化环境中具备极快的冷启动能力。</p>
<h2>社区激辩——理性的探讨与情绪的宣泄</h2>
<p>这篇博文在 Reddit 的 r/golang 板块引发了数百条评论的激烈讨论。这不仅仅是语言之争，更是两种工程文化的碰撞。</p>
<h3>反方阵营：Java 依然是王者</h3>
<ol>
<li>
<p><strong>“这是管理层的愚蠢决定”</strong>：<br />
一位愤怒的网友评论道：“计算资源是廉价的，开发人员的时间才是昂贵的。” 他认为，虽然 Go 节省了内存，但在业务逻辑极其复杂的企业级应用中，Java 强大的 IDE 支持、成熟的设计模式和庞大的生态库能显著降低开发成本。强行切换到 Go，可能会导致开发效率的崩塌。</p>
</li>
<li>
<p><strong>“Java 并没有停滞不前”</strong>：<br />
很多 Java 支持者指出，WSO2 对 Java 的印象似乎还停留在 Java 8 时代。现代 Java (21+) 引入了 <strong>Virtual Threads (Project Loom)</strong>，在并发模型上已经可以与 Go 的 Goroutine 媲美；而 <strong>GraalVM</strong> 的成熟也让 Java 能够编译成原生镜像，启动速度不再是短板。</p>
</li>
<li>
<p><strong>“生态位的不可替代性”</strong>：<br />
在处理遗留系统（如 SOAP, XML, 复杂的事务处理）方面，Java 积累了 20 年的库是 Go 无法比拟的。用 Go 去重写这些复杂的业务逻辑，无异于“重新发明轮子”，且容易引入新的 Bug。</p>
</li>
</ol>
<h3>正方阵营：Go 是未来的选择</h3>
<ol>
<li>
<p><strong>“运维友好才是真的友好”</strong>：<br />
一位 DevOps 工程师反驳道：“在微服务架构下，运维成本是巨大的。” Go 生成的静态二进制文件（Static Binary）是运维的梦想——没有依赖地狱，没有 JVM 版本冲突，所有东西都打包在一个几 MB 的文件里。这种部署的便捷性，是 Java 永远无法达到的。</p>
</li>
<li>
<p><strong>“简洁是一种防御机制”</strong>：<br />
Java 项目容易陷入“过度设计”的泥潭——层层叠叠的抽象、复杂的继承关系、魔法般的注解。Go 的强制简洁性（没有继承、显式错误处理）虽然写起来啰嗦，但读起来轻松。在人员流动频繁的大型团队中，Go 代码的可维护性往往优于 Java。</p>
</li>
<li>
<p><strong>“云原生的网络效应”</strong>：<br />
正如 WSO2 所言，如果你在写 K8s Controller，如果你在写 Sidecar，如果你在写网关，Go 就是默认语言。这不仅仅是语言特性的问题，这是生态引力的问题。逆流而上使用 Java 编写这些组件，会让你失去整个社区的支持。</p>
</li>
</ol>
<h2>小结：没有终极语言，只有最适合的工具</h2>
<p>WSO2 的声明并非要“杀死” Java。他们明确表示，现有的 Java 产品线将继续得到长期支持。但在新一代的云原生基础设施平台上，他们坚定地选择了 Go。</p>
<p>这一选择揭示了软件行业的一个趋势：通用编程语言的时代似乎正在结束，“领域专用语言”的时代正在到来。</p>
<ul>
<li>做前端？选 TS/JS。</li>
<li>做 AI 模型训练？选 Python。</li>
<li>做操作系统、浏览器或者嵌入式系统？选 C/Rust/C++。</li>
<li>做企业级业务逻辑（尤其是遗留系统）？Java 依然稳健。</li>
<li>做云原生基础设施、中间件、高并发服务？Go 是当之无愧的王者。</li>
</ul>
<p>对于 Gopher 而言，WSO2 的转型是一个强有力的信号：你们选对了赛道。Go 不仅是 Google 的语言，它正在成为定义未来十年企业级基础设施的通用语。</p>
<p>资料链接：</p>
<ul>
<li>https://wso2.com/library/blogs/goodbye-java-hello-go</li>
<li>https://www.reddit.com/r/golang/comments/1qomr6g/goodbye_java_hello_go/</li>
</ul>
<hr />
<p><strong>你的技术栈“保卫战”</strong></p>
<p>WSO2 的转身，是时代的缩影，也是个体的写照。在你的团队中，是否也发生过类似的“去 Java 化”或“拥抱 Go”的讨论？你认为在云原生时代，Java 还能守住它的江山吗？</p>
<p>欢迎在评论区分享你的观点或经历，无论是坚守者还是转型者，我们都想听听你的声音！</p>
<p>如果这篇文章引发了你的思考，别忘了点个【赞】和【在看】，并转发给你的架构师朋友，看看他们怎么选！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/01/29/wso2-goodbye-java-hello-go-tech-stack-shift/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 语言的“舒适区”：为何在这张“鄙视链”金字塔中，Go 仅次于 C？</title>
		<link>https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid/</link>
		<comments>https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid/#comments</comments>
		<pubDate>Wed, 07 Jan 2026 00:16:00 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Abomination]]></category>
		<category><![CDATA[asm]]></category>
		<category><![CDATA[BorrowChecker]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[CognitiveBurden]]></category>
		<category><![CDATA[ComfortZone]]></category>
		<category><![CDATA[ContemptChain]]></category>
		<category><![CDATA[DataOrientedDesign]]></category>
		<category><![CDATA[DOD]]></category>
		<category><![CDATA[Elixir]]></category>
		<category><![CDATA[GarbageCollection]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HTMX]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[lifetimes]]></category>
		<category><![CDATA[Lua]]></category>
		<category><![CDATA[MemeLanguages]]></category>
		<category><![CDATA[MemorySafety]]></category>
		<category><![CDATA[MentalModel]]></category>
		<category><![CDATA[metaprogramming]]></category>
		<category><![CDATA[Minimalism]]></category>
		<category><![CDATA[NecessaryEvil]]></category>
		<category><![CDATA[Nononsense]]></category>
		<category><![CDATA[Overengineering]]></category>
		<category><![CDATA[Pragmatism]]></category>
		<category><![CDATA[programminglanguage]]></category>
		<category><![CDATA[Pyramid]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[TotalFailure]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[WildPointer]]></category>
		<category><![CDATA[Zig]]></category>
		<category><![CDATA[元编程]]></category>
		<category><![CDATA[内存安全]]></category>
		<category><![CDATA[实用主义]]></category>
		<category><![CDATA[心智模型]]></category>
		<category><![CDATA[拒绝废话]]></category>
		<category><![CDATA[掌控感]]></category>
		<category><![CDATA[极简主义]]></category>
		<category><![CDATA[现代化C]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[舒适区]]></category>
		<category><![CDATA[认知负担]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[过度设计]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[鄙视链]]></category>
		<category><![CDATA[野指针]]></category>
		<category><![CDATA[金字塔]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5684</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid 大家好，我是Tony Bai。 最近，一张“编程语言分级图”在技术社区引发大家热议。它没有参考 TIOBE 排名，也不看 GitHub Star 数，而是完全基于一种简单粗暴的价值观：谁最不折腾人？ 在这张金字塔中，C 语言高居神坛（The one and only），而 Java、Python、C++ 被踩在最底层的“憎恶（Abomination）”泥潭里。甚至连备受推崇的 Rust，也被归入了“彻底失败（Total failure）”。 ** Go 语言则稳稳地站在了 T1 梯队——“No nonsense（拒绝废话）”。** 这张图看似偏激，却也道出了一些资深开发者的心声。它揭示了 Go 语言最大的魅力：在混沌的软件工程世界里，Go 为我们圈出了一块难得的“舒适区”。 鄙视链解构：极简主义者的“神曲” 这张图从上到下，宛如但丁的《神曲》，描绘了从天堂到地狱的编程世界观。meme图的作者显然是一位厌恶抽象、崇尚掌控机器、鄙视过度设计的硬核程序员。让我们逐层拆解： 塔尖：The one and only（唯一的真神） C。 C 是编程界的拉丁语。它直接映射硬件，没有隐藏的运行时，没有 GC。它是操作系统和驱动的基石，是所有软件的“第一推动力”。在极简主义眼中，只有 C 是纯粹的。 T1 梯队：No nonsense（拒绝废话 / 实干家） Go、OCaml（骆驼）、Lua、ASM（芯片/汇编）、Erlang（红色e）。 这一层是“干活”的语言。它们专注解决问题、务实、没有过度设计。 Go：带 GC 的 C，工业界的实干家。 Lua &#38; [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/go-language-comfort-zone-in-contempt-chain-pyramid-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid">本文永久链接</a> &#8211; https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid</p>
<p>大家好，我是Tony Bai。</p>
<p>最近，一张“编程语言分级图”在技术社区引发大家热议。它没有参考 TIOBE 排名，也不看 GitHub Star 数，而是完全基于一种简单粗暴的价值观：<strong>谁最不折腾人？</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/go-language-comfort-zone-in-contempt-chain-pyramid-2.png" alt="" /></p>
<p>在这张金字塔中，C 语言高居神坛（The one and only），而 Java、Python、C++ 被踩在最底层的“憎恶（Abomination）”泥潭里。甚至连备受推崇的 Rust，也被归入了“彻底失败（Total failure）”。</p>
<p>** Go 语言则稳稳地站在了 T1 梯队——“No nonsense（拒绝废话）”。**</p>
<p>这张图看似偏激，却也道出了一些资深开发者的心声。它揭示了 Go 语言最大的魅力：在混沌的软件工程世界里，Go 为我们圈出了一块难得的<strong>“舒适区”</strong>。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/distributed-system-guide-qr.png" alt="img{512x368}" /></p>
<h2>鄙视链解构：极简主义者的“神曲”</h2>
<p>这张图从上到下，宛如但丁的《神曲》，描绘了从天堂到地狱的编程世界观。meme图的作者显然是一位厌恶抽象、崇尚掌控机器、鄙视过度设计的硬核程序员。让我们逐层拆解：</p>
<ol>
<li>
<p><strong>塔尖：The one and only（唯一的真神）</strong></p>
<ul>
<li><strong>C</strong>。</li>
<li>C 是编程界的拉丁语。它直接映射硬件，没有隐藏的运行时，没有 GC。它是操作系统和驱动的基石，是所有软件的“第一推动力”。在极简主义眼中，只有 C 是纯粹的。</li>
</ul>
</li>
<li>
<p><strong>T1 梯队：No nonsense（拒绝废话 / 实干家）</strong></p>
<ul>
<li><strong>Go</strong>、<strong>OCaml</strong>（骆驼）、<strong>Lua</strong>、<strong>ASM</strong>（芯片/汇编）、<strong>Erlang</strong>（红色e）。</li>
<li>这一层是“干活”的语言。它们专注解决问题、务实、没有过度设计。
<ul>
<li><strong>Go</strong>：带 GC 的 C，工业界的实干家。</li>
<li><strong>Lua &amp; ASM</strong>：极致的小巧与极致的控制。</li>
<li><strong>OCaml &amp; Erlang</strong>：虽然是函数式或特定领域，但以实用和高可靠性著称，不搞虚头巴脑的学术概念。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>T2 梯队：Meme languages（网红/小众神教）</strong></p>
<ul>
<li><strong>Odin</strong>、<strong>Jai</strong>（绿色文字）、<strong>HolyC</strong>（黄色十字六边形）、<strong>Elixir</strong>（紫色水滴）、<a href="https://tonybai.com/2024/09/20/htmx-gopher-perfect-partner-for-full-stack"><strong>HTMX</strong></a>（激光眼马）。</li>
<li>我敢保证这一层的很多语言你都没有听过，我也是查了很久才对号入座，这也说明原meme图的作者在编程语言方面涉猎甚广。这一层的语言通常具有“网红”属性，或者带有强烈的“亚文化/宗教”色彩。它们在特定圈子（如独立游戏开发、TempleOS 粉丝）中声量巨大，但在主流工业界存在感稀薄。
<ul>
<li><strong>Odin &amp; Jai</strong>：这两者常被绑定提及，代表了“Handmade”社区（手工造轮子）的价值观。它们试图取代 C++ 用于游戏开发，强调面向数据设计（DOD）。Odin 虽好但小众，Jai 则因长期未公开发布而被调侃为“幻之语言”。</li>
<li><strong>HolyC</strong>：这是“上帝的程序员”Terry Davis 为 TempleOS 创造的语言，在技术宅圈子中是神一般的存在（Meme 之神），但几乎没有实际生产用途。</li>
<li><strong>Elixir &amp; HTMX</strong>：前者是 Erlang VM 上的“时髦文青”，后者是最近在推特上掀起“回归 HTML”运动的网红库。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>T3 梯队：Necessary evil（必要之恶）</strong></p>
<ul>
<li><strong>JS</strong>、<strong>CSS</strong>、<strong>Bash</strong>、<strong>Swift</strong>、<strong>TeX</strong>、<strong>SQL</strong>。</li>
<li>你很讨厌它们，但你离不开它们。因为它们垄断了特定领域（浏览器、终端、苹果生态、论文排版、数据库）。你用它们不是因为爱，而是因为别无选择。</li>
</ul>
</li>
<li>
<p><strong>T4 梯队：Total failure（彻底失败 / 认知灾难）</strong></p>
<ul>
<li><strong>Haskell</strong>、<strong>Rust</strong>（齿轮）、<strong>Zig</strong>（橙色Z）、<strong>Scala</strong>、<strong>Racket</strong>、<strong>Kotlin</strong>。</li>
<li>这是最引战的一层。这里的“失败”指的不是技术失败，而是<strong>“在追求简单的道路上失败了”</strong>。
<ul>
<li><strong>Rust</strong>：为了内存安全或零开销抽象，引入了极其复杂的心智负担（生命周期、编译期计算）。作者认为让程序员当编译器的奴隶是一种失败。</li>
<li><strong>Zig</strong>：虽然标榜是 C 的继承者，但它要求显式管理所有资源（到处传递 Allocator），且引入了强大的 comptime 元编程。在作者看来，这并没有真正降低 C 的心智负担，反而换了一种方式折腾大脑，且至今仍未发布正式版（1.0）。</li>
<li><strong>Haskell &amp; Scala</strong>：学术概念堆砌，Monad 满天飞，导致代码难以阅读和维护。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>底层：Abomination（憎恶 / 不可名状之物）</strong></p>
<ul>
<li><strong>C++</strong>、<strong>C#</strong>、<strong>Java</strong>、<strong>PHP</strong>、<strong>TS</strong>、<strong>Python</strong>、<strong>Ruby</strong>。</li>
<li>地狱最底层。它们犯了<strong>“过度设计”、“臃肿”、“慢”</strong>的原罪。
<ul>
<li><strong>C++</strong>：特性大杂烩，学习曲线陡峭。</li>
<li><strong>Java/C#</strong>：企业级官僚主义，层层叠叠的抽象工厂。</li>
<li><strong>Python/Ruby/PHP</strong>：解释执行慢，动态类型在大型工程中是维护灾难。</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2>神坛之下的第一人：Go 是“带了安全带的 C”</h2>
<p>在这张图中，C 是唯一的“神”。为什么？因为 C 诚实。它与机器直接对话，没有中间商赚差价。但 C 也是危险的，内存泄漏和野指针是每个 C 程序员的噩梦。</p>
<p><strong>Go 为什么紧随其后？</strong></p>
<p>因为 Go 完美地继承了 C 的“诚实”，同时补上了“安全”的短板。</p>
<p>在“No nonsense”这一层，Go 与 Lua（极简脚本）、ASM（汇编）并列。这说明在作者眼中，<strong>Go 的本质不是“简化的 Java”，而是“现代化的 C”。</strong></p>
<ul>
<li><strong>舒适在“透明”</strong>：看到一行 Go 代码，你基本能准确预估它的运行代价。没有隐式类型转换，没有构造函数里的黑魔法。代码写成什么样，逻辑就怎么跑。</li>
<li><strong>舒适在“克制”</strong>：Go 只有 25 个关键字。它拒绝了许多“看起来很酷”的特性（如三元运算符、复杂的元编程），只为了让你在读代码时，不需要在大脑里运行一个复杂的解析器。</li>
</ul>
<p>Go 处于这个位置，是因为它保留了 C 的<strong>掌控感</strong>，同时剔除了 C 的<strong>恐惧感</strong>（内存泄漏、野指针）。</p>
<h2>下层的窒息感：为何 Java 和 C++ 是“憎恶”？</h2>
<p>再往下看，最底层的“Abomination”包含了 C++、Java、Python 等工业界巨头。这并非说它们不能干活，而是说用它们干活<strong>“很不舒服”</strong>。</p>
<p>在这个“极简主义”的评价体系里，这些语言代表了<strong>“过度设计”</strong>的极端：</p>
<ul>
<li><strong>C++ 的认知负担</strong>：你想写个 Hello World，却迷失在模板元编程、右值引用和 20 种初始化方式的迷宫里。</li>
<li><strong>Java 的官僚主义</strong>：AbstractSingletonProxyFactoryBean……你写的不是代码，是填空题。层层叠叠的抽象，让代码与其运行的硬件彻底失联。</li>
</ul>
<p><strong>Go 的舒适区，建立在对这种“复杂性”的拒绝之上。</strong> 在 Go 里，你不需要画 UML 图，不需要背诵设计模式，你只需要关注：数据怎么流，逻辑怎么走。</p>
<h2>侧面的焦虑感：为何 Rust 是“彻底失败”？</h2>
<p>这是最引发争议的一点。Rust 被归为“Total failure”。这显然不是指 Rust 的技术失败，而是指它<strong>违背了“No nonsense”的初衷</strong>。</p>
<p>Rust 为了追求内存安全和零成本抽象，引入了极高的认知成本（生命周期、借用检查）。这导致写 Rust 代码时，开发者往往在与编译器搏斗，而不是在解决业务问题。</p>
<p><strong>Go 的舒适，是一种“妥协的艺术”。</strong></p>
<p>Go 承认：与其让人脑去计算每一个变量的生命周期（Rust 的做法），不如让 CPU 多跑几毫秒来做 GC（Go 的做法）。</p>
<p>在这个算力过剩而人脑算力稀缺的时代，Go 选择了<strong>让人舒服</strong>，而不是让机器舒服。</p>
<h2>小结：拒绝废话，回归本质</h2>
<p>这张图之所以能引起共鸣，是因为它精准地击中了现代软件工程的痛点：<strong>我们花了太多时间在对付语言特性、框架和工具链，却忘了我们最初只是想写程序解决问题。</strong></p>
<p>Go 语言处于 <strong>No nonsense</strong> 这一层，恰恰证明了它的核心价值：</p>
<p>它不追求“纯粹”的完美（像 Haskell），也不追求“极致”的性能（像 Rust），更不追求“大而全”的框架（像 Java）。</p>
<p><strong>Go 只是想让你舒服地、直白地、没有废话地，把代码写出来，然后按时下班。</strong></p>
<p>在当今这个充满焦虑的技术世界里，这难道不是最顶级的“舒适区”吗？^_^</p>
<hr />
<p><strong>你的“鄙视链”排位</strong></p>
<p>这张图虽然偏激，但确实代表了一些人心中的极简主义的审美。<strong>在你心中的编程语言金字塔里，谁是那个“唯一的真神”？谁又是让你痛苦不堪的“不可名状之物”？你认同把 Rust 放在“彻底失败”这一层吗？</strong></p>
<p><strong>欢迎在评论区晒出你的“私房排位表”，或者为你的本命语言辩护！</strong> (请文明交流，勿伤和气~ )</p>
<p><strong>如果这篇文章戳中了你的笑点或痛点，别忘了点个【赞】和【在看】，看看你的朋友圈里有多少“极简主义者”！</strong></p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/01/07/go-language-comfort-zone-in-contempt-chain-pyramid/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>逃离 Java 的“自行车棚”：Go 语言真的是那片“净土”吗？</title>
		<link>https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land/</link>
		<comments>https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land/#comments</comments>
		<pubDate>Thu, 18 Dec 2025 00:23:08 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[BikeShedding]]></category>
		<category><![CDATA[Checkstyle]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[EngineeringEfficiency]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[golangci-lint]]></category>
		<category><![CDATA[IdiomaticGo]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[linter]]></category>
		<category><![CDATA[NotInventedHere]]></category>
		<category><![CDATA[Optional]]></category>
		<category><![CDATA[PR]]></category>
		<category><![CDATA[PullRequest]]></category>
		<category><![CDATA[TeamCulture]]></category>
		<category><![CDATA[代码审查]]></category>
		<category><![CDATA[团队文化]]></category>
		<category><![CDATA[工程效率]]></category>
		<category><![CDATA[帕金森定律]]></category>
		<category><![CDATA[开发体验]]></category>
		<category><![CDATA[技术迁移]]></category>
		<category><![CDATA[极简设计]]></category>
		<category><![CDATA[自行车棚效应]]></category>
		<category><![CDATA[设计模式]]></category>
		<category><![CDATA[语法糖]]></category>
		<category><![CDATA[语言特性]]></category>
		<category><![CDATA[静态分析]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5553</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land 大家好，我是Tony Bai。 “如果每次我看到‘为什么不这么写？’这种针对完美代码的 PR 评论都能得到一分钱，我现在已经退休了。” 近日，一位在 r/golang 社区发帖的开发者发出了这样的咆哮。他受够了 Java 生态中那种无休止的、关于细枝末节的争论——也就是所谓的“自行车棚效应”(Bike Shedding)。他正在认真考虑转向 Go 语言。 但问题是，换一门语言，真的就能彻底摆脱人性的弱点，找到那份久违的“简单”与“高效”吗？ 这篇帖子迅速引发了数百条评论的热议，并形成了一次关于团队文化、工程效率以及如何在代码审查中保持理性的深度反思。 科普小贴士：什么是“自行车棚效应” (Bike Shedding)？ 这个概念源自帕金森定律。典故是：一个委员会在审批核电站计划时，对极其复杂的反应堆设计匆匆通过（因为太难懂，大家不敢轻易发言），却为了旁边自行车棚该漆成什么颜色而争论了一个小时（因为每个人都能懂，都想发表意见）。 在软件开发中，它特指团队在无关紧要的琐事（如代码风格、变量命名、语法糖）上浪费大量时间，而忽略了真正重要的系统设计和逻辑问题。 审视“自行车棚”—— 无效争论的代价 发帖人的痛苦，源于一种许多开发者都深有体会的经历：PR 被琐碎的个人偏好所劫持。 症状：代码逻辑正确、测试通过、符合规范。但审查者依然会问：“为什么不用 Optional.of(&#8230;).orElse(&#8230;) 而是用 if-else？” 潜台词：“如果是我的话，我会这么写。我要指出这一点，以显示我有在认真看代码，而且我很聪明。” 后果： 时间浪费：为了一个不影响功能的改动，需要重新提交代码、等待 CI 跑完（在企业级 Java 项目中可能长达 20-30 分钟）、等待再次审查。 士气低落：开发者感到自己的工作不被尊重，变成了为了满足审查者个人喜好而工作的“打字员”。 正如一位评论者尖锐指出的：“这不仅是语言问题，更是‘人’的问题。” 有些开发者倾向于过度工程化，为了使用设计模式而使用设计模式，而忽略了代码的实际价值。 Go 是解药吗？—— “强制统一”带来的自由 为什么发帖人认为 Go 是解药？因为 Go 的设计哲学确实在很大程度上抑制了“自行车棚”的滋生土壤。 1. 极简的语法与“唯一解” Go 语言的设计哲学是“少即是多”。它没有 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/escaping-java-bicycle-shed-is-go-the-pure-land-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land">本文永久链接</a> &#8211; https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land</p>
<p>大家好，我是Tony Bai。</p>
<p>“如果每次我看到‘为什么不这么写？’这种针对完美代码的 PR 评论都能得到一分钱，我现在已经退休了。”</p>
<p>近日，一位在 r/golang 社区发帖的开发者<a href="https://www.reddit.com/r/golang/comments/1pechqt/who_else_has_or_wants_to_move_from_java_to_go/">发出了这样的咆哮</a>。他受够了 Java 生态中那种无休止的、关于细枝末节的争论——也就是所谓的“自行车棚效应”(Bike Shedding)。他正在认真考虑转向 Go 语言。</p>
<p><strong>但问题是，换一门语言，真的就能彻底摆脱人性的弱点，找到那份久违的“简单”与“高效”吗？</strong></p>
<p>这篇帖子迅速引发了数百条评论的热议，并形成了一次关于<strong>团队文化</strong>、<strong>工程效率</strong>以及<strong>如何在代码审查中保持理性</strong>的深度反思。</p>
<blockquote>
<hr />
<p><strong>科普小贴士：什么是“自行车棚效应” (Bike Shedding)？</strong></p>
<p>这个概念源自帕金森定律。典故是：一个委员会在审批核电站计划时，对极其复杂的反应堆设计匆匆通过（因为太难懂，大家不敢轻易发言），却为了旁边<strong>自行车棚该漆成什么颜色</strong>而争论了一个小时（因为每个人都能懂，都想发表意见）。</p>
<p>在软件开发中，它特指<strong>团队在无关紧要的琐事（如代码风格、变量命名、语法糖）上浪费大量时间，而忽略了真正重要的系统设计和逻辑问题</strong>。</p>
</blockquote>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/api-design-pattern-and-implementation-qr.png" alt="img{512x368}" /></p>
<h2>审视“自行车棚”—— 无效争论的代价</h2>
<p>发帖人的痛苦，源于一种许多开发者都深有体会的经历：<strong>PR 被琐碎的个人偏好所劫持</strong>。</p>
<ul>
<li><strong>症状</strong>：代码逻辑正确、测试通过、符合规范。但审查者依然会问：“为什么不用 Optional.of(&#8230;).orElse(&#8230;) 而是用 if-else？”</li>
<li><strong>潜台词</strong>：“如果是我的话，我会这么写。我要指出这一点，以显示我有在认真看代码，而且我很聪明。”</li>
<li><strong>后果</strong>：
<ul>
<li><strong>时间浪费</strong>：为了一个不影响功能的改动，需要重新提交代码、等待 CI 跑完（在企业级 Java 项目中可能长达 20-30 分钟）、等待再次审查。</li>
<li><strong>士气低落</strong>：开发者感到自己的工作不被尊重，变成了为了满足审查者个人喜好而工作的“打字员”。</li>
</ul>
</li>
</ul>
<p>正如一位评论者尖锐指出的：“<strong>这不仅是语言问题，更是‘人’的问题。</strong>” 有些开发者倾向于过度工程化，为了使用设计模式而使用设计模式，而忽略了代码的实际价值。</p>
<h2>Go 是解药吗？—— “强制统一”带来的自由</h2>
<p>为什么发帖人认为 Go 是解药？因为 Go 的设计哲学确实在很大程度上抑制了“自行车棚”的滋生土壤。</p>
<p><strong>1. 极简的语法与“唯一解”</strong></p>
<p>Go 语言的设计哲学是“少即是多”。它没有 Optional，没有复杂的流式操作符，没有十种不同的方式来实现同一个功能。<br />
一位资深 Gopher 指出：“在 Go 中，很少有人会争论‘为什么不这么写’，因为通常<strong>只有一种</strong>地道的写法。” gofmt 更是从根本上消灭了关于格式化的争论。</p>
<p><strong>2. “Not Invented Here” vs. “Just Copy It”</strong></p>
<p>Java 生态倾向于构建通用的、高度抽象的框架。而 Go 社区更推崇“复制一点代码胜过引入一点依赖”。这种文化鼓励简单、直接的实现，而不是过早的抽象。<br />
评论区有人提到：“在 Go 中，如果你看到重复代码，你可能会选择容忍它；但在 Java 中，你会被要求抽象出一个通用的 Factory 模式。”</p>
<p><strong>3. 工具链的胜利</strong></p>
<p>Go 的工具链（lint, vet）非常强大且统一。如果一个问题可以通过静态分析发现，那就交给机器去阻止，而不是在 PR 中由人来指指点点。</p>
<h2>硬币的另一面 —— Go 社区的“新自行车棚”</h2>
<p>然而，逃离了 Java，就能彻底摆脱“自行车棚”吗？社区的声音并非一边倒。<strong>Go 并不是没有任何争论的乌托邦。</strong></p>
<ul>
<li><strong>新的争论点</strong>：虽然不再争论 Optional，但 Go 社区也有自己的“圣战”：
<ul>
<li>“为什么你要用这个第三方库？标准库不够好吗？”</li>
<li>“这个 struct 应该放在 pkg 目录下吗？”</li>
<li>“为什么你要定义这个接口？让消费者去定义！”</li>
</ul>
</li>
<li><strong>过度的“地道”追求</strong>：有时，对“Idiomatic Go”（地道 Go 代码）的追求也会演变成一种教条主义。一位评论者分享了自己的经历：仅仅因为不想在代码中看到哪怕一点点“Java 味”，审查者就拒绝了一个完美运行的 PR。</li>
</ul>
<p>由此看来，Go 虽然减少了语法的复杂性，从而减少了<em>语法层面</em>的争论空间，但它也无法消除<em>人类</em>对于微小差异的执着。</p>
<h2>给所有团队的“防杠指南”</h2>
<p>无论你使用 Java 还是 Go，如何建立一个健康的 Code Review 文化才是根本。社区贡献了许多极具价值的建议：</p>
<ol>
<li><strong>区分“阻塞”与“建议”</strong>：引入明确的前缀，如 nit: (吹毛求疵，不阻塞合并)、suggestion: (建议，可不采纳)、blocker: (必须修改)。这能清晰地传达审查者的意图。</li>
<li><strong>自己动手，丰衣足食</strong>：如果审查者对某个非功能性的风格问题非常在意，且有权限，不妨直接提交一个小改动，而不是阻碍原作者的 PR。</li>
<li><strong>规则自动化</strong>：凡是能用 Linter (如 Checkstyle, golangci-lint) 解决的问题，绝不在人工审查中讨论。<strong>让机器做坏人</strong>。</li>
<li><strong>接受“足够好”</strong>：Code Review 的目标是保证代码质量、发现 Bug 和分享知识，而不是追求“完美”。<strong>完美是完成的敌人</strong>。</li>
</ol>
<h2>小结：选择语言，更是选择文化</h2>
<p>回到标题的问题：<strong>Go 是开发者的净土或避风港吗？是，也不是。</strong></p>
<p>它确实通过强制的规范和极简的设计，消灭了许多“低级”的自行车棚，提供了一种更务实、更直接的编程体验。</p>
<p>但在任何有人的地方，争论都会寻找新的出口。如果你厌倦了 Java 的复杂，Go 绝对值得尝试；但请记得，<strong>真正的避风港不在语言里，而在一个成熟、理性、相互尊重的团队文化中。</strong></p>
<p>资料链接：https://www.reddit.com/r/golang/comments/1pechqt/who_else_has_or_wants_to_move_from_java_to_go/</p>
<hr />
<p><strong>吐槽时间</strong></p>
<p>“自行车棚效应”恐怕是每个程序员心中的痛。<strong>你在代码审查中遇到过最离谱、最让你抓狂的“自行车棚”争论是什么？是关于一个变量名，还是一个缩进？</strong></p>
<p><strong>欢迎在评论区吐吐槽，让我们一起把这些“无效卷”晒在阳光下！</strong></p>
<p><strong>如果这篇文章说出了你的心声，别忘了点个【赞】和【在看】，并转发给你的团队（也许能改变点什么）！</strong></p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/12/18/escaping-java-bicycle-shed-is-go-the-pure-land/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域，AI 流量激增！</title>
		<link>https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge/</link>
		<comments>https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge/#comments</comments>
		<pubDate>Wed, 17 Dec 2025 00:25:45 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AITraffic]]></category>
		<category><![CDATA[AI流量]]></category>
		<category><![CDATA[APIClient]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<category><![CDATA[cloudflare]]></category>
		<category><![CDATA[gemini]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Googlebot]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[http3]]></category>
		<category><![CDATA[infrastructure]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[net/http]]></category>
		<category><![CDATA[nodejs]]></category>
		<category><![CDATA[Perplexity]]></category>
		<category><![CDATA[PostQuantumCryptography]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Starlink]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[互联网回顾报告]]></category>
		<category><![CDATA[后量子加密]]></category>
		<category><![CDATA[基础设施]]></category>
		<category><![CDATA[爬虫]]></category>
		<category><![CDATA[生成式AI]]></category>
		<category><![CDATA[用户行为流量]]></category>
		<category><![CDATA[自动化API客户端]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5546</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge 大家好，我是Tony Bai。 近日，互联网基础设施巨头 Cloudflare 发布了其备受瞩目的《2025 年度互联网回顾报告》。这份基于其全球庞大网络数据的报告，如同一面镜子，映照出全球互联网在流量、技术、安全和 AI 等领域的最新脉搏。 而对于我们 Go 开发者而言，今年的报告带来了两个极其振奋人心的消息： Go 语言在自动化 API 客户端领域的王者地位，不仅得以巩固，甚至还在持续扩大领先优势。 AI 相关流量和应用正在以前所未有的速度崛起，而 Go 正是这股浪潮背后不可或缺的基础设施构建者。 本文将为你深度解读这份报告中，与我们 Gopher 息息相关的核心亮点。 Go 语言：自动化 API 领域的“超级巨星” 在现代应用架构中，自动化 API 请求（即服务与服务之间的机器通信）早已成为流量的主体。而构建这些 API 客户端的编程语言，其流行度直接反映了该语言在后端和基础设施领域的真实“统治力”。 Cloudflare 的报告再次确认了一个我们早已熟知，但看到数据后依然会心潮澎湃的事实：Go 是自动化 API 客户端最受欢迎的选择。 关键数据解读： 五分之一的天下：在 2025 年，全球 20% 的自动化 API 请求，都是由基于 Go 的客户端发出的。这意味着，每五个飞驰在互联网上的自动化 API 请求中，就有一个是用 Go 编写的！ 惊人的增长势头：这一数字，相较于 2024 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/cloudflare-2025-report-go-language-api-traffic-ai-surge-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge">本文永久链接</a> &#8211; https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge</p>
<p>大家好，我是Tony Bai。</p>
<p>近日，互联网基础设施巨头 Cloudflare 发布了其备受瞩目的《<a href="https://radar.cloudflare.com/year-in-review/2025">2025 年度互联网回顾报告</a>》。这份基于其全球庞大网络数据的报告，如同一面镜子，映照出全球互联网在流量、技术、安全和 AI 等领域的最新脉搏。</p>
<p>而对于我们 Go 开发者而言，今年的报告带来了两个极其振奋人心的消息：</p>
<ol>
<li><strong>Go 语言在自动化 API 客户端领域的王者地位，不仅得以巩固，甚至还在持续扩大领先优势。</strong></li>
<li><strong>AI 相关流量和应用正在以前所未有的速度崛起，而 Go 正是这股浪潮背后不可或缺的基础设施构建者。</strong></li>
</ol>
<p>本文将为你深度解读这份报告中，与我们 Gopher 息息相关的核心亮点。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/api-design-pattern-and-implementation-qr.png" alt="" /></p>
<h2>Go 语言：自动化 API 领域的“超级巨星”</h2>
<p>在现代应用架构中，自动化 API 请求（即服务与服务之间的机器通信）早已成为流量的主体。而构建这些 API 客户端的编程语言，其流行度直接反映了该语言在后端和基础设施领域的真实“统治力”。</p>
<p>Cloudflare 的报告再次确认了一个我们早已熟知，但看到数据后依然会心潮澎湃的事实：<strong>Go 是自动化 API 客户端最受欢迎的选择。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/cloudflare-2025-report-go-language-api-traffic-ai-surge-2.png" alt="" /></p>
<p><strong>关键数据解读：</strong></p>
<ul>
<li>
<p><strong>五分之一的天下</strong>：在 2025 年，全球 <strong>20%</strong> 的自动化 API 请求，都是由基于 Go 的客户端发出的。这意味着，<strong>每五个飞驰在互联网上的自动化 API 请求中，就有一个是用 Go 编写的！</strong></p>
</li>
<li>
<p><strong>惊人的增长势头</strong>：这一数字，相较于 2024 年的 <strong>12%</strong>，实现了超过 66% 的惊人增长。Go 不仅是第一，而且正在以“断层式”的速度，进一步拉开与追赶者的差距。</p>
</li>
<li>
<p><strong>竞争格局</strong>：Python 紧随其后，份额从 9.6% 增长到 17%。而去年排名第二的 Node.js，其份额则跌至 8.3%，被 Java (11.2%) 超越。</p>
</li>
</ul>
<p><strong>为什么是 Go？</strong></p>
<p>这份数据雄辩地证明了 Go 在构建网络服务和客户端方面的核心优势：</p>
<ol>
<li><strong>极致的性能与并发</strong>：Go 的 goroutine 模型，使其能够以极低的资源开销，轻松处理海量的并发 API 请求。</li>
<li><strong>强大的标准库</strong>：net/http 标准库本身就极其强大、易用且生产力极高。</li>
<li><strong>静态二进制文件</strong>：Go 能够编译成无依赖的单一二进制文件，这对于在容器化环境中部署 API 客户端和服务，简直是“天作之合”。</li>
</ol>
<h2>AI 浪潮：新的战场，Go 的新机遇</h2>
<p>如果说 Go 在 API 领域的领先是“意料之中”，那么报告中关于 AI 流量的爆炸式增长，则为 Go 的未来描绘出了一个更加激动人心的新战场。</p>
<h3>Googlebot：AI 时代的“头号流量玩家”</h3>
<p>报告指出，连续第三年，来自 Google IP 段 66.249.64.0/20 的流量，成为 Cloudflare 网络上最大的请求来源。这背后的“巨兽”，正是 <strong>Googlebot</strong>。</p>
<p>值得注意的是，Googlebot 已经演变成一个<strong>双重目的</strong>的爬虫：它不仅为传统搜索引擎建立索引，更在为 Google 的 <strong>AI 模型（如 Gemini）进行大规模的数据抓取和训练</strong>。</p>
<blockquote>
<p>2025 年，Googlebot 贡献了超过 <strong>28%</strong> 的“已验证机器人”流量，其爬取量远超 OpenAI 的 GPTBot (7.5%) 和微软的 Bingbot (6%)。</p>
</blockquote>
<p><img src="https://tonybai.com/wp-content/uploads/2025/cloudflare-2025-report-go-language-api-traffic-ai-surge-3.png" alt="" /></p>
<h3>AI 用户行为流量激增 15 倍</h3>
<p>报告将 AI 爬虫流量分为三类：训练 (training)、搜索 (search) 和<strong>用户行为 (user action)</strong>。其中，“用户行为”指的是当用户在 ChatGPT 等应用中提问，AI 为了回答问题而去实时访问外部网站所产生的流量。</p>
<blockquote>
<p>2025 年，这类“用户行为”驱动的 AI 爬取流量，增长了超过 <strong>15 倍</strong>！</p>
</blockquote>
<p>这预示着一个全新的互联网范式正在形成：越来越多的流量，将不再由人类直接发起，而是由 AI 智能体，为了服务于人类的需求而发起。</p>
<h3>Go 在 AI 基础设施中的角色</h3>
<p>这对 Go 开发者意味着什么？</p>
<p>AI 模型本身或许由 Python 主导，但支撑这些模型进行大规模数据爬取、数据处理、模型服务（API serving）的<strong>庞大基础设施</strong>，正是 Go 语言大显身手的领域。</p>
<p>当你看到 ChatGPT、Perplexity 等服务的流量排名在“生成式 AI 服务”榜单中不断攀升时，可以想见，其背后必然有无数由 Go 编写的高性能 API 网关、数据管道和后端服务在默默支撑。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/cloudflare-2025-report-go-language-api-traffic-ai-surge-4.png" alt="" /></p>
<h2>其他值得关注的趋势</h2>
<ul>
<li><strong>后量子加密曙光</strong>：由人类产生的、采用<a href="https://tonybai.com/2025/05/20/post-quantum-cryptography-in-go"><strong>后量子加密</strong></a>的 Web 流量份额，在 2025 年从年初的 29% <strong>增长到了 52%</strong>。这主要得益于苹果在 iOS 等操作系统中默认开启了对<a href="https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union">混合量子安全密钥交换</a>的支持。</li>
<li><strong>HTTP/3 稳步增长</strong>：全球使用 HTTP/3 和 HTTP/2 的 Web 请求份额都在微弱增长，HTTP/3 的占比达到了 21%。</li>
<li><strong>Starlink 流量翻倍</strong>：卫星互联网服务 Starlink 的流量在 2025 年翻了一番，显示出其在全球“连接未连接者”方面的巨大潜力。</li>
</ul>
<h2>小结语：站在时代的潮头</h2>
<p>Cloudflare 的 2025 年度报告，为我们描绘了一幅激动人心的画卷。在这幅画卷中，Go 语言不仅是当前<strong>云原生和 API 经济</strong>的绝对王者，更是即将到来的 <strong>AI 时代</strong>不可或缺的核心基础设施构建者。</p>
<p>“五分之一的 API 请求由 Go 发出”——这个数据，不仅仅是一个值得骄傲的里程碑，更是对 Go 语言设计哲学——<strong>简单、高效、并发</strong>——在真实世界中取得巨大成功的最有力证明。作为 Gopher，我们正站在时代的潮头。</p>
<p>资料链接：</p>
<ul>
<li>https://blog.cloudflare.com/radar-2025-year-in-review/</li>
<li>https://radar.cloudflare.com/year-in-review/2025</li>
</ul>
<hr />
<p><strong>你的“体感”如何？</strong></p>
<p>数据告诉我们，Go 正在制霸 API 领域。<strong>在你日常的工作中，是否也感受到了 Go 在微服务、网关或 AI 基础设施中的统治力？或者，你观察到了 Python 或 Rust 在哪些特定领域正在发起挑战？</strong></p>
<p><strong>欢迎在评论区分享你的一线观察，让我们一起拼凑出更真实的技术版图！</strong></p>
<p><strong>如果这篇文章让你对 Go 的未来更有信心，别忘了点个【赞】和【在看】，并转发给你的团队！</strong></p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/12/17/cloudflare-2025-report-go-language-api-traffic-ai-surge/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如果《疯狂动物城》是一个分布式系统，那它一定是用 Go 写的</title>
		<link>https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go/</link>
		<comments>https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go/#comments</comments>
		<pubDate>Sat, 06 Dec 2025 14:05:50 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[影音坊]]></category>
		<category><![CDATA[技术志]]></category>
		<category><![CDATA[BlockingI/O]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cgroup]]></category>
		<category><![CDATA[cloudnative]]></category>
		<category><![CDATA[distributedsystem]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[GMP]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[Namespace]]></category>
		<category><![CDATA[QPS]]></category>
		<category><![CDATA[Zootopia]]></category>
		<category><![CDATA[Zootopia2]]></category>
		<category><![CDATA[分布式系统]]></category>
		<category><![CDATA[协程]]></category>
		<category><![CDATA[容器化]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[架构师]]></category>
		<category><![CDATA[环境隔离]]></category>
		<category><![CDATA[疯狂动物城]]></category>
		<category><![CDATA[疯狂动物城2]]></category>
		<category><![CDATA[调度模型]]></category>
		<category><![CDATA[高并发]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5489</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go 大家好，我是Tony Bai。 文章开始前，先给各位道个歉，今天的标题确实有点“党”。 毕竟，非要说一个满是毛茸茸动物的动画片是用 Go 语言写的，这脑洞开得确实有点大。 但请原谅一个老程序员的“职业病”。 为了迎接本周末《疯狂动物城2》的观影家庭活动，上个周末，我特意腾出时间，陪家里5岁的二娃重温了第一部经典。原本我是想好好享受亲子时光的，可看着看着，作为写了十几年代码的程序员，我的关注点却莫名其妙地“跑偏”了。 当看到那座容纳了冰川、沙漠、雨林，拥有千万级“居民并发量”的超级城市运转得如此丝滑时，我脑子里的画面变了：这越看越像一个设计精良的云原生分布式系统；而那个身手敏捷的兔子警官，怎么看都像一只跑在服务器里的 Gopher…… 于是，我忍不住这股“胡思乱想”的冲动，决定一本正经地胡说八道一番。 如果你也好奇，当一个架构师戴着“代码滤镜”看电影时，到底看到了什么？不妨继续听我聊聊 在我眼里，如果要把这座“动物城”搬到服务器上，它的底层架构，一定是用 Go 语言写的。 为什么这么说？因为陪娃看电影的过程中，我仿佛看到了 Go 语言设计哲学的完美具象化。 那个巨大的“空调墙”与容器化 电影最震撼的一幕，莫过于朱迪坐火车进城。 火车穿过烈日炎炎的撒哈拉广场（Sahara Square），下一秒就钻进了冰天雪地的冰川镇（Tundratown）。 女儿指着屏幕好奇地问我：“爸爸，为什么那边那么热，这边这么冷，它们在一起不会化掉吗？” 我指着那道巨大的分隔墙说：“因为有那堵墙呀，它把热气和冷气隔开了。” 在那一刻，我脑子里闪过的其实是 Docker 和 Kubernetes。 在传统的系统里，不同环境的应用混在一起很容易“打架”（环境冲突）。而在动物城里，为了让北极熊（需要低温库）和骆驼（需要高温环境）在同一台“物理机”上共存，设计师构建了最极致的环境隔离。 这不正是 Go 语言统治的云原生世界吗？ Go 语言构建了 Docker，构建了 Kubernetes。正是这些基础设施，像那道巨大的空调墙一样，通过 Namespace（命名空间）和 Cgroup（资源限制），让成千上万个习性迥异的“服务”互不干扰，在此消彼长的流量洪峰中，不仅没“化掉”，还活得很好。 树懒“闪电”与高并发的噩梦 重温经典，依然被树懒“闪电”查车牌那段笑出内伤。 女儿笑得在沙发上捧腹：“爸爸，他太慢了！朱迪急死了！” 我跟着笑，但心里却是一阵恶寒——这简直是每一个后端工程师的噩梦：主线程阻塞（Blocking I/O）。 试想一下，如果动物城的市政大厅系统是单线程的，一只树懒卡在窗口办业务，后面排队的一万只动物全得等着。整个城市的吞吐量（QPS）瞬间归零，系统直接宕机。 但动物城（Zootopia）作为一个千万人口的超大系统，依然运转良好，说明它底层一定解决了这个问题。 如果是用 Go 写的，这就很好解释了。 Go 的设计哲学里，最核心的就是“高并发”。面对慢吞吞的“树懒式”任务（比如网络等待、文件读取），Go 不会傻等。它会派出一个轻量级的 goroutine（协程）去盯着树懒，主线程立马转头去处理下一只豹子或兔子的请求。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/zootopia-distributed-system-written-in-go-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go">本文永久链接</a> &#8211; https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go</p>
<p>大家好，我是Tony Bai。</p>
<p><strong>文章开始前，先给各位道个歉，今天的标题确实有点“党”。</strong></p>
<p>毕竟，非要说一个满是毛茸茸动物的动画片是用 Go 语言写的，这脑洞开得确实有点大。</p>
<p>但请原谅一个老程序员的“职业病”。</p>
<p>为了迎接本周末《疯狂动物城2》的观影家庭活动，上个周末，我特意腾出时间，陪家里5岁的二娃重温了第一部经典。原本我是想好好享受亲子时光的，可看着看着，作为写了十几年代码的程序员，我的关注点却莫名其妙地“跑偏”了。</p>
<p>当看到那座容纳了冰川、沙漠、雨林，拥有千万级“居民并发量”的超级城市运转得如此丝滑时，我脑子里的画面变了：这越看越像一个设计精良的<strong>云原生分布式系统</strong>；而那个身手敏捷的兔子警官，怎么看都像一只跑在服务器里的 <strong>Gopher</strong>……</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/zootopia-distributed-system-written-in-go-2.jpg" alt="" /></p>
<p>于是，我忍不住这股“胡思乱想”的冲动，决定一本正经地胡说八道一番。</p>
<p><strong>如果你也好奇，当一个架构师戴着“代码滤镜”看电影时，到底看到了什么？不妨继续听我聊聊</strong></p>
<p>在我眼里，如果要把这座“动物城”搬到服务器上，它的底层架构，一定是用 <strong>Go 语言</strong>写的。</p>
<p>为什么这么说？因为陪娃看电影的过程中，我仿佛看到了 Go 语言设计哲学的完美具象化。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/google-adk-in-action-qr.png" alt="" /></p>
<h2>那个巨大的“空调墙”与容器化</h2>
<p>电影最震撼的一幕，莫过于朱迪坐火车进城。</p>
<p>火车穿过烈日炎炎的撒哈拉广场（Sahara Square），下一秒就钻进了冰天雪地的冰川镇（Tundratown）。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/zootopia-distributed-system-written-in-go-3.jpg" alt="" /></p>
<p>女儿指着屏幕好奇地问我：“爸爸，为什么那边那么热，这边这么冷，它们在一起不会化掉吗？”</p>
<p>我指着那道巨大的分隔墙说：“因为有那堵墙呀，它把热气和冷气隔开了。”</p>
<p>在那一刻，我脑子里闪过的其实是 <strong>Docker 和 Kubernetes</strong>。</p>
<p>在传统的系统里，不同环境的应用混在一起很容易“打架”（环境冲突）。而在动物城里，为了让北极熊（需要低温库）和骆驼（需要高温环境）在同一台“物理机”上共存，设计师构建了最极致的<strong>环境隔离</strong>。</p>
<p>这不正是 Go 语言统治的云原生世界吗？</p>
<p>Go 语言构建了 Docker，构建了 Kubernetes。正是这些基础设施，像那道巨大的空调墙一样，通过 Namespace（命名空间）和 Cgroup（资源限制），让成千上万个习性迥异的“服务”互不干扰，在此消彼长的流量洪峰中，不仅没“化掉”，还活得很好。</p>
<h2>树懒“闪电”与高并发的噩梦</h2>
<p>重温经典，依然被树懒“闪电”查车牌那段笑出内伤。</p>
<p>女儿笑得在沙发上捧腹：“爸爸，他太慢了！朱迪急死了！”</p>
<p>我跟着笑，但心里却是一阵恶寒——这简直是每一个后端工程师的噩梦：<strong>主线程阻塞（Blocking I/O）</strong>。</p>
<p>试想一下，如果动物城的市政大厅系统是单线程的，一只树懒卡在窗口办业务，后面排队的一万只动物全得等着。整个城市的吞吐量（QPS）瞬间归零，系统直接宕机。</p>
<p>但动物城（Zootopia）作为一个千万人口的超大系统，依然运转良好，说明它底层一定解决了这个问题。</p>
<p>如果是用 <strong>Go</strong> 写的，这就很好解释了。</p>
<p>Go 的设计哲学里，最核心的就是<strong>“高并发”</strong>。面对慢吞吞的“树懒式”任务（比如网络等待、文件读取），Go 不会傻等。它会派出一个轻量级的 goroutine（协程）去盯着树懒，主线程立马转头去处理下一只豹子或兔子的请求。</p>
<p>在这个庞大的系统里，也许有成千上万只“树懒”在慢动作，但整个城市依然像朱迪一样反应灵敏、健步如飞。这就是 Go 语言 GMP 调度模型的魔力。</p>
<h2>朱迪警官：小身材，大能量</h2>
<p>最后，说说我们的主角，兔子朱迪。</p>
<p>在满是大象、犀牛、北极熊的警局里，朱迪显得太小了。她没有庞大的身躯，起初也不被看好，被安排去贴罚单。</p>
<p>这像极了 Go 语言刚诞生时的处境。相比于 Java（大象）的厚重、C++（犀牛）的复杂，Go 显得语法简单、标准库精简，甚至生成的二进制文件都很小，一度被认为是“玩具语言”。</p>
<p>但朱迪凭什么破了大案？</p>
<p><strong>靠的是灵活性、执行力和低资源消耗。</strong></p>
<p>她能钻进犀牛进不去的狭窄管道（相对低内存的占用），她能在他人的视野盲区快速穿梭（极速启动）。</p>
<p>在构建现代微服务架构时，我们越来越不喜欢笨重的“单体应用”，而倾向于像朱迪这样<strong>小而美、独立部署、逻辑清晰</strong>的服务。</p>
<p>Go 语言就是代码世界里的“朱迪”。它剔除了所有花哨的语法糖，强制你写出清晰（甚至有点死板）的代码，但正是这种克制和高效，让它成为了支撑起整个动物城（云原生生态）最坚实的骨架。</p>
<h2>写在最后</h2>
<p>电影结束了，女儿意犹未尽，还在模仿朱迪的动作。</p>
<p>她问我：“爸爸，下周我们去看《疯狂动物城》第二部，朱迪会不会变得更厉害？”</p>
<p>我说：“肯定会啊，因为她一直在努力让这个城市变得更好。”</p>
<p>作为程序员，我们写下的每一行代码，何尝不是在构建一个虚拟的“动物城”？我们选择 Go，选择各种架构，不过是为了让这个系统更包容、更稳定，让里面的“居民”生活得更好。</p>
<p><strong>这周末，我将带娃直击《疯狂动物城2》。</strong> 听说这一次，动物城面临了前所未有的复杂危机。</p>
<p>届时，我会继续为大家带来<strong>“程序员眼中的《疯狂动物城2》”</strong>，看看在新的挑战下，我们的“系统架构”又该如何进化？</p>
<p>敬请期待！</p>
<hr />
<p><strong>互动话题：</strong></p>
<p>在重温经典电影时，你有没有因为“职业病”而产生过什么奇怪的联想？欢迎在评论区分享你的脑洞！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>为什么 Go 在悄悄地做 Rust 做不到的事：保持简单</title>
		<link>https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple/</link>
		<comments>https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple/#comments</comments>
		<pubDate>Fri, 21 Nov 2025 00:29:50 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[build.rs]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[cargo]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[crates]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JIT]]></category>
		<category><![CDATA[Medium]]></category>
		<category><![CDATA[monad]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[slack]]></category>
		<category><![CDATA[Swiggy]]></category>
		<category><![CDATA[SystemProgramming]]></category>
		<category><![CDATA[TonyBai]]></category>
		<category><![CDATA[Zig]]></category>
		<category><![CDATA[二进制简洁性]]></category>
		<category><![CDATA[借用检查器]]></category>
		<category><![CDATA[内存安全]]></category>
		<category><![CDATA[团队]]></category>
		<category><![CDATA[复杂性]]></category>
		<category><![CDATA[安全性]]></category>
		<category><![CDATA[工程化实践]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[所有权]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[显式优于隐式]]></category>
		<category><![CDATA[权力]]></category>
		<category><![CDATA[构建系统]]></category>
		<category><![CDATA[清晰]]></category>
		<category><![CDATA[炒作]]></category>
		<category><![CDATA[生产环境]]></category>
		<category><![CDATA[知识体系]]></category>
		<category><![CDATA[确定性构建系统]]></category>
		<category><![CDATA[简单]]></category>
		<category><![CDATA[系统编程]]></category>
		<category><![CDATA[编译速度]]></category>
		<category><![CDATA[舒适]]></category>
		<category><![CDATA[认知税]]></category>
		<category><![CDATA[设计哲学]]></category>
		<category><![CDATA[软件设计]]></category>
		<category><![CDATA[重构]]></category>
		<category><![CDATA[错误处理]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5418</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple 大家好，我是Tony Bai。 近日，一篇题为《为什么 Zig 在悄悄地做 Rust 做不到的事：保持简单》的文章在开发者社区引发了热议。文章以其辛辣、富有煽动性的文风，将 Zig 描绘成 Rust 复杂性的“解毒剂”，是“一个终于接受了心理治疗的 C 项目”，并引发了关于“简单性”与“安全性”的深刻辩论。 这不禁让我们——作为 Go 社区的观察者——产生了一个有趣的想法：如果我们将文中的主角 Zig，完全替换为 Go，这篇文章的论点是否依然成立？ Go 语言，在其诞生之初，同样被视为对 C++ 等语言复杂性的“反叛”。它与 Zig 在追求编译速度、二进制简洁性以及“显式优于隐式”的哲学上，有着惊人的相似之处。 于是，我们进行了一次大胆的“思想实验”：在保留原文犀利风格和核心论证结构的前提下，将所有关于 Zig 的部分都替换为 Go，并将代码示例“翻译”为地道的 Go 代码。 这并非意在挑起 Go 与 Rust 之间的“战争”，而是希望通过这样一次“角色扮演”，从一个全新的、极具张力的视角，来重新审视 Go 语言的设计哲学，以及它在现代编程语言光谱中所占据的那个独特、宝贵且时常被误解的位置。 以下，便是这次思想实验的成果。各位小伙伴儿品一品，这样替换后，是不是不仅完美地道出了 Go 在“简单”与“显式”上的坚持，更说出了许多 Gopher 心里想说，却又不好意思直接对 Rust 爱好者说出口的‘真心话’？ Rust 对安全性大声疾呼。Go 只是把它构建了进去——没有那些仪式感、没有那些说教、也没有那 15 分钟的编译时间。 引子 我第一次写 Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/why-go-is-quietly-doing-what-rust-couldnt-staying-simple-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple">本文永久链接</a> &#8211; https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple</p>
<p>大家好，我是Tony Bai。</p>
<p>近日，一篇题为《<a href="https://freedium-mirror.cfd/@daxx5/why-zig-is-quietly-doing-what-rust-couldnt-staying-simple-a47f86b3a58a">为什么 Zig 在悄悄地做 Rust 做不到的事：保持简单</a>》的文章在开发者社区引发了热议。文章以其辛辣、富有煽动性的文风，将 Zig 描绘成 Rust 复杂性的“解毒剂”，是“一个终于接受了心理治疗的 C 项目”，并引发了关于“简单性”与“安全性”的深刻辩论。</p>
<p>这不禁让我们——作为 Go 社区的观察者——产生了一个有趣的想法：<strong>如果我们将文中的主角 Zig，完全替换为 Go，这篇文章的论点是否依然成立？</strong></p>
<p>Go 语言，在其诞生之初，同样被视为对 C++ 等语言复杂性的“反叛”。它与 Zig 在追求编译速度、二进制简洁性以及“显式优于隐式”的哲学上，有着惊人的相似之处。</p>
<p>于是，我们进行了一次大胆的“思想实验”：在保留原文犀利风格和核心论证结构的前提下，将所有关于 Zig 的部分都替换为 Go，并将代码示例“翻译”为地道的 Go 代码。</p>
<p>这并非意在挑起 Go 与 Rust 之间的“战争”，而是希望通过这样一次“角色扮演”，从一个全新的、极具张力的视角，来重新审视 Go 语言的设计哲学，以及它在现代编程语言光谱中所占据的那个独特、宝贵且时常被误解的位置。</p>
<p>以下，便是这次思想实验的成果。各位小伙伴儿品一品，这样替换后，是不是不仅完美地道出了 Go 在“简单”与“显式”上的坚持，<strong>更说出了许多 Gopher 心里想说，却又不好意思直接对 Rust 爱好者说出口的‘真心话’？</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-testing-journey-qr.png" alt="" /></p>
<hr />
<p>Rust 对安全性大声疾呼。Go 只是把它构建了进去——没有那些仪式感、没有那些说教、也没有那 15 分钟的编译时间。</p>
<h2>引子</h2>
<p>我第一次写 Go 代码的时候，忍不住笑出声来。不是因为它好笑——而是因为我不敢相信，在现代编程世界里，还存在着如此……<strong>安静</strong>的东西。</p>
<p>在与 Rust “搏斗”多年之后——那门承诺将我们从 C 的苦海中拯救出来，却不知怎的变成了一场性格测试的语言——Go 感觉就像是 Rust 霓虹闪烁的都市中心里，一间温暖、极简的小木屋。</p>
<p>而这，正是关键所在。</p>
<p>Go 并非试图成为未来。它只是想保持理智。</p>
<hr />
<h2>Rust 承诺了天堂，却给了我们一堆文书工作</h2>
<p>还记得那股炒作的热潮吗？Rust 是“C 语言杀手”，是内存安全的“弥赛亚”，是<a href="https://tonybai.com/2025/09/01/system-programming-in-go">系统编程</a>的“救世主”。</p>
<p>平心而论，Rust 确实……算是兑现了。你可以写出快如闪电的安全代码——<strong>在你向借用检查器献祭了三只山羊和整个周末的心智健全之后</strong>。</p>
<p>你看着这样的代码：</p>
<pre><code class="rust">// Rust
fn main() {
    let mut data = vec![1, 2, 3];
    let ref1 = &amp;data;
    data.push(4); // 借用检查器：“凡人，你不能这么做。”
    println!("{:?}", ref1);
}
</code></pre>
<p>你会想，<strong>为什么？为什么我的编译器听起来像我的前任在解释情感边界？</strong></p>
<p>Rust 像一个严厉的治疗师一样教你所有权。而 Go 呢，只是耸耸肩说：<strong>“你搞坏了，你修好它。”</strong></p>
<p>这就是哲学的分水岭。<strong>Rust 假设你不可信。Go 假设你是个成年人。</strong></p>
<p>Rust 的才华毋庸置疑——安全、并发、无畏的重构。但它也……让人筋疲力尽。那些仪式感。那些工具链。那种将过度工程伪装成纯粹性的文化。</p>
<p>而 Go 呢，穿着连帽衫，拿着半个三明治出现，说：“嘿，想不想直接把该死的二进制文件构建出来？”</p>
<hr />
<h2>无聊之美</h2>
<p>这是大多数人忽略的一点：<strong>简单不是一个特性。它是一种反叛。</strong></p>
<p>Go 看起来很无聊。感觉也很无聊。读起来就像一个终于接受了心理治疗的 C 项目。</p>
<pre><code class="go">// Go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
</code></pre>
<p>就是这样。没有宏。没有 build.rs。没有 Cargo 尖叫着说哪个 crate 过期了。</p>
<p><strong>仅仅。一个。编译器。</strong></p>
<p>其底层呢？一个能让你团队喜极而泣的设计：</p>
<ul>
<li><strong>没有隐藏的控制流。</strong></li>
<li><strong>没有未定义行为。</strong></li>
<li>** 没有运行时的“惊吓” (No runtime surprises)**。（即，没有像 JIT 或复杂后台进程那样，会产生不可预测行为的“魔法”运行时）</li>
<li><strong>一个像钟表一样精确工作的确定性构建系统。</strong></li>
</ul>
<p>你可以去读 Go 编译器的源码，并且<strong>真的能读懂它</strong>。你去试试读 Rust 的编译器源码，那你需要咖啡因、心理治疗和一个祈祷小组。</p>
<p>Go 不性感。它很实用。它是那种你会忘记你正在使用的语言——而这，是最高的赞美。</p>
<hr />
<h2>Rust 扩展了代码库，Go 扩展了人类</h2>
<p>说实话吧——Rust 最大的优点也是它最大的诅咒：<strong>它迫使你思考。不停地思考。</strong></p>
<p>每一行代码都是一场关于生命周期、可变性和宇宙正义的哲学辩论。</p>
<p>Go 呢？Go 就像是说：“嘿，这是内存。别把自己捅了就行。” (笔者注：Go是GC语言，这句直接替换zig后的表达可能不是很契合)</p>
<p>这很重要。尤其是在团队中。</p>
<p>Rust 感觉像学术界——人们在 Slack 上辩论着 monad，而功能的截止日期却在悄悄溜走。Go 感觉像那个穿着脏兮兮运动鞋、代码却能跑起来的初创公司工程师。</p>
<p>在 Swiggy 这样的规模下，<strong>Go 取代了 Java 后端，因为它扩展了开发团队</strong>。Go 也许正在悄悄地为系统编程做同样的事情——不是因为它“更好”，而是因为它<strong>更人性化</strong>。 (笔者注：由于有特定背景局限，这里将zig替换为Go后可能也不是很契合了)</p>
<p>你不需要一块精神白板来在脑中记住 12 条借用规则。你只需要……写。</p>
<hr />
<h2>讽刺的转折：Go 才是 Rust 假装要成为的样子</h2>
<p>Rust 将自己营销为“安全的系统编程”。但它实际上是——一个<strong>系统框架</strong>。</p>
<p>Cargo、crates、宏、过程魔法——这是一个生态系统，而不是一门语言。华丽，但沉重。</p>
<p>Go 把所有这些都剥离了。</p>
<p>没有依赖爆炸。没有语言版本混乱。没有每夜构建的轮盘赌。</p>
<p>最关键的是——Go 的构建系统是如此集成，如此具有确定性，以至于整个 CI/CD 的设置都感觉更清爽了。</p>
<p><strong>Rust 像一座现代大教堂一样构建。Go 像一条工具腰带一样构建。</strong></p>
<blockquote>
<p>“Go 不试图保护你。它试图赋予你力量。”</p>
</blockquote>
<p>这就是那场安静的反叛。Go 相信你知道自己在做什么——它只给你足够的绳子让你把事情绑在一起，而不是让你上吊。</p>
<p>而讽刺的是什么？Go 中那些“不安全”的部分，在实践中往往最终更安全，因为<strong>你能看到一切</strong>。没有魔法。没有语法糖。只有原始的意图。</p>
<hr />
<h2>当炒作退去，简单性胜出</h2>
<p>每个技术周期都以同样的方式结束。</p>
<p>炒作机器火力全开。Medium 上的文章成倍增加。Meme 如潮水般涌来。然后有一天——凌晨两点，生产环境着火了，你只想知道为什么该死的二进制文件崩溃了。</p>
<p><strong>Rust 给了你安全。但 Go 给了你清晰。</strong></p>
<pre><code class="go">// Go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Create("output.txt")
    if err != nil {
        // 你能清晰地看到错误处理
        panic(err)
    }
    defer file.Close()

    _, err = file.WriteString("Explicit is better than implicit.")
    if err != nil {
        panic(err)
    }
}
</code></pre>
<p>你简直可以追踪到每一个字节。没有隐藏的分配器。没有神秘之处。</p>
<p>这正是老派 C 开发者所怀念的那种控制感——但现代开发者却忘记了自己也需要这种感觉。</p>
<h2>这场安静革命的教训</h2>
<ul>
<li><strong>简单是一种权力</strong>。你的语言越可预测，你付出的认知税就越少。</li>
<li><strong>安全不是舒适</strong>。Rust 让你感到安全，但筋疲力尽。Go 让你感到暴露，但一切尽在掌握。</li>
<li><strong>你不需要另一个抽象。你需要更少的抽象。</strong></li>
<li><strong>有时，无聊会赢</strong>。因为无聊的东西能扩展、能调试、能交付。</li>
</ul>
<h2>最后的思考</h2>
<p>Rust 将继续演进。它配得上它的王座。但在某个地方，有一支小团队正在用 Go 构建——没有炒作，没有技术大会演讲，没有花哨的市场营销。</p>
<p>只是在悄悄地编写着那些永不崩溃、编译只需几秒、在生产环境中如幽灵般运行的干净的二进制文件。</p>
<p>这就是没人预见到的转折。Go 并非在与 Rust 的未来竞争。它在复活编程的过去——我们早已遗忘的那些美好部分。</p>
<p>而且，也许，仅仅是也许，这就是它最终获胜的方式。</p>
<p>资料链接：https://freedium-mirror.cfd/@daxx5/why-zig-is-quietly-doing-what-rust-couldnt-staying-simple-a47f86b3a58a</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/11/21/why-go-is-quietly-doing-what-rust-couldnt-staying-simple/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
