mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
docs: 补齐会话垃圾回收注释并去除历史措辞
This commit is contained in:
@@ -42,6 +42,9 @@ impl SessionGc {
|
||||
}
|
||||
|
||||
impl SessionGc {
|
||||
/// 清空 session 与 epoch 全部对象的 GC mark 位。epoch 臂须与 session 同清:
|
||||
/// 置位不随 session 清位消失,残留位会让下一次 `mark` 的 DFS 在已标对象处
|
||||
/// 短路,漏扫其新增引用边。
|
||||
pub(crate) fn clear_all_marks(&mut self, vm: &mut Vm) {
|
||||
vm_debug!("[GC] clear_all_marks: {} session objects", vm.gc_state.session_object_ptrs.len());
|
||||
for &ptr in &vm.gc_state.session_object_ptrs {
|
||||
@@ -375,6 +378,12 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
|
||||
/// 从 VM roots 标记存活的 session/epoch 对象、字符串与 BigInt,供 `sweep` 判定。
|
||||
///
|
||||
/// 对象 DFS 同时跟踪 session 与 epoch 两张表,两族 mark 位同字节,晋升收集按
|
||||
/// “标记 ∩ epoch 表”取活集;字符串边走 rope 闭包传播(Cons 子节点与扁平化产物),
|
||||
/// BigInt 边直接入存活集。roots 由 `Vm::for_each_root` 枚举,字段清单与
|
||||
/// `rewrite_values` 一一对应、须同步。只置位不搬移,调用前须先 `clear_all_marks` 清位。
|
||||
pub(crate) fn mark(&mut self, vm: &Vm) {
|
||||
vm_debug!("[GC] mark phase: {} roots", vm.gc_state.session_object_ptrs.len());
|
||||
let mut seeds = Vec::new();
|
||||
@@ -444,6 +453,10 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放对象本体之外的堆外属性数据(元素区、元素 meta、hash 属性区、属性 meta
|
||||
/// 与各族 native 状态盒),按 capacity 经 `Box::from_raw` 各恰好释放一次,返回
|
||||
/// 字节数。`obj_ptr` 为空时返回 0;`require_session` 为 true 时断言对象属
|
||||
/// session epoch,epoch 原件走 false 分支。不含 upvalue 列表与对象本体,由调用方处理。
|
||||
pub(crate) fn drop_object_heap_data(obj_ptr: *mut JsObject, require_session: bool) -> u64 {
|
||||
if obj_ptr.is_null() {
|
||||
return 0;
|
||||
@@ -546,6 +559,12 @@ impl SessionGc {
|
||||
bytes
|
||||
}
|
||||
|
||||
/// 移动式清扫 session 对象:存活对象复制进新 arena,死对象原地释放。
|
||||
///
|
||||
/// 按 GC mark 位分流:存活对象经 `clone_for_session_epoch` 复制(native 盒随族深拷)
|
||||
/// 并登记 forwarding 旧址→新址,死对象释放本体、堆区与独占 upvalue;随后按转发表重写
|
||||
/// 新对象的值边、native 边与 promise 结算链裸指针,转发查找使共享与环去重到同一克隆。
|
||||
/// 最后重写全部根引用、换表并以新 Bump 接管 session epoch,旧 arena 整体归还并清 mark 位。
|
||||
pub(crate) fn sweep(&mut self, vm: &mut Vm) -> u64 {
|
||||
let old_ptrs = std::mem::take(&mut vm.gc_state.session_object_ptrs);
|
||||
let mut forwarding = std::mem::take(&mut vm.gc_state.forwarding);
|
||||
@@ -749,6 +768,7 @@ impl SessionGc {
|
||||
freed
|
||||
}
|
||||
|
||||
/// 完整收集的触发判断:对象、字符串或 BigInt 表非空,且字节账目已达阈值。
|
||||
pub(crate) fn should_collect(&self, vm: &Vm) -> bool {
|
||||
(!vm.gc_state.session_object_ptrs.is_empty()
|
||||
|| !vm.gc_state.session_string_ptrs.is_empty()
|
||||
@@ -789,6 +809,10 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
|
||||
/// 完整收集编排:`mark` → `sweep`(对象移动式搬移)→ `sweep_session_strings` →
|
||||
/// `sweep_session_bigints`,累加三类释放字节;字符串与 BigInt 地址稳定、无需
|
||||
/// forwarding。更新各表、字节账目与 mark 位,累计收集次数与最近/最大/最小耗时,
|
||||
/// 释放字节大于 0 或每满 100 次时输出统计摘要。
|
||||
pub(crate) fn collect(&mut self, vm: &mut Vm) {
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -891,12 +915,14 @@ impl SessionGc {
|
||||
);
|
||||
}
|
||||
|
||||
/// 执行期字符串回收触发入口:`should_collect_strings` 命中时执行 strings-only 收集。
|
||||
pub(crate) fn maybe_collect_strings_only(&mut self, vm: &mut Vm) {
|
||||
if self.should_collect_strings(vm) {
|
||||
self.collect_strings_only(vm);
|
||||
}
|
||||
}
|
||||
|
||||
/// 完整收集触发入口:`should_collect` 命中时执行完整收集。
|
||||
pub(crate) fn maybe_collect(&mut self, vm: &mut Vm) {
|
||||
if self.should_collect(vm) {
|
||||
self.collect(vm);
|
||||
@@ -1115,6 +1141,7 @@ impl SessionGc {
|
||||
freed
|
||||
}
|
||||
|
||||
/// 单行 GC 统计摘要:收集次数、扫描/存活/死对象数、释放字节与最近耗时。
|
||||
pub(crate) fn stats_summary(&self) -> String {
|
||||
format!(
|
||||
"[GC] collection #{}: {} scanned, {} live, {} dead, {} freed, {}μs",
|
||||
@@ -1162,6 +1189,9 @@ fn rewrite_forwarded_value(
|
||||
.unwrap_or(value)
|
||||
}
|
||||
|
||||
/// 按 forwarding 表把全部 VM 根引用重写到搬移后的新地址。与 `for_each_value`
|
||||
/// 共用同一字段清单(经 `rewrite_values` 遍历)且须同步:遗漏字段会在搬移后
|
||||
/// 保留指向旧 arena 的悬垂指针。
|
||||
pub(crate) fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
||||
// 统一遍历:与 for_each_value 共用同一字段清单。
|
||||
|
||||
@@ -122,8 +122,8 @@ fn gc_roots_contains_registers_frames_and_root_roots() {
|
||||
#[test]
|
||||
fn rewrite_matches_roots_coverage() {
|
||||
// 指针重写必须覆盖根收集访问的同一组执行核心字段:session→marker 映射后,
|
||||
// 每个持有 session 的字段都应被改写为 marker(与 Step 1 的 for_each 覆盖测试
|
||||
// 互补,共同构成孪生清单验证)。
|
||||
// 每个持有 session 的字段都应被改写为 marker(与上文基于 for_each 的覆盖
|
||||
// 测试同口径,共同构成孪生清单验证)。
|
||||
let mut vm = Vm::new();
|
||||
let obj = plain_object(&mut vm);
|
||||
let session = vm.promote_object(obj);
|
||||
@@ -426,7 +426,8 @@ fn map_native_storage_is_not_a_normal_object_edge() {
|
||||
|
||||
assert!(map_obj.hash_props_vec().is_none());
|
||||
assert!(!map_obj.native_data().is_null());
|
||||
// Verify native storage pointer is not traced as a normal object edge.
|
||||
// Map 的 native 存储指针不得被当作普通对象边扫描:扫到会把 native 堆数据
|
||||
// 误拉进 mark 集合。
|
||||
let mut stack = Vec::new();
|
||||
let mut live = HashSet::with_hasher(FxBuildHasher);
|
||||
let mut live_bigints = HashSet::with_hasher(FxBuildHasher);
|
||||
@@ -912,9 +913,9 @@ fn global_prop_opt(vm: &Vm, name: &str) -> Option<JsValue> {
|
||||
vm.resolve_property(global, si)
|
||||
}
|
||||
|
||||
/// 闭包捕获变量跨对象 sweep 存活:reset 触发完整收集(session_epoch 替换),
|
||||
/// 修复前 cell 分配于旧 arena、随 Bump drop 悬垂;修复后独立堆分配、
|
||||
/// 地址稳定,sweep 只重写 cell.value 中的对象引用,值跨搬移保留。
|
||||
/// 闭包捕获变量跨对象 sweep 存活:reset 触发完整收集(session_epoch 替换)。
|
||||
/// cell 独立堆分配、不入 session arena,arena 整体回收时不连带释放——否则跨
|
||||
/// arena 存活的 cell 指针悬垂;sweep 只重写 cell.value 中的对象引用,值跨搬移保留。
|
||||
#[test]
|
||||
fn closure_cell_survives_object_sweep() {
|
||||
let mut vm = vm_with_threshold(1);
|
||||
@@ -940,8 +941,8 @@ fn closure_cell_survives_object_sweep() {
|
||||
}
|
||||
|
||||
/// 私有字段类的 brand cell 跨对象 sweep 存活:`@@class_brand` upvalue cell
|
||||
/// 存类 brand 对象,sweep 后其值经 forwarding 重写为搬移后的新对象地址,
|
||||
/// cell 指针仍稳定(修复前 cell 悬垂、值被复用覆盖)。
|
||||
/// 存类 brand 对象,sweep 后其值经 forwarding 重写为搬移后的新对象地址。
|
||||
/// cell 独立堆分配,指针在周边 arena 回收后仍稳定,值不被内存复用覆盖。
|
||||
#[test]
|
||||
fn private_brand_cell_survives_object_sweep() {
|
||||
let mut vm = vm_with_threshold(1);
|
||||
@@ -1108,7 +1109,8 @@ fn strings_only_then_full_collect_keeps_object_strings_live() {
|
||||
let obj_session = vm.promote_object(obj);
|
||||
vm.regs[0] = JsValue::from_js_object(obj_session);
|
||||
|
||||
// 第一轮 strings-only:对象被 mark 置位,修复前该位残留至完整收集。
|
||||
// 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整
|
||||
// 收集会使 mark DFS 在对象处短路、漏标其属性中的串;随后校验串跨完整收集存活。
|
||||
vm.maybe_collect_session_strings();
|
||||
assert!(vm.gc_state.session_string_ptrs.contains(&s_ptr));
|
||||
|
||||
@@ -1464,7 +1466,8 @@ fn promise_settle_via_original_propagates_to_clone_reaction() {
|
||||
/// 指令边界触发执行期收集(阈值 1),原件随 epoch 释放;随后结算入口是
|
||||
/// 克隆一(闭包目标槽在晋升时已改写到它),反应在最新克隆上——结算须沿
|
||||
/// 链触达全部迁移反应,消费者(同 run 末微任务触发,字节码子模块表仍有效)
|
||||
/// 方能落值。修复前克隆反应为空、原件已释放,反应静默永久丢失。
|
||||
/// 方能落值。反应迁入克隆并接结算链后,原件与旧克隆的结算沿链传到最新克隆,
|
||||
/// 反应不因原件释放而静默丢失。
|
||||
#[test]
|
||||
fn promise_promote_migrates_pre_promote_reactions_to_clone() {
|
||||
let mut vm = vm_with_threshold(1);
|
||||
|
||||
Reference in New Issue
Block a user