diff --git a/moli-renderer-v8/src/runtime/owner_maintenance.rs b/moli-renderer-v8/src/runtime/owner_maintenance.rs index 35eb31ade7..e64d384937 100644 --- a/moli-renderer-v8/src/runtime/owner_maintenance.rs +++ b/moli-renderer-v8/src/runtime/owner_maintenance.rs @@ -29,9 +29,10 @@ const ACTIVE_PAGE_MODERATE_MEMORY_PRESSURE_PERIOD: Duration = Duration::from_sec #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) enum RendererOwnerMaintenanceAction { - /// Ask V8 to perform its moderate-memory-pressure maintenance for the - /// isolate currently attached to this Page. - ModerateMemoryPressure, + /// Ask V8 to perform memory-pressure maintenance for the isolate currently + /// attached to this Page. The isolate selects moderate or critical + /// pressure from its current heap occupancy. + MemoryPressure, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -102,7 +103,7 @@ impl RendererPageOwnerMaintenanceResidence { }; Some(RendererOwnerMaintenanceTask { token, - action: RendererOwnerMaintenanceAction::ModerateMemoryPressure, + action: RendererOwnerMaintenanceAction::MemoryPressure, scheduled_for: deadline, }) } @@ -143,11 +144,11 @@ pub(super) async fn execute_owner_maintenance_task_on_local_lane( run_entry_on_bound_owner_local_store_local_task(local_executor, entry, move |entry| { Box::pin(async move { match task.action() { - RendererOwnerMaintenanceAction::ModerateMemoryPressure => entry + RendererOwnerMaintenanceAction::MemoryPressure => entry .page_vm_mut() .vm_mut() .renderer_document_isolate_ops() - .notify_renderer_document_isolate_moderate_memory_pressure(), + .notify_renderer_document_isolate_memory_pressure(), } }) }) diff --git a/moli-renderer-v8/src/script_vm.rs b/moli-renderer-v8/src/script_vm.rs index 2fc4f748f1..35e7139cbc 100644 --- a/moli-renderer-v8/src/script_vm.rs +++ b/moli-renderer-v8/src/script_vm.rs @@ -53,6 +53,17 @@ use url::Url; pub(crate) type ScriptVmBootstrapError = Box<(anyhow::Error, DomHost)>; +fn renderer_document_isolate_critical_pressure_required( + used_heap_size: usize, + heap_size_limit: usize, +) -> bool { + if heap_size_limit == 0 { + return false; + } + let critical_threshold = heap_size_limit / 3 + usize::from(!heap_size_limit.is_multiple_of(3)); + used_heap_size >= critical_threshold +} + #[cfg(test)] fn expect_ready_child_frame_owner_source_future_for_test(future: F) -> F::Output where @@ -6742,10 +6753,24 @@ impl ScriptVm { }) } - fn notify_renderer_document_isolate_moderate_memory_pressure(&mut self) -> Result<()> { + fn notify_renderer_document_isolate_memory_pressure(&mut self) -> Result<()> { self.renderer_document_isolate .with_entered_renderer_document_isolate(|isolate| { - isolate.memory_pressure_notification(v8::MemoryPressureLevel::Moderate); + let stats = isolate.get_heap_statistics(); + if renderer_document_isolate_critical_pressure_required( + stats.used_heap_size(), + stats.heap_size_limit(), + ) { + // One renderer process can host several Page isolates. A + // target with a rapidly replaced child realm must not hit + // V8's process-fatal heap limit before its owner can close + // that target. Escalate the existing periodic maintenance + // once this isolate consumes a third of its own limit. + isolate.memory_pressure_notification(v8::MemoryPressureLevel::Critical); + isolate.low_memory_notification(); + } else { + isolate.memory_pressure_notification(v8::MemoryPressureLevel::Moderate); + } Ok(()) }) } @@ -6821,11 +6846,8 @@ impl ScriptVmRendererDocumentIsolateOps<'_> { self.vm.collect_renderer_document_isolate_garbage() } - pub(super) fn notify_renderer_document_isolate_moderate_memory_pressure( - &mut self, - ) -> Result<()> { - self.vm - .notify_renderer_document_isolate_moderate_memory_pressure() + pub(super) fn notify_renderer_document_isolate_memory_pressure(&mut self) -> Result<()> { + self.vm.notify_renderer_document_isolate_memory_pressure() } } diff --git a/moli-renderer-v8/src/script_vm/tests/mod.rs b/moli-renderer-v8/src/script_vm/tests/mod.rs index e3ba66fd47..940d8c604c 100644 --- a/moli-renderer-v8/src/script_vm/tests/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/mod.rs @@ -5,7 +5,7 @@ use super::{ PostParseProcessingAction, PostParseRuntimeDriverStep, PostParseStageBoundary, PostParseTaskCompletion, PostParseTaskExecutionToken, PostParseTaskInvalidationPolicy, ReadyPostParseAction, ScriptVm, ScriptVmDefaultWorldBootstrap, StandaloneScriptVmHarness, - select_post_parse_driver_step, + renderer_document_isolate_critical_pressure_required, select_post_parse_driver_step, }; use crate::document_runtime::{ CurrentScriptContextSpec, DeferredPageTask, DeferredPageTaskLane, DeferredPageTaskState, @@ -5193,6 +5193,20 @@ fn dom_wrapper_expando_survives_renderer_document_isolate_garbage_collection() { assert_eq!(retained, "retained"); } +#[test] +fn renderer_document_isolate_memory_pressure_escalates_at_one_third_of_heap_limit() { + assert!(!renderer_document_isolate_critical_pressure_required(0, 0)); + assert!(!renderer_document_isolate_critical_pressure_required( + 33, 100 + )); + assert!(renderer_document_isolate_critical_pressure_required( + 34, 100 + )); + assert!(renderer_document_isolate_critical_pressure_required( + 100, 100 + )); +} + #[test] fn context_wrapper_cache_is_cleared_on_script_vm_teardown() { let mut vm = new_parsed_test_vm(