From 392df5988b644e3fd2c36936bafcaa18ded81e62 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 20 Sep 2026 07:11:36 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=86=99=E5=B1=8F=E9=9A=9C=E9=80=80?= =?UTF-8?q?=E7=9B=B4=E9=80=9A=E5=90=8E=E8=AE=A2=E6=AD=A3=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=9B=BE=E7=95=99=E5=AD=98=E4=B8=8E=E9=80=83=E9=80=B8=E5=AD=97?= =?UTF-8?q?=E9=9D=A2=E9=87=8F=E4=B8=A4=E6=9E=9A=20mem=20=E9=94=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/object_graph_retention_tests.rs | 25 +++++++++++++------ crates/oxide_vm/tests/session_arena_tests.rs | 14 ++++++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/oxide_vm/tests/object_graph_retention_tests.rs b/crates/oxide_vm/tests/object_graph_retention_tests.rs index e266a225..f3242dd9 100644 --- a/crates/oxide_vm/tests/object_graph_retention_tests.rs +++ b/crates/oxide_vm/tests/object_graph_retention_tests.rs @@ -1,8 +1,11 @@ -//! mem_object_graph 留存计数回归锚:克隆式晋升双计的数值基线。 +//! mem_object_graph 留存计数回归锚:逃逸写直通 + 测量点 promote 语义下的 +//! 确定性账目。 //! -//! 15 层满二叉树(32767 节点)在「逃逸写屏障克隆 + 测量点 promote」晋升语义下 -//! 的三个确定性计数。晋升语义或陈旧顶层寄存器根处理改动后,若以下任一数值 -//! 变化,说明双计机制已变——须对照 tests/stress/mem_object_graph.js 的基线 +//! 15 层满二叉树(32767 节点)在「写路径直通(不引克隆)+ 根 promote 深克隆」 +//! 晋升语义下的三个确定性计数。run 末 session 表只含直 session 分配对象, +//! 整树节点字面量驻留 epoch;promote_rooted 把顶层 var 寄存器根的 epoch 原件 +//! 深克隆为 session 内唯一副本。晋升语义或根处理改动后,若以下任一数值变化, +//! 说明 session 留存账目已变——须对照 tests/stress/mem_object_graph.js 的基线 //! (benchmark_baseline.json)复核后再更新本锚。 use std::sync::Arc; @@ -39,9 +42,11 @@ fn run_module(source: &str) -> Vm { vm } -/// 留存锚:run 末屏障已把整树克隆进 session(全局镜像,32769 = 整树 + 2), +/// 留存锚:逃逸写直通后 run 末 session 表只含直 session 分配的 2 枚对象 +/// (makeNode 函数对象与其 prototype 子对象),整树节点字面量驻留 epoch, /// 顶层 var 寄存器槽仍持有 epoch 原件(SET 路径不回写寄存器);测量点 -/// promote_rooted 再克隆寄存器原件一份——两份互异副本在 full GC 后均根可达。 +/// promote_rooted 把寄存器原件深克隆进 session(转发表去重,整树 32767), +/// full GC 后逻辑存活集(整树 + 函数 2)在 session 内恰一份、无双计。 /// epoch 侧 65534 的 2× 与节点无关:其中 32767 是每次 JS 调用无条件创建的 /// arguments 对象(CREATE_ARGUMENTS),另 32767 才是节点字面量。 #[test] @@ -49,10 +54,14 @@ fn object_graph_retention_anchor() { let mut vm = run_module(OBJECT_GRAPH); assert_eq!(vm.epoch_object_count(), 65534, "run 末 epoch 计数"); - assert_eq!(vm.session_object_count(), 32769, "run 末 session 屏障克隆计数"); + assert_eq!(vm.session_object_count(), 2, "run 末 session 直分配计数(函数对象 + prototype 子对象)"); vm.promote_rooted_epoch_objects(); vm.promote_session_epoch_refs(); vm.collect_session_gc(); - assert_eq!(vm.session_object_count(), 65536, "留存锚:克隆式晋升双计 = 2 × 32769(逻辑存活集 32769)"); + assert_eq!( + vm.session_object_count(), + 32769, + "留存锚:整树根克隆 + 函数直分配 = 逻辑存活集 32769(单份,无双计)" + ); } diff --git a/crates/oxide_vm/tests/session_arena_tests.rs b/crates/oxide_vm/tests/session_arena_tests.rs index 08880402..98a7d3f7 100644 --- a/crates/oxide_vm/tests/session_arena_tests.rs +++ b/crates/oxide_vm/tests/session_arena_tests.rs @@ -114,8 +114,9 @@ fn new_object_literal_tracked_and_freed_on_reset() { assert_eq!(vm.epoch_object_count(), 0, "reset 后追踪表应清空"); } -/// full_reset 同样释放 NEW_OBJECT 字面量:带属性写的 epoch 源对象经 promote 深拷贝 -/// 到 session 后,完全重置同时释放源对象与克隆的堆数据,不得双重释放。 +/// full_reset 同样释放 NEW_OBJECT 字面量:带属性写的 epoch 源对象经逃逸写直通 +/// 留存 global 槽(不克隆),完全重置随 global 重建丢弃槽位并释放源对象堆 +/// 数据,不得悬垂。 #[test] fn new_object_literal_freed_by_full_reset() { let mut vm = Vm::new(); @@ -125,8 +126,13 @@ fn new_object_literal_freed_by_full_reset() { globalThis.keep = o; o.k7", ); assert_eq!(format!("{}", result), "7", "逃逸后属性读取应正常"); - assert!(vm.epoch_object_count() > 0, "字面量源对象在 promote 后仍登记于 epoch 追踪表"); - assert!(vm.session_object_count() > 0, "逃逸克隆应登记于 session 追踪表"); + assert!(vm.epoch_object_count() > 0, "字面量源对象直通留存 epoch,登记于 epoch 追踪表"); + let keep = global_prop(&vm, "keep"); + // SAFETY: 逃逸值是本 VM 自有的 epoch 对象,本 session 内指针有效。 + assert!( + unsafe { (&*keep.as_js_object_ptr()).is_epoch() }, + "逃逸值应直通留存 epoch,不克隆进 session" + ); vm.full_reset(); assert_eq!(vm.epoch_object_count(), 0, "full_reset 后 epoch 追踪表应清空");