<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>内存泄漏 on Zampo Blog</title><link>https://blog.cpdd.fyi/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</link><description>Recent content in 内存泄漏 on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Thu, 28 May 2026 14:20:00 +0800</lastBuildDate><atom:link href="https://blog.cpdd.fyi/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 内存为什么不能都怪 GC：goroutine 泄漏、无界缓存和高分配速率修法完全不同</title><link>https://blog.cpdd.fyi/posts/go-gc-leak-three-types/</link><pubDate>Thu, 28 May 2026 14:20:00 +0800</pubDate><guid>https://blog.cpdd.fyi/posts/go-gc-leak-three-types/</guid><description>&lt;p&gt;服务又 OOM 了。&lt;/p&gt;
&lt;p&gt;Pod 刚重启，群里已经开始出方案：&lt;code&gt;GOMEMLIMIT&lt;/code&gt; 设低一点，&lt;code&gt;GOGC&lt;/code&gt; 调一下，limit 先加 1Gi，GC 日志再翻一遍。&lt;/p&gt;
&lt;p&gt;这些动作有时候能救火，但经常救不到根上。&lt;/p&gt;
&lt;p&gt;因为很多 Go 服务所谓的“内存泄漏”，根本不是一种病。goroutine 卡住、全局 map 只增不删、热路径疯狂分配，看起来都像内存曲线往上走，修法却完全不同。&lt;/p&gt;
&lt;p&gt;你把它们都叫 GC 问题，排查就会变成调参赌博。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GC 不是清洁工，它不能删除还被引用的对象。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这篇不再重复讲 OOMKilled 第一现场怎么确认。上篇已经讲过：先看谁被杀、撞到什么 limit、heap 和 RSS 是不是同一本账。这篇往下走一步：当你确认 Go 服务确实有内存压力，怎么把“泄漏”拆成三类。&lt;/p&gt;
&lt;p&gt;拆清楚以后，很多争论会少一半。&lt;/p&gt;
&lt;h2 id="第一类goroutine-泄漏真正漏的是生命周期"&gt;第一类：goroutine 泄漏，真正漏的是生命周期&lt;/h2&gt;
&lt;p&gt;goroutine 泄漏最容易被低估。&lt;/p&gt;
&lt;p&gt;很多人听到 goroutine，会觉得它很轻：几 KB 栈，问题不大。但线上事故里，麻烦通常不只是 goroutine 自己占多少内存，而是它还挂着什么。&lt;/p&gt;
&lt;p&gt;请求已经超时了，调用方走了，下游 channel 还在等发送；&lt;code&gt;context&lt;/code&gt; 已经 cancel 了，后台 worker 还在跑；连接断了，读循环没有退出；定时任务里起了 goroutine，却没有任何关闭协议。&lt;/p&gt;
&lt;p&gt;这些 goroutine 还活着，栈上可能就挂着 request、response、session、buffer、logger field、trace span。&lt;/p&gt;
&lt;p&gt;对象还被引用着，GC 就不会删。&lt;/p&gt;
&lt;p&gt;排查信号通常很直接：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;goroutine 数持续上涨，业务回落后不回落；
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;goroutine profile 里同一类栈越来越多；
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;heap diff 里能看到被这些 goroutine 间接持有的对象；
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GC CPU 跟着变高，但 live heap 不一定夸张到一眼能看出来。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;事故现场不要只抓 heap。goroutine profile 要一起留：&lt;/p&gt;</description></item></channel></rss>