Bug 958492 - Start an incremental GC when nearing the allocation threshold for non-incremental zone GCs, r=billm.

This commit is contained in:
Brian Hackett 2014-10-18 08:11:22 -07:00
Родитель 3c7249f931
Коммит a159f4b2a3
5 изменённых файлов: 69 добавлений и 5 удалений

Просмотреть файл

@ -84,6 +84,15 @@ class GCSchedulingTunables
*/
size_t gcZoneAllocThresholdBase_;
/* Fraction of threshold.gcBytes() which triggers an incremental GC. */
double zoneAllocThresholdFactor_;
/*
* Number of bytes to allocate between incremental slices in GCs triggered
* by the zone allocation threshold.
*/
size_t zoneAllocDelayBytes_;
/*
* Totally disables |highFrequencyGC|, the HeapGrowthFactor, and other
* tunables that make GC non-deterministic.
@ -126,6 +135,8 @@ class GCSchedulingTunables
GCSchedulingTunables()
: gcMaxBytes_(0),
gcZoneAllocThresholdBase_(30 * 1024 * 1024),
zoneAllocThresholdFactor_(0.9),
zoneAllocDelayBytes_(1024 * 1024),
dynamicHeapGrowthEnabled_(false),
highFrequencyThresholdUsec_(1000 * 1000),
highFrequencyLowLimitBytes_(100 * 1024 * 1024),
@ -140,6 +151,8 @@ class GCSchedulingTunables
size_t gcMaxBytes() const { return gcMaxBytes_; }
size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
double zoneAllocThresholdFactor() const { return zoneAllocThresholdFactor_; }
size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; }
bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; }

Просмотреть файл

@ -28,6 +28,7 @@ JS::Zone::Zone(JSRuntime *rt)
gcMallocBytes(0),
gcMallocGCTriggered(false),
usage(&rt->gc.usage),
gcDelayBytes(0),
data(nullptr),
isSystem(false),
usedByExclusiveThread(false),

Просмотреть файл

@ -283,6 +283,10 @@ struct Zone : public JS::shadow::Zone,
// Thresholds used to trigger GC.
js::gc::ZoneHeapThreshold threshold;
// Amount of data to allocate before triggering a new incremental slice for
// the current GC.
size_t gcDelayBytes;
// Per-zone data for use by an embedder.
void *data;

Просмотреть файл

@ -971,9 +971,36 @@ Chunk::allocateArena(Zone *zone, AllocKind thingKind)
zone->usage.addGCArena();
if (!rt->isHeapCompacting() && zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
AutoUnlockGC unlock(rt);
rt->gc.triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
if (!rt->isHeapCompacting()) {
size_t usedBytes = zone->usage.gcBytes();
size_t thresholdBytes = zone->threshold.gcTriggerBytes();
size_t igcThresholdBytes = thresholdBytes * rt->gc.tunables.zoneAllocThresholdFactor();
if (usedBytes >= thresholdBytes) {
// The threshold has been surpassed, immediately trigger a GC,
// which will be done non-incrementally.
AutoUnlockGC unlock(rt);
rt->gc.triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
} else if (usedBytes >= igcThresholdBytes) {
// Reduce the delay to the start of the next incremental slice.
if (zone->gcDelayBytes < ArenaSize)
zone->gcDelayBytes = 0;
else
zone->gcDelayBytes -= ArenaSize;
if (!zone->gcDelayBytes) {
// Start or continue an in progress incremental GC. We do this
// to try to avoid performing non-incremental GCs on zones
// which allocate a lot of data, even when incremental slices
// can't be triggered via scheduling in the event loop.
AutoUnlockGC unlock(rt);
rt->gc.triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
// Delay the next slice until a certain amount of allocation
// has been performed.
zone->gcDelayBytes = rt->gc.tunables.zoneAllocDelayBytes();
}
}
}
return aheader;
@ -5675,8 +5702,13 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
State prevState = incrementalState;
if (!incremental) {
/* If non-incremental GC was requested, reset incremental GC. */
resetIncrementalGC("requested");
// Reset any in progress incremental GC if this was triggered via the
// API. This isn't required for correctness, but sometimes during tests
// the caller expects this GC to collect certain objects, and we need
// to make sure to collect everything possible.
if (reason != JS::gcreason::ALLOC_TRIGGER)
resetIncrementalGC("requested");
stats.nonincremental("requested");
budget = SliceBudget::Unlimited;
} else {
@ -5874,6 +5906,8 @@ GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64
int64_t budget;
if (millis)
budget = SliceBudget::TimeBudget(millis);
else if (reason == JS::gcreason::ALLOC_TRIGGER)
budget = sliceBudget;
else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
else

Просмотреть файл

@ -5996,6 +5996,7 @@ main(int argc, char **argv, char **envp)
#ifdef JSGC_GENERATIONAL
|| !op.addBoolOption('\0', "no-ggc", "Disable Generational GC")
#endif
|| !op.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC")
|| !op.addIntOption('\0', "available-memory", "SIZE",
"Select GC settings based on available memory (MB)", 0)
#if defined(JS_CODEGEN_ARM)
@ -6132,6 +6133,17 @@ main(int argc, char **argv, char **envp)
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
// Set some parameters to allow incremental GC in low memory conditions,
// as is done for the browser, except in more-deterministic builds or when
// disabled by command line options.
#ifndef JS_MORE_DETERMINISTIC
if (!op.getBoolOption("no-incremental-gc")) {
JS_SetGCParameter(rt, JSGC_DYNAMIC_HEAP_GROWTH, 1);
JS_SetGCParameter(rt, JSGC_DYNAMIC_MARK_SLICE, 1);
JS_SetGCParameter(rt, JSGC_SLICE_TIME_BUDGET, 10);
}
#endif
js::SetPreserveWrapperCallback(rt, DummyPreserveWrapperCallback);
result = Shell(cx, &op, envp);