fix: full_reset/run 边界换新 Bump 替代原地 reset——旧 epoch/session arena 全量归还系统分配器、容量不跨 reset 保留,配 arena 保留锚校准用例

This commit is contained in:
2026-09-11 19:43:55 +00:00
parent abad859893
commit ad0405fe51
6 changed files with 119 additions and 9 deletions
+10
View File
@@ -992,6 +992,16 @@ impl Vm {
self.gc_state.session_bytes_peak
}
/// 双 arena(epoch + session 对象)当前 chunk 已分配字节之和。
///
/// # 注意事项
/// reset 边界读 = 换新 Bump 后空 arena 恰 0(空 chunk 哨兵计 0);
/// run 中途读 = 当前 chunk 用量(非全 arena 口径——run 计量走
/// [`Self::run_alloc_bytes`],勿据此断言)。
pub fn arena_retained_bytes(&self) -> usize {
self.epoch.bump().allocated_bytes() + self.gc_state.session_epoch.allocated_bytes()
}
/// 本 run 累计分配字节:epoch arena + session 对象 arena + session 手工堆
/// 账目(session 串 + session 对象及其属性向量 + GC 后补回的 BigInt)。
/// 单次 run 内单调不减(执行期对象不回收、
+23 -4
View File
@@ -343,7 +343,9 @@ impl Vm {
self.teardown_session_heap_data();
self.epoch.reset();
self.gc_state.epoch_object_ptrs.clear();
self.gc_state.session_epoch.reset();
// 换新 Bump:旧 session arena 全量归还系统分配器(与 sweep 路径同构),
// 容量不跨 full_reset 保留。
self.gc_state.session_epoch = bumpalo::Bump::new();
self.gc_state.session_bytes_allocated = 0;
self.gc_state.session_bytes_peak = 0;
self.gc_state.string_gc_watermark = self.kernel_core.config().session_gc_threshold;
@@ -1201,6 +1203,23 @@ mod tests {
assert!(unsafe { *session_ptr } == 123);
}
/// run 边界换新 Bump 后双 arena 保留锚恰 0:重源 run 冲高水位,
/// full_reset 后 epoch/session 两 arena 均空(容量不跨 reset 保留)。
#[test]
fn full_reset_zeroes_arena_retained() {
let mut vm = Vm::new();
run_source(
&mut vm,
"var t = 0; for (var i = 0; i < 20000; i++) { var o = { s: 'ab' + i, a: [i] }; t += o.s.length + o.a.length; } t",
);
assert!(vm.epoch.bump().allocated_bytes() > 0, "重源 run 应冲高 epoch arena 水位");
vm.full_reset();
assert_eq!(vm.epoch.bump().allocated_bytes(), 0);
assert_eq!(vm.gc_state.session_epoch.allocated_bytes(), 0);
}
#[test]
fn immutables_cache_filled_once_per_module() {
let mut vm = Vm::new();
@@ -1318,12 +1337,12 @@ mod tests {
}
#[test]
fn session_epoch_reset_is_only_in_full_reset_state_clear() {
fn session_epoch_replacement_is_only_in_full_reset_state_clear() {
let src = include_str!("vm_support.rs");
let production = src.split("#[cfg(test)]").next().expect("production source");
assert_eq!(production.matches("self.gc_state.session_epoch.reset()").count(), 1);
assert_eq!(production.matches("self.gc_state.session_epoch = bumpalo::Bump::new()").count(), 1);
assert!(production.contains("fn clear_full_reset_state(&mut self)"));
assert!(production.contains("self.gc_state.session_epoch.reset();"));
assert!(production.contains("self.gc_state.session_epoch = bumpalo::Bump::new();"));
}
#[test]