Bug 1386660 - Part 6: Clarify relationships between prefs. r=sfink
Make it clearer how prefs, JSGC params, and their defaults relate to one
another.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -135,86 +135,107 @@ class BackgroundDecommitTask : public GC
/*
* Encapsulates all of the GC tunables. These are effectively constant and
* should only be modified by setParameter.
*/
class GCSchedulingTunables
{
/*
- * Soft limit on the number of bytes we are allowed to allocate in the GC
- * heap. Attempts to allocate gcthings over this limit will return null and
- * subsequently invoke the standard OOM machinery, independent of available
- * physical memory.
+ * JSGC_MAX_BYTES
+ *
+ * Maximum nominal heap before last ditch GC.
*/
UnprotectedData<size_t> gcMaxBytes_;
/*
+ * JSGC_MAX_NURSERY_BYTES
+ *
* Maximum nursery size for each zone group.
- * Initially DefaultNurseryBytes and can be set by
- * javascript.options.mem.nursery.max_kb
*/
ActiveThreadData<size_t> gcMaxNurseryBytes_;
/*
+ * JSGC_ALLOCATION_THRESHOLD
+ *
* The base value used to compute zone->threshold.gcTriggerBytes(). When
* usage.gcBytes() surpasses threshold.gcTriggerBytes() for a zone, the
* zone may be scheduled for a GC, depending on the exact circumstances.
*/
ActiveThreadOrGCTaskData<size_t> gcZoneAllocThresholdBase_;
/* Fraction of threshold.gcBytes() which triggers an incremental GC. */
UnprotectedData<float> zoneAllocThresholdFactor_;
/* The same except when doing so would interrupt an already running GC. */
UnprotectedData<float> zoneAllocThresholdFactorAvoidInterrupt_;
/*
* Number of bytes to allocate between incremental slices in GCs triggered
* by the zone allocation threshold.
+ *
+ * This value does not have a JSGCParamKey parameter yet.
*/
UnprotectedData<size_t> zoneAllocDelayBytes_;
/*
+ * JSGC_DYNAMIC_HEAP_GROWTH
+ *
* Totally disables |highFrequencyGC|, the HeapGrowthFactor, and other
* tunables that make GC non-deterministic.
*/
ActiveThreadData<bool> dynamicHeapGrowthEnabled_;
/*
+ * JSGC_HIGH_FREQUENCY_TIME_LIMIT
+ *
* We enter high-frequency mode if we GC a twice within this many
* microseconds. This value is stored directly in microseconds.
*/
ActiveThreadData<uint64_t> highFrequencyThresholdUsec_;
/*
+ * JSGC_HIGH_FREQUENCY_LOW_LIMIT
+ * JSGC_HIGH_FREQUENCY_HIGH_LIMIT
+ * JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX
+ * JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN
+ *
* When in the |highFrequencyGC| mode, these parameterize the per-zone
* "HeapGrowthFactor" computation.
*/
ActiveThreadData<uint64_t> highFrequencyLowLimitBytes_;
ActiveThreadData<uint64_t> highFrequencyHighLimitBytes_;
ActiveThreadData<double> highFrequencyHeapGrowthMax_;
ActiveThreadData<double> highFrequencyHeapGrowthMin_;
/*
+ * JSGC_LOW_FREQUENCY_HEAP_GROWTH
+ *
* When not in |highFrequencyGC| mode, this is the global (stored per-zone)
* "HeapGrowthFactor".
*/
ActiveThreadData<double> lowFrequencyHeapGrowth_;
/*
+ * JSGC_DYNAMIC_MARK_SLICE
+ *
* Doubles the length of IGC slices when in the |highFrequencyGC| mode.
*/
ActiveThreadData<bool> dynamicMarkSliceEnabled_;
/*
+ * JSGC_REFRESH_FRAME_SLICES_ENABLED
+ *
* Controls whether painting can trigger IGC slices.
*/
ActiveThreadData<bool> refreshFrameSlicesEnabled_;
/*
+ * JSGC_MIN_EMPTY_CHUNK_COUNT
+ * JSGC_MAX_EMPTY_CHUNK_COUNT
+ *
* Controls the number of empty chunks reserved for future allocation.
*/
UnprotectedData<uint32_t> minEmptyChunkCount_;
UnprotectedData<uint32_t> maxEmptyChunkCount_;
public:
GCSchedulingTunables();
@@ -1146,16 +1167,21 @@ class GCRuntime
*/
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
ActiveThreadData<VerifyPreTracer*> verifyPreData;
private:
UnprotectedData<bool> chunkAllocationSinceLastGC;
ActiveThreadData<int64_t> lastGCTime;
+ /*
+ * JSGC_MODE
+ * prefs: javascript.options.mem.gc_per_zone and
+ * javascript.options.mem.gc_incremental.
+ */
ActiveThreadData<JSGCMode> mode;
mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
/* During shutdown, the GC needs to clean up every possible object. */
ActiveThreadData<bool> cleanUpEverything;
// Gray marking must be done after all black marking is complete. However,
@@ -1280,27 +1306,35 @@ class GCRuntime
/*
* Indicates that a GC slice has taken place in the middle of an animation
* frame, rather than at the beginning. In this case, the next slice will be
* delayed so that we don't get back-to-back slices.
*/
ActiveThreadData<bool> interFrameGC;
- /* Default budget for incremental GC slice. See js/SliceBudget.h. */
+ /*
+ * Default budget for incremental GC slice. See js/SliceBudget.h.
+ *
+ * JSGC_SLICE_TIME_BUDGET
+ * pref: javascript.options.mem.gc_incremental_slice_ms,
+ */
ActiveThreadData<int64_t> defaultTimeBudget_;
/*
* We disable incremental GC if we encounter a Class with a trace hook
* that does not implement write barriers.
*/
ActiveThreadData<bool> incrementalAllowed;
/*
* Whether compacting GC can is enabled globally.
+ *
+ * JSGC_COMPACTING_ENABLED
+ * pref: javascript.options.mem.gc_compacting
*/
ActiveThreadData<bool> compactingEnabled;
ActiveThreadData<bool> rootsRemoved;
/*
* These options control the zealousness of the GC. At every allocation,
* nextScheduled is decremented. When it reaches zero we do a full GC.
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1788,92 +1788,195 @@ JS_RemoveWeakPointerCompartmentCallback(
extern JS_PUBLIC_API(void)
JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject*>* objp);
extern JS_PUBLIC_API(void)
JS_UpdateWeakPointerAfterGCUnbarriered(JSObject** objp);
typedef enum JSGCParamKey {
- /** Maximum nominal heap before last ditch GC. */
+ /**
+ * Maximum nominal heap before last ditch GC.
+ *
+ * Soft limit on the number of bytes we are allowed to allocate in the GC
+ * heap. Attempts to allocate gcthings over this limit will return null and
+ * subsequently invoke the standard OOM machinery, independent of available
+ * physical memory.
+ *
+ * Pref: javascript.options.mem.max
+ * Default: 0xffffffff
+ */
JSGC_MAX_BYTES = 0,
- /** Number of JS_malloc bytes before last ditch GC. */
+ /**
+ * Number of JS_malloc bytes before last ditch GC.
+ *
+ * Pref; javascript.options.mem.high_water_mark
+ * Default: 0xffffffff
+ */
JSGC_MAX_MALLOC_BYTES = 1,
- /** Maximum size of the generational GC nurseries. */
+ /**
+ * Maximum size of the generational GC nurseries.
+ *
+ * Pref: javascript.options.mem.nursery.max_kb
+ * Default: JS::DefaultNurseryBytes
+ */
JSGC_MAX_NURSERY_BYTES = 2,
/** Amount of bytes allocated by the GC. */
JSGC_BYTES = 3,
/** Number of times GC has been invoked. Includes both major and minor GC. */
JSGC_NUMBER = 4,
- /** Select GC mode. */
+ /**
+ * Select GC mode.
+ *
+ * See: JSGCMode in GCAPI.h
+ * prefs: javascript.options.mem.gc_per_zone and
+ * javascript.options.mem.gc_incremental.
+ * Default: JSGC_MODE_INCREMENTAL
+ */
JSGC_MODE = 6,
/** Number of cached empty GC chunks. */
JSGC_UNUSED_CHUNKS = 7,
/** Total number of allocated GC chunks. */
JSGC_TOTAL_CHUNKS = 8,
- /** Max milliseconds to spend in an incremental GC slice. */
+ /**
+ * Max milliseconds to spend in an incremental GC slice.
+ *
+ * Pref: javascript.options.mem.gc_incremental_slice_ms
+ * Default: DefaultTimeBudget.
+ */
JSGC_SLICE_TIME_BUDGET = 9,
- /** Maximum size the GC mark stack can grow to. */
+ /**
+ * Maximum size the GC mark stack can grow to.
+ *
+ * Pref: none
+ * Default: MarkStack::DefaultCapacity
+ */
JSGC_MARK_STACK_LIMIT = 10,
/**
- * GCs less than this far apart in time will be considered 'high-frequency GCs'.
+ * GCs less than this far apart in time will be considered 'high-frequency
+ * GCs'.
+ *
* See setGCLastBytes in jsgc.cpp.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms
+ * Default: HighFrequencyThresholdUsec
*/
JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
- /** Start of dynamic heap growth. */
+ /**
+ * Start of dynamic heap growth.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_low_limit_mb
+ * Default: HighFrequencyLowLimitBytes
+ */
JSGC_HIGH_FREQUENCY_LOW_LIMIT = 12,
- /** End of dynamic heap growth. */
+ /**
+ * End of dynamic heap growth.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_high_limit_mb
+ * Default: HighFrequencyHighLimitBytes
+ */
JSGC_HIGH_FREQUENCY_HIGH_LIMIT = 13,
- /** Upper bound of heap growth. */
+ /**
+ * Upper bound of heap growth.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_heap_growth_max
+ * Default: HighFrequencyHeapGrowthMax
+ */
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX = 14,
- /** Lower bound of heap growth. */
+ /**
+ * Lower bound of heap growth.
+ *
+ * Pref: javascript.options.mem.gc_high_frequency_heap_growth_min
+ * Default: HighFrequencyHeapGrowthMin
+ */
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN = 15,
- /** Heap growth for low frequency GCs. */
+ /**
+ * Heap growth for low frequency GCs.
+ *
+ * Pref: javascript.options.mem.gc_low_frequency_heap_growth
+ * Default: LowFrequencyHeapGrowth
+ */
JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
/**
* If false, the heap growth factor is fixed at 3. If true, it is determined
* based on whether GCs are high- or low- frequency.
+ *
+ * Pref: javascript.options.mem.gc_dynamic_heap_growth
+ * Default: DynamicHeapGrowthEnabled
*/
JSGC_DYNAMIC_HEAP_GROWTH = 17,
- /** If true, high-frequency GCs will use a longer mark slice. */
+ /**
+ * If true, high-frequency GCs will use a longer mark slice.
+ *
+ * Pref: javascript.options.mem.gc_dynamic_mark_slice
+ * Default: DynamicMarkSliceEnabled
+ */
JSGC_DYNAMIC_MARK_SLICE = 18,
- /** Lower limit after which we limit the heap growth. */
+ /**
+ * Lower limit after which we limit the heap growth.
+ *
+ * The base value used to compute zone->threshold.gcTriggerBytes(). When
+ * usage.gcBytes() surpasses threshold.gcTriggerBytes() for a zone, the
+ * zone may be scheduled for a GC, depending on the exact circumstances.
+ *
+ * Pref: javascript.options.mem.gc_allocation_threshold_mb
+ * Default GCZoneAllocThresholdBase
+ */
JSGC_ALLOCATION_THRESHOLD = 19,
/**
* We try to keep at least this many unused chunks in the free chunk pool at
* all times, even after a shrinking GC.
+ *
+ * Pref: javascript.options.mem.gc_min_empty_chunk_count
+ * Default: MinEmptyChunkCount
*/
JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
- /** We never keep more than this many unused chunks in the free chunk pool. */
+ /**
+ * We never keep more than this many unused chunks in the free chunk
+ * pool.
+ *
+ * Pref: javascript.options.mem.gc_min_empty_chunk_count
+ * Default: MinEmptyChunkCount
+ */
JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
- /** Whether compacting GC is enabled. */
+ /**
+ * Whether compacting GC is enabled.
+ *
+ * Pref: javascript.options.mem.gc_compacting
+ * Default: CompactingEnabled
+ */
JSGC_COMPACTING_ENABLED = 23,
- /** If true, painting can trigger IGC slices. */
+ /**
+ * If true, painting can trigger IGC slices.
+ *
+ * Pref: javascript.options.mem.gc_refresh_frame_slices_enabled
+ * Default: RefreshFrameSlicesEnabled
+ */
JSGC_REFRESH_FRAME_SLICES_ENABLED = 24,
} JSGCParamKey;
extern JS_PUBLIC_API(void)
JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value);
extern JS_PUBLIC_API(void)
JS_ResetGCParameter(JSContext* cx, JSGCParamKey key);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -262,47 +262,76 @@ using mozilla::Move;
using mozilla::Swap;
using mozilla::TimeStamp;
using JS::AutoGCRooter;
/*
* Default settings for tuning the GC. Some of these can be set at runtime,
* This list is not complete, some tuning parameters are not listed here.
+ *
+ * If you change the values here, please also consider changing them in
+ * modules/libpref/init/all.js where they are duplicated for the Firefox
+ * preferences.
*/
namespace js {
namespace gc {
namespace TuningDefaults {
+ /* JSGC_ALLOCATION_THRESHOLD */
static const size_t GCZoneAllocThresholdBase = 30 * 1024 * 1024;
static const float ZoneAllocThresholdFactor = 0.9f;
static const float ZoneAllocThresholdFactorAvoidInterrupt = 0.9f;
+
+ /* no parameter */
static const size_t ZoneAllocDelayBytes = 1024 * 1024;
+
+ /* JSGC_DYNAMIC_HEAP_GROWTH */
static const bool DynamicHeapGrowthEnabled = false;
+
+ /* JSGC_HIGH_FREQUENCY_TIME_LIMIT */
static const uint64_t HighFrequencyThresholdUsec = 1000000;
+
+ /* JSGC_HIGH_FREQUENCY_LOW_LIMIT */
static const uint64_t HighFrequencyLowLimitBytes = 100 * 1024 * 1024;
+
+ /* JSGC_HIGH_FREQUENCY_HIGH_LIMIT */
static const uint64_t HighFrequencyHighLimitBytes = 500 * 1024 * 1024;
+
+ /* JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX */
static const double HighFrequencyHeapGrowthMax = 3.0;
+
+ /* JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN */
static const double HighFrequencyHeapGrowthMin = 1.5;
+
+ /* JSGC_LOW_FREQUENCY_HEAP_GROWTH */
static const double LowFrequencyHeapGrowth = 1.5;
+
+ /* JSGC_DYNAMIC_MARK_SLICE */
static const bool DynamicMarkSliceEnabled = false;
+
+ /* JSGC_REFRESH_FRAME_SLICES_ENABLED */
static const bool RefreshFrameSlicesEnabled = true;
+
+ /* JSGC_MIN_EMPTY_CHUNK_COUNT */
static const uint32_t MinEmptyChunkCount = 1;
+
+ /* JSGC_MAX_EMPTY_CHUNK_COUNT */
static const uint32_t MaxEmptyChunkCount = 30;
- /*
- * JSGC_SLICE_TIME_BUDGET.
- * javascript.options.mem.gc_incremental_slice_ms
- */
+ /* JSGC_SLICE_TIME_BUDGET */
static const int64_t DefaultTimeBudget =
SliceBudget::UnlimitedTimeBudget;
+ /* JSGC_MODE */
static const JSGCMode Mode = JSGC_MODE_INCREMENTAL;
+ /* JSGC_COMPACTING_ENABLED */
static const bool CompactingEnabled = true;
+
}}} // namespace js::gc::TuningDefaults
/* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
static const int IGC_MARK_SLICE_MULTIPLIER = 2;
const AllocKind gc::slotsToThingKind[] = {
/* 0 */ AllocKind::OBJECT0, AllocKind::OBJECT2, AllocKind::OBJECT2, AllocKind::OBJECT4,
/* 4 */ AllocKind::OBJECT4, AllocKind::OBJECT8, AllocKind::OBJECT8, AllocKind::OBJECT8,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1437,48 +1437,98 @@ pref("javascript.options.ion.offthread_c
#ifdef DEBUG
pref("javascript.options.jit.full_debug_checks", true);
#endif
// This preference instructs the JS engine to discard the
// source of any privileged JS after compilation. This saves
// memory, but makes things like Function.prototype.toSource()
// fail.
pref("javascript.options.discardSystemSource", false);
-// This preference limits the memory usage of javascript.
+
+// Many of the the following preferences tune the SpiderMonkey GC, if you
+// change the defaults here please also consider changing them in
+// js/src/jsgc.cpp. They're documented in js/src/jsapi.h.
+
+// JSGC_MAX_MALLOC_BYTES
+// This preference limits the malloc memory that javascript objects may use.
// If you want to change these values for your device,
// please find Bug 417052 comment 17 and Bug 456721
// Comment 32 and Bug 613551.
+// Override the shell's default of 0xffffffff
pref("javascript.options.mem.high_water_mark", 128);
+
+// JSGC_MAX_BYTES
+// SpiderMonkey defaults to 2^32-1 bytes, but this is measured in MB so that
+// cannot be represented directly in order to show it in about:config.
pref("javascript.options.mem.max", -1);
-pref("javascript.options.mem.nursery.max_kb", -1);
+
+// JSGC_MAX_NURSERY_BYTES
+#if defined(ANDROID) || defined(XP_IOS) || defined(MOZ_B2G)
+pref("javascript.options.mem.nursery.max_kb", 4096);
+#else
+pref("javascript.options.mem.nursery.max_kb", 16384);
+#endif
+
+// JSGC_MODE
pref("javascript.options.mem.gc_per_zone", true);
pref("javascript.options.mem.gc_incremental", true);
+
+// JSGC_SLICE_TIME_BUDGET
+// Override the shell's default of unlimited slice time.
pref("javascript.options.mem.gc_incremental_slice_ms", 5);
+
+// JSGC_COMPACTING_ENABLED
pref("javascript.options.mem.gc_compacting", true);
+
pref("javascript.options.mem.log", false);
pref("javascript.options.mem.notify", false);
pref("javascript.options.gc_on_memory_pressure", true);
pref("javascript.options.compact_on_user_inactive", true);
#ifdef NIGHTLY_BUILD
pref("javascript.options.compact_on_user_inactive_delay", 15000); // ms
#else
pref("javascript.options.compact_on_user_inactive_delay", 300000); // ms
#endif
+// JSGC_HIGH_FREQUENCY_TIME_LIMIT
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000);
+
+// JSGC_HIGH_FREQUENCY_LOW_LIMIT
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);
+
+// JSGC_HIGH_FREQUENCY_HIGH_LIMIT
pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 500);
+
+// JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
+
+// JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
+
+// JSGC_LOW_FREQUENCY_HEAP_GROWTH
pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
+
+// JSGC_DYNAMIC_HEAP_GROWTH
+// Override SpiderMonkey default (false).
pref("javascript.options.mem.gc_dynamic_heap_growth", true);
+
+// JSGC_DYNAMIC_MARK_SLICE
+// Override SpiderMonkey default (false).
pref("javascript.options.mem.gc_dynamic_mark_slice", true);
+
+// JSGC_REFRESH_FRAME_SLICES_ENABLED
pref("javascript.options.mem.gc_refresh_frame_slices_enabled", true);
+
+// JSGC_ALLOCATION_THRESHOLD
pref("javascript.options.mem.gc_allocation_threshold_mb", 30);
+
+// JSGC_MIN_EMPTY_CHUNK_COUNT
pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
+
+// JSGC_MAX_EMPTY_CHUNK_COUNT
pref("javascript.options.mem.gc_max_empty_chunk_count", 30);
pref("javascript.options.showInConsole", false);
pref("javascript.options.shared_memory", true);
pref("javascript.options.throw_on_debuggee_would_run", false);
pref("javascript.options.dump_stack_on_debuggee_would_run", false);