<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>内存管理 on Cloudside</title><link>https://cloudside.icyyan.com/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/</link><description>Recent content in 内存管理 on Cloudside</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sat, 20 Jun 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://cloudside.icyyan.com/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/index.xml" rel="self" type="application/rss+xml"/><item><title>Python 垃圾回收机制：引用计数、循环引用和分代 GC</title><link>https://cloudside.icyyan.com/posts/python-garbage-collection-guide/</link><pubDate>Sat, 20 Jun 2026 10:00:00 +0800</pubDate><guid>https://cloudside.icyyan.com/posts/python-garbage-collection-guide/</guid><description>&lt;p&gt;很多人第一次关心 Python 的垃圾回收，不是因为写了多复杂的代码，而是因为遇到了几个很实际的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;del obj&lt;/code&gt; 之后，内存为什么没有立刻降下来？&lt;/li&gt;
&lt;li&gt;明明没有全局变量引用某个对象，它为什么还活着？&lt;/li&gt;
&lt;li&gt;Python 不是有垃圾回收吗，为什么还会出现内存泄漏？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gc.collect()&lt;/code&gt; 到底该不该在业务代码里手动调用？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些问题背后其实是同一个机制：Python 的内存回收不是单一算法，而是几层机制一起工作。平时我们说的“Python 垃圾回收”，在最常见的 CPython 解释器里，主要由两部分组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引用计数：对象引用数变成 0 时，通常立刻释放。&lt;/li&gt;
&lt;li&gt;循环垃圾回收器：专门处理引用计数解决不了的循环引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章主要讨论 CPython，因为日常用 &lt;code&gt;python&lt;/code&gt; 命令启动的解释器，大多数情况下就是 CPython。PyPy、Jython、IronPython 等实现可以采用不同策略，所以不要把本文所有细节直接套到每一种 Python 实现上。&lt;/p&gt;
&lt;h2 id="先从变量和对象的关系说起"&gt;先从变量和对象的关系说起&lt;/h2&gt;
&lt;p&gt;在 Python 里，变量不是装对象的盒子，更像是贴在对象上的名字。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段代码里没有创建两个列表。它创建了一个列表对象，然后让 &lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt; 都指向它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;a ─┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├──&amp;gt; [1, 2, 3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b ─┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;删除的也不是列表对象本身，而是删除名字 &lt;code&gt;a&lt;/code&gt; 到列表对象的那条引用。因为 &lt;code&gt;b&lt;/code&gt; 还指向这个列表，所以对象仍然活着：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;b ───&amp;gt; [1, 2, 3]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这就是理解 Python 垃圾回收的入口：&lt;strong&gt;对象什么时候能被回收，取决于还有没有地方能继续访问它。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;del&lt;/code&gt; 的含义也要顺手纠正一下：&lt;code&gt;del name&lt;/code&gt; 删除的是名字绑定；&lt;code&gt;del obj.attr&lt;/code&gt; 删除的是属性引用；&lt;code&gt;del some_list[i]&lt;/code&gt; 删除的是容器里的一个引用。它们都不等于“立刻把某块内存还给操作系统”。&lt;/p&gt;</description></item><item><title>Rust 里的 Box 到底是什么</title><link>https://cloudside.icyyan.com/posts/rust-box-guide/</link><pubDate>Sun, 24 May 2026 10:00:00 +0800</pubDate><guid>https://cloudside.icyyan.com/posts/rust-box-guide/</guid><description>&lt;p&gt;学 Rust 时，&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 很容易被一句话带过：它可以把数据放到堆上。&lt;/p&gt;
&lt;p&gt;这句话没错，但如果只记住这一句，后面看到 &lt;code&gt;Box&amp;lt;dyn Trait&amp;gt;&lt;/code&gt;、&lt;code&gt;Box&amp;lt;List&amp;gt;&lt;/code&gt;、&lt;code&gt;Pin&amp;lt;Box&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;、&lt;code&gt;Box::leak&lt;/code&gt; 时，还是会觉得它像一个突然冒出来的语法补丁。&lt;/p&gt;
&lt;p&gt;更好的理解方式是：&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 不是“逃离所有权系统”的工具，而是 Rust 所有权系统里最基础的一种拥有型指针。它让一个值住在堆上，同时让所有权仍然清清楚楚地归某个变量管理。&lt;/p&gt;
&lt;h2 id="先看-box-解决了什么问题"&gt;先看 Box 解决了什么问题&lt;/h2&gt;
&lt;p&gt;普通变量通常可以这样理解：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;n&lt;/code&gt; 这个值本身就放在当前栈帧里。栈很快，进入函数时分配，函数返回时回收，生命周期也很清楚。&lt;/p&gt;
&lt;p&gt;但有些场景光靠栈不够舒服：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个类型递归地包含自己，编译器算不出它的大小。&lt;/li&gt;
&lt;li&gt;你想把不同具体类型放进同一个集合，只要求它们实现同一个 trait。&lt;/li&gt;
&lt;li&gt;一个值很大，你希望移动时只移动一个指针。&lt;/li&gt;
&lt;li&gt;某些 API 需要一个固定地址的拥有型对象，比如配合 &lt;code&gt;Pin&lt;/code&gt; 使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这时 &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 就出现了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Box::new(&lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以把它粗略想成这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;栈上变量 n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; 保存一个指针
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; 指向堆上的 42
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 本身在栈上，里面保存指向堆数据的指针。真正的 &lt;code&gt;T&lt;/code&gt; 在堆上。变量离开作用域时，&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 会自动释放堆上的 &lt;code&gt;T&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 同时有两个特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它是指针，可以间接访问堆上的值。&lt;/li&gt;
&lt;li&gt;它拥有这个值，离开作用域时负责释放它。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这和 C 里的裸指针很不一样。你不用手写 &lt;code&gt;free&lt;/code&gt;，也不能随便复制出多个拥有者。Rust 仍然会检查所有权、移动和借用。&lt;/p&gt;
&lt;h2 id="最基本的用法boxnew"&gt;最基本的用法：Box::new&lt;/h2&gt;
&lt;p&gt;创建一个 &lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 最常见的方式是 &lt;code&gt;Box::new&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Box::new(String::from(&lt;span style="color:#e6db74"&gt;&amp;#34;cloudside&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里 &lt;code&gt;String&lt;/code&gt; 这个值由 &lt;code&gt;Box&lt;/code&gt; 放到堆上管理，&lt;code&gt;name&lt;/code&gt; 是一个拥有它的 &lt;code&gt;Box&amp;lt;String&amp;gt;&lt;/code&gt;。注意，&lt;code&gt;String&lt;/code&gt; 自己内部还会管理一块字符串缓冲区；这里说的是 &lt;code&gt;String&lt;/code&gt; 这个三字段结构本身的位置。&lt;/p&gt;</description></item></channel></rss>