3.9 KiB
VM、执行帧与对象的生命周期
VM 由宿主持有,VM 拥有 objects 包的 ObjectStore,由它持有私有对象堆;每次调用临时创建执行帧。一次执行结束不等于 VM 销毁;宿主可以保留全局字典并在同一 VM 中执行多次。
| 层级 | 创建 | 结束 |
|---|---|---|
| Vm | 宿主调用 Vm::new,创建空堆并取得独立堆身份 | Rust 所有者销毁;使用 Arc 时等最后一份 Arc 被释放 |
| ObjectStore | 独立调用 ObjectStore::new,或由 Vm::new 内部创建;分配独立堆身份 | 所有者销毁时释放整个堆;没有共享可变堆入口 |
| EndorFrame | 每次语言函数调用建立,加入本次调用的帧向量 | 函数返回或 Result::Err 时,从内到外清理 |
| EndorObject | new_*/allocate 分配,返回一份外部强引用 | 总强引用归零、显式循环回收,或整体 VM 销毁 |
运行期间的根
每个执行帧持有函数的一份根引用,函数通过堆边继续保活代码、globals、builtins、默认参数和闭包。局部变量与求值栈的对象槽分别持有根;Cell 通过自己的堆边持有捕获内容。
堆计数关系是:总强引用数 = 宿主/执行根引用数 + 属性/载荷内部入边数。对象头 external_refs 同时统计宿主和执行根,不只是宿主变量。
ObjectRef 拷贝、Value::clone 和句柄变量离开 Rust 作用域,都不会自动 retain/release。宿主为每份显式持有配对 retain/release。借用读取在所有者删除内容前需要 retain,详见 对象模型。
MakeFunction 成功时将新函数的一份分配根压入求值栈;函数通过托管边保活代码、globals、builtins、默认值及闭包 Cell,再释放原输入栈根。返回新函数后,这些依赖不随外围帧清理而销毁。创建失败保留原栈供错误清理,创建成功后的后续错误也会释放临时函数及其独占依赖。
返回与失败
call/execute 返回 Value::Object 时,结果拥有一份根。执行器将这份根从帧移交给调用者或宿主,避免帧清理回收结果;使用后须 release_value,或取出对象句柄后 release。Native 回调原始返回值仍是借用值,由 VM 在移除输入根之前保活。
Result::Err 会触发活动帧清理;last_traceback 仅保存函数名、文件、代码首行与指令位置,不保活已经销毁的帧。此前已经完成的全局字典或属性写入不会回滚。
Rust panic 尚无专门的捕获恢复或执行根 RAII 清理机制。宿主捕获 panic 后不能据此假定当前 VM 已恢复全部执行根计数;整机进程终止与正常错误返回也不是同一条清理路径。
回收和整体销毁
通常释放在总引用数归零时立即执行。堆使用显式工作列表递减出边,长链不会递归析构。不可达循环通过手动 collect_cycles 从根标记后批量移除;没有自动阈值、分代或增量 GC。
Vm 没有独立后台线程、暂停状态或显式 shutdown 状态机。Rust 所有权销毁它所拥有的堆和剩余载荷,包括没有先做循环回收的对象;原生共享资源继续遵循各自的 Rust 所有权。外部 ObjectRef 不保活 Vm。
Server 的 Tokio 运行时、监听器、信号处理和 Host 文件信息由宿主管理;优雅退出见 运行。当前宿主不持有数据库连接。数据库能力只为未来的特殊类按需提供,异步连接关闭与原生载荷生命周期仍需单独接入,见 数据库特殊类。数据库不自动保存或恢复 VM 对象。
源码定位:objects/src 中的 store.rs/heap.rs 负责对象生命周期,core/src 中的 vm.rs/frame.rs/eval.rs 负责执行状态和帧根。返回 运行时索引。