mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
fix: test262 runner 进程 RSS 无界增长——session 收尾统一释放内建裸堆区/upvalue 别名 + 每测试分配上限 256MiB(任务 116)
- 泄漏修复:KernelSession 收尾(teardown_intrinsic_protos/free_session_upvalues) 释放内建对象 Box::into_raw 裸堆区(release_raw_heap 四区置空幂等)与 session upvalue 原件/晋升克隆别名去重释放;bindings 原生盒负载边、 session_gc 边记录、vm_props 属性区同步收尾 - 失控测试封顶:runner kernel max_alloc_bytes=256MiB(dispatch 采样口径 run_alloc_bytes/run_alloc_bytes_full),超限计 memory-limit 失败而非拖垮 进程 RSS(arena 高水位 GB 级 × 多 worker = 宿主 OOM) - 归属:RSS 泄漏 pre-existing(HEAD 无 WIP 同现);identifiers escaped unicode 挂死族另立 B132(pre-existing,supervise 超时自愈) - 验证:build/clippy/四 crate 测试全绿零新增告警;language 前 5000 测试 单窗峰值 330MB(旧 ~4.5MB/测试 单调增长→88GB OOM 签名消失), 窗口间峰值 0.17-1.6GB 有界、逐测试释放回基线
This commit is contained in:
@@ -555,7 +555,8 @@ pub struct Vm {
|
||||
impl Drop for Vm {
|
||||
fn drop(&mut self) {
|
||||
// 直接 drop(test262 每测试新建即弃)不经 reset/full_reset 路径:
|
||||
// 统一收尾释放全部 session 堆数据,防逐测试累积泄漏。
|
||||
// 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。
|
||||
self.teardown_intrinsic_protos();
|
||||
self.teardown_session_heap_data();
|
||||
}
|
||||
}
|
||||
@@ -974,6 +975,47 @@ impl Vm {
|
||||
self.gc_state.session_bytes_peak
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节:epoch arena + session 对象 arena + session 手工堆
|
||||
/// 账目(串/BigInt/cell/属性向量)。单次 run 内单调不减(执行期对象不回收、
|
||||
/// 串 GC 只降手工堆账目而 arena 不减);run 边界(reset)后重新起算,
|
||||
/// 供单 run 分配上限判定。
|
||||
///
|
||||
/// 注意:手工堆账目只在 promote/字符串分配点更新,执行期对象属性区
|
||||
/// (元素/属性向量扩容)增长对其不可见——上限判定须配合
|
||||
/// [`Self::run_alloc_bytes_full`] 的深采样层。
|
||||
pub(crate) fn run_alloc_bytes(&self) -> usize {
|
||||
self.epoch.bump().allocated_bytes()
|
||||
+ self.gc_state.session_epoch.allocated_bytes()
|
||||
+ self.gc_state.session_bytes_allocated
|
||||
}
|
||||
|
||||
/// 本 run 累计分配字节的全量重算版:arena 计数器之外,逐一重算已登记对象
|
||||
/// 的堆数据(属性/元素向量 + upvalue 列表 + native 状态盒)、session 串、
|
||||
/// BigInt 与 upvalue cell 的容量。采样深度高于账目更新点,
|
||||
/// 兜住属性区扩容这类账目盲区。
|
||||
pub(crate) fn run_alloc_bytes_full(&self) -> u64 {
|
||||
let mut bytes = self.run_alloc_bytes() as u64;
|
||||
for &ptr in self
|
||||
.gc_state
|
||||
.epoch_object_ptrs
|
||||
.iter()
|
||||
.chain(self.gc_state.session_object_ptrs.iter())
|
||||
{
|
||||
if ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: ptr 来自对象表登记,dispatch 安全点处仍有效。
|
||||
bytes += SessionGc::object_heap_data_bytes(unsafe { &*ptr });
|
||||
}
|
||||
for &ptr in &self.gc_state.session_string_ptrs {
|
||||
// SAFETY: ptr 来自字符串表登记,收尾前始终有效。
|
||||
bytes += (std::mem::size_of::<oxide_types::object::JsString>() + unsafe { (*ptr).len() }) as u64;
|
||||
}
|
||||
bytes += (self.gc_state.session_bigint_ptrs.borrow().len() * std::mem::size_of::<num_bigint::BigInt>()) as u64;
|
||||
bytes += (self.gc_state.session_cell_ptrs.borrow().len() * std::mem::size_of::<Cell>()) as u64;
|
||||
bytes
|
||||
}
|
||||
|
||||
/// 无条件执行一次完整 session GC(mark + 移动式 sweep + 串/BigInt 清扫)。
|
||||
///
|
||||
/// # 副作用
|
||||
@@ -1525,9 +1567,10 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch(&mut self) -> Result<JsValue, String> {
|
||||
// config.max_steps 逐指令只读且循环内不变:提到循环外,免每次经 kernel_core
|
||||
// Arc 指针追寻读取(热点内唯一的 config 访问)。
|
||||
// config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外,
|
||||
// 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。
|
||||
let max_steps = self.kernel_core.config.max_steps;
|
||||
let max_alloc_bytes = self.kernel_core.config.max_alloc_bytes;
|
||||
let mut steps: u64 = 0;
|
||||
loop {
|
||||
steps += 1;
|
||||
@@ -1554,6 +1597,21 @@ impl Vm {
|
||||
return Err(format!("VM step limit exceeded at pc={}", self.pc));
|
||||
}
|
||||
}
|
||||
// 单 run 分配上限:账目盲区(属性区扩容)靠两层采样兜住——轻层每
|
||||
// 64 指令读三个计数器,深层每 2^18 指令全量重算(含逐对象属性区
|
||||
// 重算);超限 run 按步数超限同款处理(默认 skip / --no-skip 下
|
||||
// fail),防单测试 arena 高水位拖垮宿主。
|
||||
if let Some(cap) = max_alloc_bytes {
|
||||
let deep = (steps & 0x3FFFF) == 0;
|
||||
if deep || (steps & 0x3F) == 0 {
|
||||
let used = if deep { self.run_alloc_bytes_full() as usize } else { self.run_alloc_bytes() };
|
||||
if used > cap {
|
||||
vm_warn!("dispatch: memory limit {cap} exceeded (used {used}) at pc={}", self.pc);
|
||||
self.profiling.set_instruction_count(steps);
|
||||
return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.pc >= self.bytecode.len() {
|
||||
let tail: Vec<String> = self
|
||||
.bytecode
|
||||
|
||||
Reference in New Issue
Block a user