mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
test: 新增内存占用基准用例与留存内存采集
bench 每轮采集在 workload 后 promote 根持有/会话引用的 epoch 对象并强制 一次 full GC,新增 peak_bytes / retained_bytes / retained_objects 三口径 (原 session_* 字段为执行期累计分配,不反映 workload 后留存);dispatch 循环增加 session 堆高水位采样,VM 暴露 session_bytes_peak 与 collect_session_gc。新增 8 个 mem_* 压力用例(对象图/字符串/闭包链/数组 增删/跨调用保留/Map/Set 保留/字符串拼接),基线扩至 76 条目。
This commit is contained in:
@@ -130,7 +130,7 @@ impl Vm {
|
||||
/// 函数目标豁免均直落 epoch 值);Map/Set 等原生盒按键值直插 epoch 值。
|
||||
/// `rewrite_object_values` 覆盖元素/meta/属性/proto/captured_this/home_object/
|
||||
/// cell 值;克隆子树经 forwarding 去重,环与共享引用各克隆一次。
|
||||
pub(crate) fn promote_session_epoch_refs(&mut self) {
|
||||
pub fn promote_session_epoch_refs(&mut self) {
|
||||
let objects = std::mem::take(&mut self.gc_state.session_object_ptrs);
|
||||
if objects.is_empty() {
|
||||
return;
|
||||
@@ -185,6 +185,40 @@ impl Vm {
|
||||
self.gc_state.session_object_ptrs.extend(objects);
|
||||
}
|
||||
|
||||
/// 把根直接持有的 epoch 对象(顶层 var 寄存器、挂起句柄等)晋升进 session,
|
||||
/// 并把根引用改写到克隆体。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 每个根 epoch 对象深克隆进 session(含 native 状态盒,子引用递归晋升),
|
||||
/// 账目计入克隆字节;根引用按转发表重写。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 供 workload 后观测点(基准留存测量)使用:本方法之后存活集完全对
|
||||
/// session 可见,后续完整 GC 的留存账目不再漏"仅驻留 epoch arena 的对象"。
|
||||
/// - 与 `promote_session_epoch_refs` 互补:本方法处理根直接持有的 epoch 对象,
|
||||
/// 后者处理 session 对象持有的 epoch 子引用(闭包捕获等绕过写屏障的来源)。
|
||||
pub fn promote_rooted_epoch_objects(&mut self) {
|
||||
let mut epoch_roots = Vec::new();
|
||||
self.for_each_root(|value| {
|
||||
if value.is_object() {
|
||||
let ptr = value.as_js_object_ptr();
|
||||
if !ptr.is_null() {
|
||||
// SAFETY: 根上的对象值均为 VM 自有存活 JsObject。
|
||||
if unsafe { &*ptr }.is_epoch() {
|
||||
epoch_roots.push(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let mut forwarding = std::mem::take(&mut self.gc_state.forwarding);
|
||||
for ptr in epoch_roots {
|
||||
self.promote_object_inner(ptr, &mut forwarding);
|
||||
}
|
||||
crate::session_gc::rewrite_vm_roots(self, &forwarding);
|
||||
forwarding.clear();
|
||||
self.gc_state.forwarding = forwarding;
|
||||
}
|
||||
|
||||
/// 逃逸写屏障:写向全局/session 对象的对象值不指向 epoch,否则 epoch
|
||||
/// 重置后悬垂。session 函数目标豁免晋升——克隆会令写入侧与字节码持有的
|
||||
/// 原始对象分裂(类构造器在原型上续建方法会落到非克隆体);函数持有的
|
||||
|
||||
@@ -915,7 +915,7 @@ fn rewrite_forwarded_value(
|
||||
.unwrap_or(value)
|
||||
}
|
||||
|
||||
fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||
pub(crate) fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
||||
// 统一遍历:与 for_each_value 共用同一字段清单。
|
||||
vm.rewrite_values(|value| rewrite_forwarded_value(value, forwarding));
|
||||
|
||||
@@ -969,6 +969,26 @@ impl Vm {
|
||||
self.gc_state.session_bytes_allocated
|
||||
}
|
||||
|
||||
/// 执行期 session 堆账目的峰值高水位(顶层指令边界采样,全量重置清零)。
|
||||
pub fn session_bytes_peak(&self) -> usize {
|
||||
self.gc_state.session_bytes_peak
|
||||
}
|
||||
|
||||
/// 无条件执行一次完整 session GC(mark + 移动式 sweep + 串/BigInt 清扫)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 存活对象复制进新 session arena,全部根与原生盒按转发表重写;
|
||||
/// `session_bytes_allocated` 重置为清扫后的存活字节。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入);
|
||||
/// 执行期触发仍走水位路径,本入口供事后观测(如基准测 workload 后留存堆)。
|
||||
pub fn collect_session_gc(&mut self) {
|
||||
let mut session_gc = std::mem::take(&mut self.gc_state.session_gc);
|
||||
session_gc.collect(self);
|
||||
self.gc_state.session_gc = session_gc;
|
||||
}
|
||||
|
||||
/// 当前 epoch 中已分配并跟踪的对象数量(未晋升到 session 的临时对象)。
|
||||
pub fn epoch_object_count(&self) -> usize {
|
||||
self.gc_state.epoch_object_ptrs.len()
|
||||
@@ -1517,9 +1537,15 @@ impl Vm {
|
||||
// inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。
|
||||
// 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅
|
||||
// 少量字段比较。
|
||||
if self.native_call_depth == 0 && self.gc_state.session_bytes_allocated >= self.gc_state.string_gc_watermark
|
||||
{
|
||||
self.maybe_collect_session_strings();
|
||||
if self.native_call_depth == 0 {
|
||||
// 峰值高水位:同一边界采样 session 堆账目上界
|
||||
let bytes = self.gc_state.session_bytes_allocated;
|
||||
if bytes > self.gc_state.session_bytes_peak {
|
||||
self.gc_state.session_bytes_peak = bytes;
|
||||
}
|
||||
if bytes >= self.gc_state.string_gc_watermark {
|
||||
self.maybe_collect_session_strings();
|
||||
}
|
||||
}
|
||||
if let Some(max_steps) = max_steps {
|
||||
if steps > max_steps {
|
||||
|
||||
@@ -38,6 +38,9 @@ pub(crate) struct GcState {
|
||||
/// `cell.value` 中的对象引用),只在 full_reset 统一释放。
|
||||
pub(crate) session_cell_ptrs: RefCell<Vec<*mut UpvalueCell>>,
|
||||
pub(crate) session_bytes_allocated: usize,
|
||||
/// 执行期 session 堆账目的峰值高水位(`session_bytes_allocated` 的采样上界),
|
||||
/// 顶层指令边界采样,全量重置清零。留存内存观测用。
|
||||
pub(crate) session_bytes_peak: usize,
|
||||
/// 执行期字符串 GC 的触发水位:本次收集后的存活字节 + 阈值增量。
|
||||
/// 仅当账目超过水位才在指令边界触发回收——活串超阈值时不会每指令重复
|
||||
/// 触发无死串可回收的白跑,且保证触发点恒在无 builtin 局部活值的边界。
|
||||
|
||||
@@ -100,6 +100,7 @@ impl Vm {
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
session_bytes_peak: 0,
|
||||
string_gc_watermark: gc_threshold,
|
||||
gc_threshold_cached: gc_threshold,
|
||||
gc_watermark: gc_threshold,
|
||||
@@ -212,6 +213,7 @@ impl Vm {
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
session_bytes_peak: 0,
|
||||
string_gc_watermark: gc_threshold,
|
||||
gc_threshold_cached: gc_threshold,
|
||||
gc_watermark: gc_threshold,
|
||||
@@ -329,6 +331,7 @@ impl Vm {
|
||||
self.gc_state.epoch_object_ptrs.clear();
|
||||
self.gc_state.session_epoch.reset();
|
||||
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;
|
||||
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
||||
self.symbols.reset();
|
||||
|
||||
Reference in New Issue
Block a user