mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: GC sweep 复用 Vm.forwarding,消除每轮 HashMap 分配
This commit is contained in:
Generated
+1
@@ -1125,6 +1125,7 @@ dependencies = [
|
|||||||
"oxide_types",
|
"oxide_types",
|
||||||
"proptest",
|
"proptest",
|
||||||
"regex",
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ indexmap.workspace = true
|
|||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
unicode-normalization.workspace = true
|
unicode-normalization.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
rustc-hash.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
oxide_compiler = { path = "../oxide_compiler" }
|
oxide_compiler = { path = "../oxide_compiler" }
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::time::Instant;
|
|||||||
use oxide_kernel::vm_debug;
|
use oxide_kernel::vm_debug;
|
||||||
use oxide_types::object::{JsObject, PropMetaEntry};
|
use oxide_types::object::{JsObject, PropMetaEntry};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
use crate::builtins::{array_buffer, data_view, map, regexp, set, typed_array};
|
use crate::builtins::{array_buffer, data_view, map, regexp, set, typed_array};
|
||||||
use crate::vm::Vm;
|
use crate::vm::Vm;
|
||||||
@@ -187,7 +188,7 @@ impl SessionGc {
|
|||||||
|
|
||||||
pub(crate) fn sweep(&mut self, vm: &mut Vm) -> u64 {
|
pub(crate) fn sweep(&mut self, vm: &mut Vm) -> u64 {
|
||||||
let old_ptrs = std::mem::take(&mut vm.session_object_ptrs);
|
let old_ptrs = std::mem::take(&mut vm.session_object_ptrs);
|
||||||
let mut forwarding: HashMap<*mut JsObject, *mut JsObject> = HashMap::new();
|
let mut forwarding = std::mem::take(&mut vm.forwarding);
|
||||||
let new_arena = bumpalo::Bump::new();
|
let new_arena = bumpalo::Bump::new();
|
||||||
let mut survivors = 0u64;
|
let mut survivors = 0u64;
|
||||||
let mut dead = 0u64;
|
let mut dead = 0u64;
|
||||||
@@ -251,6 +252,8 @@ impl SessionGc {
|
|||||||
rewrite_vm_roots(vm, &forwarding);
|
rewrite_vm_roots(vm, &forwarding);
|
||||||
|
|
||||||
vm.session_object_ptrs = forwarding.values().copied().collect();
|
vm.session_object_ptrs = forwarding.values().copied().collect();
|
||||||
|
forwarding.clear();
|
||||||
|
vm.forwarding = forwarding;
|
||||||
vm.session_epoch = new_arena;
|
vm.session_epoch = new_arena;
|
||||||
vm.session_bytes_allocated = vm.session_object_ptrs.len() * size_of::<JsObject>();
|
vm.session_bytes_allocated = vm.session_object_ptrs.len() * size_of::<JsObject>();
|
||||||
|
|
||||||
@@ -342,7 +345,9 @@ impl Default for SessionGc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_forwarded_value(value: JsValue, forwarding: &HashMap<*mut JsObject, *mut JsObject>) -> JsValue {
|
fn rewrite_forwarded_value(
|
||||||
|
value: JsValue, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
||||||
|
) -> JsValue {
|
||||||
if !value.is_object() {
|
if !value.is_object() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -352,7 +357,7 @@ fn rewrite_forwarded_value(value: JsValue, forwarding: &HashMap<*mut JsObject, *
|
|||||||
.unwrap_or(value)
|
.unwrap_or(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject>) {
|
fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||||
for value in &mut vm.regs {
|
for value in &mut vm.regs {
|
||||||
*value = rewrite_forwarded_value(*value, forwarding);
|
*value = rewrite_forwarded_value(*value, forwarding);
|
||||||
}
|
}
|
||||||
@@ -532,6 +537,28 @@ mod tests {
|
|||||||
assert!(!vm.session_object_ptrs.iter().any(|ptr| unsafe { (*(*ptr)).is_gc_marked() }));
|
assert!(!vm.session_object_ptrs.iter().any(|ptr| unsafe { (*(*ptr)).is_gc_marked() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forwarding_is_cleared_after_sweep() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let root = plain_object(&mut vm);
|
||||||
|
let child = plain_object(&mut vm);
|
||||||
|
unsafe {
|
||||||
|
(*root).set_prop_at(0, JsValue::from_js_object(child));
|
||||||
|
}
|
||||||
|
let root_session = vm.promote_object(root);
|
||||||
|
vm.regs[0] = JsValue::from_js_object(root_session);
|
||||||
|
let _ = vm.promote_object(child);
|
||||||
|
let roots = vm.gc_roots();
|
||||||
|
let mut gc = std::mem::take(&mut vm.session_gc);
|
||||||
|
gc.mark(&vm, &roots);
|
||||||
|
let _ = gc.sweep(&mut vm);
|
||||||
|
vm.session_gc = gc;
|
||||||
|
|
||||||
|
// The reused forwarding map MUST be cleared after sweep, otherwise
|
||||||
|
// promote would observe stale old->new entries pointing into the freed arena.
|
||||||
|
assert!(vm.forwarding.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
fn vm_with_low_threshold() -> Vm {
|
fn vm_with_low_threshold() -> Vm {
|
||||||
let mut cfg = KernelConfig::minimal();
|
let mut cfg = KernelConfig::minimal();
|
||||||
cfg.set_session_gc_threshold(1);
|
cfg.set_session_gc_threshold(1);
|
||||||
|
|||||||
@@ -155,6 +155,11 @@ pub struct Vm {
|
|||||||
pub epoch: Epoch,
|
pub epoch: Epoch,
|
||||||
pub(crate) session_epoch: bumpalo::Bump,
|
pub(crate) session_epoch: bumpalo::Bump,
|
||||||
pub(crate) session_gc: SessionGc,
|
pub(crate) session_gc: SessionGc,
|
||||||
|
/// Long-lived forwarding map reused by GC sweep and promote.
|
||||||
|
/// `mem::take`'d out for use, `clear()`'d and put back — capacity retained
|
||||||
|
/// across collections, so no per-event `HashMap` allocation. `FxBuildHasher`
|
||||||
|
/// because the keys are raw pointers (collision resistance is irrelevant).
|
||||||
|
pub(crate) forwarding: HashMap<*mut JsObject, *mut JsObject, rustc_hash::FxBuildHasher>,
|
||||||
pub(crate) epoch_object_ptrs: Vec<*mut JsObject>,
|
pub(crate) epoch_object_ptrs: Vec<*mut JsObject>,
|
||||||
pub(crate) session_object_ptrs: Vec<*mut JsObject>,
|
pub(crate) session_object_ptrs: Vec<*mut JsObject>,
|
||||||
pub(crate) session_bytes_allocated: usize,
|
pub(crate) session_bytes_allocated: usize,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ impl Vm {
|
|||||||
epoch: Epoch::new(),
|
epoch: Epoch::new(),
|
||||||
session_epoch: bumpalo::Bump::new(),
|
session_epoch: bumpalo::Bump::new(),
|
||||||
session_gc: crate::session_gc::SessionGc::new(),
|
session_gc: crate::session_gc::SessionGc::new(),
|
||||||
|
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||||
epoch_object_ptrs: Vec::new(),
|
epoch_object_ptrs: Vec::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
@@ -77,6 +78,7 @@ impl Vm {
|
|||||||
epoch: Epoch::new(),
|
epoch: Epoch::new(),
|
||||||
session_epoch: bumpalo::Bump::new(),
|
session_epoch: bumpalo::Bump::new(),
|
||||||
session_gc: crate::session_gc::SessionGc::new(),
|
session_gc: crate::session_gc::SessionGc::new(),
|
||||||
|
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||||
epoch_object_ptrs: Vec::new(),
|
epoch_object_ptrs: Vec::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user