perf: 执行期 session 字符串 GC 阈值回收(超 32MiB 触发,跳过对象 sweep)

new_string_owned 分配前做廉价阈值检查(1 次 usize 比较 + 阈值读取),
超 session_gc_threshold 即调 collect_strings_only:clear_all_marks →
完整 mark(现成)→ sweep_session_strings(现成),跳过对象 sweep 搬移,
builtin 层对象裸指针跨分配点零悬垂面。触发检查在登记之前——返回值
尚未根化,登记后回收会被本次 GC 判死致悬垂。分配前触发亦使 GC 期间
新串仍在栈上未登记,天然安全。

长循环字符串累积场景(REPL 常驻 / test262 长测试 / kernel_batch 复用)
session 内存从无界变为有界(≤ 阈值 + 存活串);bench gc_trigger_count
恢复有效(string_build 由 0 → 2 次触发)。07.2 rope 死节点回收依赖本机制。
This commit is contained in:
2026-08-15 12:12:03 +08:00
parent df99cd3b2a
commit 239309925e
5 changed files with 295 additions and 2 deletions
+11
View File
@@ -363,8 +363,19 @@ impl Vm {
}
/// 同 `new_string`,但以 move 接收 `String`,避免一次克隆。
///
/// # 副作用
/// - 累计 session 字符串字节超阈值时,触发一次仅字符串的 GC(对象不搬移)。
///
/// # 注意事项
/// - 触发检查在登记之前:返回值尚未写入任何执行根,若登记后回收会被本次 GC
/// 判死并释放,返回即悬垂;此时 `s` 仍在栈上、未被登记,GC 不会触碰它。
/// - 热路径仅 1 次 usize 比较 + 阈值读取,超限才进回收路径。
pub fn new_string_owned(&mut self, s: String) -> JsValue {
let len = s.len();
if self.gc_state.session_bytes_allocated >= self.kernel_core.config().session_gc_threshold {
self.maybe_collect_session_strings();
}
let ptr = Box::into_raw(Box::new(JsString::new(s)));
self.gc_state.session_string_ptrs.push(ptr);
self.gc_state.session_bytes_allocated += std::mem::size_of::<JsString>() + len;