SpiderMonkey has incremental marking which is done in small arbitrary pauses and incremental sweeping which is done in pauses proportional to the zone size.
Summary: Extant ECMAScript implementations have internal mechanisms for controlling scheduling and duration of short GC pauses.
I propose a global,
Realtime, with two static methods:
Realtime.avoidPausingFor(period) - Hints the runtime to avoid long-pausing the current JS worker/thread between now and now plus
Realtime.canPauseFor(period) alternate name
idlePeriodFor(period) - Hints the runtime that it can pause the current JS thread from now to now plus
puration milliseconds. The call signals that the user experience will not be degraded if the ECMAScript engine pauses for part or all of the duration.
Use cases for
- Preferring to run garbage collection pauses during low-action segments of a game, such as on an interstitial screen, or when there are no nearby enemies, instead of running it while the player is battling enemies
- Running GC when a user is not interacting with an article
- Running GC while the application is transitioning to a new state (and thus lots of memory is becoming garbage) and cannot respond to user input
- Running GC work of a Node.js game server that runs at 30FPS in between frames
Use cases for
- Signaling a web worker that the main thread offloads animation or graphics computation to should run GC only after the current frame is done, to allow normal web workers to have independent collections
- Avoiding collecting garbage while serving an HTTP request from Node.js to prevent end-user latency from increasing
Things to bikeshed on
canPauseFor does not mandate triggering a GC cycle the same way
System.gc in Java might. It signals an opportunity to do pauses. The spec text would not be normative upon the behavior of
avoidPausingFor for implementations should request new memory from the operating system if it is required to fulfill the request, up to the security restrictions of the host environment. However, GC may still run is necessary, runtimes are not asked to support a "never-fail allocator" or "emergency allocator."
A long-pause is an implementation-defined duration that varies between garbage collector implementations. However, it is understood to be less than 1 cumulative millisecond.
canPauseFor overrides any previous avoidPausingFor directive. This is to allow the developer to ask the implementation to avoid pausing for the rest of the frame, then when the frame is complete, allowing the implementation to pause for the remaining frame time. An avoidPausingFor call does not override an earlier canPauseFor call. This restriction may be lifted in a future spec if necessary
The host environment may restrict, ignore, set minimums, and/or set maximums on the Realtime calls. In other words, the host has ultimate control over GC scheduling and Realtime calls are an implementation hint.
This spec may not need
canPauseFor hint to run some GC work may be enough. On the other hand, it may have meaning on single-threaded processors (common in cloud servers) or concurrent GCs: Avoid running a GC operating system thread in parallel, wait until the period has expired to do that. It may be dropped from the proposal as it moves towards Stage 3 and implementation experience is gained. It is suggested engines implement
canPauseFor and gather real-world performance data in origin trials and synthetic benchmarks before deciding whether
avoidPausingFor should be removed from the spec.
A future version of the spec may specify a way to define a Realm's maximum pause length. For instance, it may be possible to spawn a Realm with a 0 to activate the engine's lowest latency collector, such as reference-counting cycle collector.