Silu 没有 GC。静态世界(typed 路径)用 Rust 式所有权规则,
动态世界(pyobj)故意泄漏。本文给出机制、实测数据与
"为什么静态世界不需要借用检查器"的分析。
类型化路径不产生需要追踪的垃圾——每块内存从出生就有唯一主人:
| 类型 | 表示 | 生命周期管理 |
|---|---|---|
int / float / bool | 栈值(i64/double/i1) | 无堆分配 |
str(rawstr) | 独占堆缓冲 {len,cap,data,cp} | 完整 drop 体系:赋值拷贝时旧缓冲 free;函数退出统一 drop 全部局部槽(_emit_local_rawstr_drops);传参借用零拷贝(被调方只读,s += x 重绑走 concat 新建——Python 别名语义精确成立:h(d) 返回 "abx" 而 d 保持 "ab") |
list[int] 等 rawlist | 独占结构体 | 赋值即所有权转移;无作用域 drop(静态程序容器通常一次构建) |
dict[int,int] 等 rawdict | 独占哈希表 | 同上 |
实测(Apple Silicon):
row = row + "*":2.3ms(amortized O(1) 原地 append)pyobj 盒({i32 tag, i8* data};create_int/str/list/dict/instance) malloc 后永不 free——运行时不存在用户盒的释放路径。
实测:30 万次循环、每次建一个小 list + dict,RSS 172MB (~570B/迭代,纯泄漏)。
这是有意的取舍:
0.9× Rust 的热路径上全部买单;
(程序结束,操作系统统一回收;类 arena 分配器用法)。
silu interpret / silu exec / pycompat 跑在 CPython 上: Python 的引用计数 + 分代 GC,与语言语义一致(四平面验证因此 能抓住编译面的内存 bug——例如 v1.7.1 写回回归就是解释器平面 先暴露的)。
Rust 借用检查器解决的问题是:**同一块内存可能有多个名字且 都可能写**。Rust 的答案是编译期证明"要么唯一写者,要么多个 只读者"(borrow checker)。
Silu 静态世界不需要它,因为语言语义更保守——三个根本差异:
str 逻辑上不可变(Python 语义):s += x是重绑(新串),不是原地写。原地 append 只是编译器在 槽独占前提下的优化发射;一旦槽可能是借来的(str 参数), 发射退回 concat 新建(_borrowed_strs 集合跟踪)。"写"这个 动作永远不发生在共享内存上。
它只能读(s[i]、len、切片都产新值)。这是借用检查器的 核心规则,但 Silu 不需要检查器来强制——**语言里根本没有 通过共享引用写的语法**(无 &mut,无原地变异方法)。Rust 需要检查器是因为它*提供*了 &mut T;Silu 不提供。
list/dict 赋值是别名(Python 语义),共享可变——但这正是 Python/Rust-Rc<RefCell> 式的运行时对象,靠独占 malloc + 程序员心智模型管理,静态 程序里它们通常一次构建终身使用(基准 8/14 静态程序里容器 装载后从不重新绑类型)。
一句话:**Rust 要检查借用,因为它把"可变别名"作为语言能力 交给你;Silu 静态子集不给你这个能力,所以无从检查、也无需 检查。**代价是表达力上限(写不了 Rust 那种零拷贝原地算法的 通用形式——原地 append 只对槽独占这个窄窗口成立);换来的是 编译器无需借用检查器即可保证内存安全。
| 维度 | Rust | Silu 静态世界 |
|---|---|---|
| 内存安全由谁保证 | 借用检查器(编译期数据流分析) | 语言语义(值不可变 + 无共享写语法) |
| 可变别名 | 提供(&mut T,需检查) | 不提供(无对应语法) |
| 借用规则 | NLL 检查器推导(有时与意图不符,需改结构) | 无需推导——规则内建于发射策略 |
| 原地变异 | 通用(&mut vec 直接 push) | 窄窗口优化(仅槽独占可证时发射;借来的槽自动退回 concat) |
| 共享引用 | &T(只读,检查器强制) | str 借用传参(只读,语法上只读——没有写它的方法) |
| 容器别名 | 受限(Rc<RefCell> 逃生舱) | Python 语义(自由别名,运行时对象) |
| 生命周期标注 | 'a 泛型参数 | 不存在——值语义让生命周期 = 词法作用域 |
| 表达力上限 | 高(零拷贝原地算法的通用形式) | 低(原地优化是编译器私事,程序员不可申请) |
| 编译器复杂度 | 借用检查器本身是研究级组件 | 发射期 3 个 set 跟踪(owned/borrowed/scope-local) |
一句话对比:Rust 把内存安全做成编译器检查的权限系统, Silu 把它做成语言不给危险语法的事实——前者换来表达力, 后者换来编译器简单(而这个编译器本身是 Silu 写的 1.2 万行、 能逐字节重建自己的代码,简单性会复利)。
一起缩(3-4 周级,也是 jsonparse/wordcount 性能的根治)。
_emit_local_rawstr_drops 配方(1-2 天)。
代价最大,仅长期驻留服务需要;当前无动机。
三项均不在 todo(无性能/正确性压力)。
附:复现命令——
python -m silu build /tmp/gc1.si -o gc1 && /usr/bin/time -l ./gc1 # 标量 1MB
python -m silu build /tmp/gc2.si -o gc2 && /usr/bin/time -l ./gc2 # str 27MB
python -m silu build /tmp/gc3.si -o gc3 && /usr/bin/time -l ./gc3 # pyobj 172MB(泄漏)