Bug 1717917 - Don't delay discarding JIT code for more than 30 seconds when animating. r=jonco

This fixes frequent OOMs on Win32 when running about:preferences tests.

The problem is that we don't discard JIT code frequently enough if we keep being
notified of animations. This resulted in leaking global objects via Baseline
IC stubs, likely because we keep loading things in the system zone. This patch
changes the heuristic to ignore the animation hint if the last code discard was
more than 30 seconds ago.

This is all quite brittle unfortunately and these heuristics should probably be
redone at some point as part of a bigger project.

Differential Revision: https://phabricator.services.mozilla.com/D123218
This commit is contained in:
Jan de Mooij 2021-08-20 15:00:51 +00:00
parent edc78017d7
commit 07228adcf0
3 changed files with 21 additions and 1 deletions

View File

@ -2145,6 +2145,12 @@ bool js::gc::IsCurrentlyAnimating(const TimeStamp& lastAnimationTime,
currentTime < (lastAnimationTime + oneSecond);
}
static bool DiscardedCodeRecently(Zone* zone, const TimeStamp& currentTime) {
static const auto thirtySeconds = TimeDuration::FromSeconds(30);
return !zone->lastDiscardedCodeTime().IsNull() &&
currentTime < (zone->lastDiscardedCodeTime() + thirtySeconds);
}
bool GCRuntime::shouldCompact() {
// Compact on shrinking GC if enabled. Skip compacting in incremental GCs
// if we are currently animating, unless the user is inactive or we're
@ -4143,7 +4149,8 @@ bool GCRuntime::shouldPreserveJITCode(Realm* realm,
if (realm->preserveJitCode()) {
return true;
}
if (IsCurrentlyAnimating(realm->lastAnimationTime, currentTime)) {
if (IsCurrentlyAnimating(realm->lastAnimationTime, currentTime) &&
DiscardedCodeRecently(realm->zone(), currentTime)) {
return true;
}
if (reason == JS::GCReason::DEBUG_GC) {

View File

@ -19,6 +19,7 @@
#include "jit/Ion.h"
#include "jit/JitZone.h"
#include "vm/Runtime.h"
#include "vm/Time.h"
#include "wasm/WasmInstance.h"
#include "debugger/DebugAPI-inl.h"
@ -402,6 +403,10 @@ void Zone::discardJitCode(JSFreeOp* fop, const DiscardOptions& options) {
return;
}
if (options.discardJitScripts && options.discardBaselineCode) {
lastDiscardedCodeTime_ = ReallyNow();
}
if (options.discardBaselineCode || options.discardJitScripts) {
#ifdef DEBUG
// Assert no JitScripts are marked as active.

View File

@ -319,6 +319,10 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
js::ZoneOrGCTaskData<js::jit::JitZone*> jitZone_;
// Last time at which JIT code was discarded for this zone. This is only set
// when JitScripts and Baseline code are discarded as well.
js::MainThreadData<mozilla::TimeStamp> lastDiscardedCodeTime_;
js::MainThreadData<bool> gcScheduled_;
js::MainThreadData<bool> gcScheduledSaved_;
js::MainThreadData<bool> gcPreserveCode_;
@ -428,6 +432,10 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
bool isPreservingCode() const { return gcPreserveCode_; }
mozilla::TimeStamp lastDiscardedCodeTime() const {
return lastDiscardedCodeTime_;
}
// Whether this zone can currently be collected.
bool canCollect();