<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GOMEMLIMIT on Zampo Blog</title><link>https://blog.cpdd.fyi/tags/gomemlimit/</link><description>Recent content in GOMEMLIMIT on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Fri, 22 May 2026 00:01:00 +0800</lastBuildDate><atom:link href="https://blog.cpdd.fyi/tags/gomemlimit/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 服务被 OOMKilled，别先怪 GC</title><link>https://blog.cpdd.fyi/posts/go-gc-debugging/</link><pubDate>Fri, 22 May 2026 00:01:00 +0800</pubDate><guid>https://blog.cpdd.fyi/posts/go-gc-debugging/</guid><description>&lt;p&gt;服务又被 OOMKilled 了。&lt;/p&gt;
&lt;p&gt;Pod 刚重启，群里已经有人开始翻 GC 日志。有人说 &lt;code&gt;GC CPU&lt;/code&gt; 到了 20%，有人说把 &lt;code&gt;GOGC&lt;/code&gt; 调低一点，还有人建议直接上 &lt;code&gt;GOMEMLIMIT&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这些动作不一定错，但顺序错了。&lt;/p&gt;
&lt;p&gt;OOMKilled 是结果，不是根因。它只说明容器被内核杀掉了，不说明是谁把内存吃掉了。可能是 Go heap 真的在涨，可能是 goroutine 泄漏把栈和引用一起拖大，可能是 &lt;code&gt;alloc_space&lt;/code&gt; 很高导致 GC 忙，可能是 RSS 里有 cgo、mmap、tmpfs，也可能只是容器 limit 给得太紧。&lt;/p&gt;
&lt;p&gt;Go 服务的内存问题，最怕一句话定性。&lt;/p&gt;
&lt;p&gt;一旦你把所有问题都叫“GC 问题”，排查就会变成调参赌博：今天调 &lt;code&gt;GOGC&lt;/code&gt;，明天加 limit，后天又开始怀疑 pprof 没抓准。真正稳定的做法，是先把几条线拆开：Go 堆、Go runtime 管理的内存、RSS、容器 limit，以及业务生命周期。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.cpdd.fyi/images/go-gc-debugging/debug-flow.png" alt="Go GC 排查流程"&gt;&lt;/p&gt;
&lt;h2 id="第一步不是调参数是确认谁杀了你"&gt;第一步不是调参数，是确认谁杀了你&lt;/h2&gt;
&lt;p&gt;线上看到内存上涨，第一件事不是打开 pprof，也不是改 &lt;code&gt;GOGC&lt;/code&gt;。&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;span class="lnt"&gt;5
&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl describe pod &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl logs &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt; --previous
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl top pod &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt; --containers
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pod &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.status.containerStatuses[*].lastState.terminated.reason}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;你要先回答几个问题：&lt;/p&gt;</description></item><item><title>Go GC 占 CPU 5%，到底要不要调 GOGC？</title><link>https://blog.cpdd.fyi/posts/go-gc-tuning/</link><pubDate>Thu, 21 May 2026 23:30:00 +0800</pubDate><guid>https://blog.cpdd.fyi/posts/go-gc-tuning/</guid><description>&lt;p&gt;服务 p99 每隔几十秒抖一下。&lt;/p&gt;
&lt;p&gt;监控里 GC CPU 大概 5%，不算夸张，但曲线很准：GC 一来，延迟就往上冒。群里很快有人提议：把 &lt;code&gt;GOGC&lt;/code&gt; 从 100 调到 200，先让 GC 少跑几次。&lt;/p&gt;
&lt;p&gt;这个动作看起来很合理。&lt;/p&gt;
&lt;p&gt;CPU 下来了，GC 日志没那么密了，p99 也安静了几天。然后另一个问题来了：容器内存开始逼近 limit，某个高峰期被 OOMKilled。大家又把 &lt;code&gt;GOGC&lt;/code&gt; 调回去，延迟抖动也跟着回来。&lt;/p&gt;
&lt;p&gt;这就是很多 Go GC 调优的真实状态：不是不会调参数，而是没想清楚自己到底在买什么。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOGC&lt;/code&gt; 调的是账期，不是魔法。&lt;/p&gt;
&lt;p&gt;调高它，相当于允许堆多长一段时间，用内存换 CPU；调低它，相当于让 GC 更勤快，用 CPU 换内存。至于 &lt;code&gt;GOMEMLIMIT&lt;/code&gt;，它不是另一个版本的 &lt;code&gt;GOGC&lt;/code&gt;，而是在告诉 runtime：这个进程由 Go 管的内存，最好别越过这条线。&lt;/p&gt;
&lt;p&gt;所以这篇不打算把 GC 参数逐个解释一遍。线上真正需要的是一条决策路径：要不要调，调什么，调多少，什么时候停手。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.cpdd.fyi/images/go-gc-tuning/gogc-heap-curve.png" alt="GOGC 与堆目标关系"&gt;&lt;/p&gt;
&lt;h2 id="先别问-gogc-设多少先问谁在付账"&gt;先别问 GOGC 设多少，先问谁在付账&lt;/h2&gt;
&lt;p&gt;Go 官方 GC Guide 给过一个很关键的公式：&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;/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;Target heap memory = Live heap + (Live heap + GC roots) × GOGC / 100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这句话不用背，抓住意思就行。&lt;/p&gt;</description></item></channel></rss>