mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +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:
@@ -117,7 +117,10 @@ pub fn bind_async_disposable_stack(core: &Arc<KernelCore>, session: &KernelSessi
|
||||
ctor.set_data_meta(0u32, PropAttributes::new(false, false, false));
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
||||
JsValue::from_js_object(Box::into_raw(ctor))
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
// 登记进 world 释放表:session 收尾统一释放构造器本体与属性区。
|
||||
world.track_leaked_object(ctor_ptr);
|
||||
JsValue::from_js_object(ctor_ptr)
|
||||
};
|
||||
|
||||
write_proto_constructor(core, proto, ctor_val);
|
||||
|
||||
@@ -135,7 +135,10 @@ pub fn bind_disposable_stack(core: &Arc<KernelCore>, session: &KernelSession, gl
|
||||
ctor.set_data_meta(0u32, PropAttributes::new(false, false, false));
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
||||
JsValue::from_js_object(Box::into_raw(ctor))
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
// 登记进 world 释放表:session 收尾统一释放构造器本体与属性区。
|
||||
world.track_leaked_object(ctor_ptr);
|
||||
JsValue::from_js_object(ctor_ptr)
|
||||
};
|
||||
|
||||
write_proto_constructor(core, proto, ctor_val);
|
||||
|
||||
@@ -43,6 +43,8 @@ fn bind_error_subtype_constructor(
|
||||
ctor.set_data_meta(2u32, PropAttributes::new(false, false, true));
|
||||
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
// 登记进 world 释放表:session 收尾统一释放构造器本体与属性区。
|
||||
session.builtin_world().track_leaked_object(ctor_ptr);
|
||||
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let proto_ctor_shape = sh.make_shape(proto.shape_id(), sf.intern("constructor").0);
|
||||
|
||||
@@ -34,5 +34,8 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
|
||||
);
|
||||
|
||||
bind_global_value(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
||||
let iterator_ptr = Box::into_raw(iterator);
|
||||
// 登记进 world 释放表:session 收尾统一释放构造器本体与属性区。
|
||||
session.builtin_world().track_leaked_object(iterator_ptr);
|
||||
bind_global_value(core, global, "Iterator", JsValue::from_js_object(iterator_ptr));
|
||||
}
|
||||
|
||||
@@ -62,5 +62,8 @@ pub fn bind_reflect(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
|
||||
],
|
||||
);
|
||||
|
||||
bind_global_value(core, global, "Reflect", JsValue::from_js_object(Box::into_raw(reflect)));
|
||||
let reflect_ptr = Box::into_raw(reflect);
|
||||
// 登记进 world 释放表:session 收尾统一释放对象本体与属性区。
|
||||
session.builtin_world().track_leaked_object(reflect_ptr);
|
||||
bind_global_value(core, global, "Reflect", JsValue::from_js_object(reflect_ptr));
|
||||
}
|
||||
|
||||
@@ -129,14 +129,15 @@ pub(crate) fn apply_binding_table(
|
||||
/// 2. 为属性名开 shape 槽位并写入访问器 meta。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// getter 函数对象经 `Box::into_raw` 持有,与 `bind_method` 的方法 wrapper 同一生命周期约定
|
||||
/// (内置对象在 session 生命周期内不被回收)。
|
||||
/// getter 函数对象登记进 world 释放表,与 `bind_method` 的方法 wrapper 同一生命周期约定
|
||||
/// (session 收尾时统一释放)。
|
||||
pub(crate) fn bind_accessor_getter(
|
||||
core: &Arc<KernelCore>, session: &KernelSession, proto: &mut JsObject, name: &str, getter_fn: *const (),
|
||||
) {
|
||||
let shape_forge = core.shape_forge().as_ref();
|
||||
let string_forge = core.perm_interner().as_ref();
|
||||
let fn_proto_val = JsValue::from_js_object(session.builtin_world().function_proto.as_ptr() as *mut JsObject);
|
||||
let world = session.builtin_world();
|
||||
let fn_proto_val = JsValue::from_js_object(world.function_proto.as_ptr() as *mut JsObject);
|
||||
|
||||
let getter_name = format!("get {name}");
|
||||
let mut getter = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
|
||||
@@ -161,7 +162,9 @@ pub(crate) fn bind_accessor_getter(
|
||||
let length_pos = getter.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
getter.set_data_meta(length_pos, PropAttributes::new(false, false, true));
|
||||
|
||||
let getter_val = JsValue::from_js_object(Box::into_raw(getter));
|
||||
let getter_ptr = Box::into_raw(getter);
|
||||
world.track_leaked_object(getter_ptr);
|
||||
let getter_val = JsValue::from_js_object(getter_ptr);
|
||||
|
||||
let si = string_forge.intern(name).0;
|
||||
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
||||
@@ -180,8 +183,8 @@ pub(crate) fn bind_accessor_getter(
|
||||
/// 2. 为键开 shape 槽位并写入访问器 meta(enumerable=false, configurable=true)。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// getter/setter 函数对象经 `Box::into_raw` 持有,与 `bind_accessor_getter` 的
|
||||
/// 方法 wrapper 同一生命周期约定(内置对象在 session 生命周期内不被回收)。
|
||||
/// getter/setter 函数对象登记进 world 释放表,session 收尾时统一释放,
|
||||
/// 与 `bind_accessor_getter` 的 getter 及方法 wrapper 同一生命周期约定。
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(crate) fn bind_accessor_getset(
|
||||
core: &Arc<KernelCore>, session: &KernelSession, proto: &mut JsObject, key: u32, getter_name: &str,
|
||||
@@ -189,7 +192,8 @@ pub(crate) fn bind_accessor_getset(
|
||||
) {
|
||||
let shape_forge = core.shape_forge().as_ref();
|
||||
let string_forge = core.perm_interner().as_ref();
|
||||
let fn_proto_val = JsValue::from_js_object(session.builtin_world().function_proto.as_ptr() as *mut JsObject);
|
||||
let world = session.builtin_world();
|
||||
let fn_proto_val = JsValue::from_js_object(world.function_proto.as_ptr() as *mut JsObject);
|
||||
|
||||
let mut getter = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
|
||||
getter.set_function(true);
|
||||
@@ -211,7 +215,9 @@ pub(crate) fn bind_accessor_getset(
|
||||
getter.ensure_hash_props().push(JsValue::int(0));
|
||||
let length_pos = getter.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
getter.set_data_meta(length_pos, PropAttributes::new(false, false, true));
|
||||
let getter_val = JsValue::from_js_object(Box::into_raw(getter));
|
||||
let getter_ptr = Box::into_raw(getter);
|
||||
world.track_leaked_object(getter_ptr);
|
||||
let getter_val = JsValue::from_js_object(getter_ptr);
|
||||
|
||||
let mut setter = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
|
||||
setter.set_function(true);
|
||||
@@ -230,7 +236,9 @@ pub(crate) fn bind_accessor_getset(
|
||||
setter.ensure_hash_props().push(JsValue::int(1));
|
||||
let length_pos = setter.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
setter.set_data_meta(length_pos, PropAttributes::new(false, false, true));
|
||||
let setter_val = JsValue::from_js_object(Box::into_raw(setter));
|
||||
let setter_ptr = Box::into_raw(setter);
|
||||
world.track_leaked_object(setter_ptr);
|
||||
let setter_val = JsValue::from_js_object(setter_ptr);
|
||||
|
||||
// 访问器属性槽:enumerable=false、configurable=true(get/set 函数对象已持有)。
|
||||
let new_shape = shape_forge.make_shape(proto.shape_id(), key);
|
||||
@@ -301,7 +309,7 @@ pub(crate) fn bind_well_known_method(
|
||||
method_name,
|
||||
fn_ptr,
|
||||
nargs,
|
||||
world.fn_proto_val(),
|
||||
world,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -617,6 +625,7 @@ fn bind_error_subtype_global(
|
||||
ctor.set_data_meta(1u32, PropAttributes::new(false, false, true));
|
||||
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
session.builtin_world().track_leaked_object(ctor_ptr);
|
||||
bind_existing_global(core, global, name, JsValue::from_js_object(ctor_ptr));
|
||||
}
|
||||
|
||||
@@ -702,7 +711,9 @@ fn bind_reflect_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
),
|
||||
],
|
||||
);
|
||||
bind_existing_global(core, global, "Reflect", JsValue::from_js_object(Box::into_raw(reflect)));
|
||||
let reflect_ptr = Box::into_raw(reflect);
|
||||
session.builtin_world().track_leaked_object(reflect_ptr);
|
||||
bind_existing_global(core, global, "Reflect", JsValue::from_js_object(reflect_ptr));
|
||||
}
|
||||
|
||||
fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
@@ -722,7 +733,9 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
core,
|
||||
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
|
||||
);
|
||||
bind_existing_global(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
||||
let iterator_ptr = Box::into_raw(iterator);
|
||||
session.builtin_world().track_leaked_object(iterator_ptr);
|
||||
bind_existing_global(core, global, "Iterator", JsValue::from_js_object(iterator_ptr));
|
||||
// 迭代器原型方法(%IteratorPrototype% 的 @@iterator 与各集合原型 next)由
|
||||
// `bind_iterator_protos` 在 object 家族重建时统一安装,这里不重复绑定,
|
||||
// 避免保留原型经 dirty reset 时属性槽膨胀。
|
||||
|
||||
Reference in New Issue
Block a user