зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset eefad3e4e594 for valgrind failures on a CLOSED TREE.
--HG-- extra : rebase_source : b5b018f5a5f23fe12600e64508ef075794d707fe
This commit is contained in:
Родитель
a52ac8a09b
Коммит
0b05d24c84
|
@ -104,126 +104,6 @@ struct ConservativeGCData
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
size_t gcMaxBytes_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The base value used to compute zone->trigger.gcBytes(). When
|
|
||||||
* usage.gcBytes() surpasses threshold.gcBytes() for a zone, the zone may
|
|
||||||
* be scheduled for a GC, depending on the exact circumstances.
|
|
||||||
*/
|
|
||||||
size_t gcZoneAllocThresholdBase_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Totally disables |highFrequencyGC|, the HeapGrowthFactor, and other
|
|
||||||
* tunables that make GC non-deterministic.
|
|
||||||
*/
|
|
||||||
bool dynamicHeapGrowthEnabled_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We enter high-frequency mode if we GC a twice within this many
|
|
||||||
* microseconds. This value is stored directly in microseconds.
|
|
||||||
*/
|
|
||||||
uint64_t highFrequencyThresholdUsec_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When in the |highFrequencyGC| mode, these parameterize the per-zone
|
|
||||||
* "HeapGrowthFactor" computation.
|
|
||||||
*/
|
|
||||||
uint64_t highFrequencyLowLimitBytes_;
|
|
||||||
uint64_t highFrequencyHighLimitBytes_;
|
|
||||||
double highFrequencyHeapGrowthMax_;
|
|
||||||
double highFrequencyHeapGrowthMin_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When not in |highFrequencyGC| mode, this is the global (stored per-zone)
|
|
||||||
* "HeapGrowthFactor".
|
|
||||||
*/
|
|
||||||
double lowFrequencyHeapGrowth_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Doubles the length of IGC slices when in the |highFrequencyGC| mode.
|
|
||||||
*/
|
|
||||||
bool dynamicMarkSliceEnabled_;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Controls the number of empty chunks reserved for future allocation.
|
|
||||||
*/
|
|
||||||
unsigned minEmptyChunkCount_;
|
|
||||||
unsigned maxEmptyChunkCount_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GCSchedulingTunables()
|
|
||||||
: gcMaxBytes_(0),
|
|
||||||
gcZoneAllocThresholdBase_(30 * 1024 * 1024),
|
|
||||||
dynamicHeapGrowthEnabled_(false),
|
|
||||||
highFrequencyThresholdUsec_(1000 * 1000),
|
|
||||||
highFrequencyLowLimitBytes_(100 * 1024 * 1024),
|
|
||||||
highFrequencyHighLimitBytes_(500 * 1024 * 1024),
|
|
||||||
highFrequencyHeapGrowthMax_(3.0),
|
|
||||||
highFrequencyHeapGrowthMin_(1.5),
|
|
||||||
lowFrequencyHeapGrowth_(1.5),
|
|
||||||
dynamicMarkSliceEnabled_(false),
|
|
||||||
minEmptyChunkCount_(1),
|
|
||||||
maxEmptyChunkCount_(30)
|
|
||||||
{}
|
|
||||||
|
|
||||||
size_t gcMaxBytes() const { return gcMaxBytes_; }
|
|
||||||
size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
|
|
||||||
bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
|
|
||||||
uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
|
|
||||||
uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; }
|
|
||||||
uint64_t highFrequencyHighLimitBytes() const { return highFrequencyHighLimitBytes_; }
|
|
||||||
double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; }
|
|
||||||
double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
|
|
||||||
double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
|
|
||||||
bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
|
|
||||||
unsigned minEmptyChunkCount() const { return minEmptyChunkCount_; }
|
|
||||||
unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
|
|
||||||
|
|
||||||
void setParameter(JSGCParamKey key, uint32_t value);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal values that effect GC scheduling that are not directly exposed
|
|
||||||
* in the GC API.
|
|
||||||
*/
|
|
||||||
class GCSchedulingState
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Influences how we schedule and run GC's in several subtle ways. The most
|
|
||||||
* important factor is in how it controls the "HeapGrowthFactor". The
|
|
||||||
* growth factor is a measure of how large (as a percentage of the last GC)
|
|
||||||
* the heap is allowed to grow before we try to schedule another GC.
|
|
||||||
*/
|
|
||||||
bool inHighFrequencyGCMode_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GCSchedulingState()
|
|
||||||
: inHighFrequencyGCMode_(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool inHighFrequencyGCMode() const { return inHighFrequencyGCMode_; }
|
|
||||||
|
|
||||||
void updateHighFrequencyMode(uint64_t lastGCTime, uint64_t currentTime,
|
|
||||||
const GCSchedulingTunables &tunables) {
|
|
||||||
inHighFrequencyGCMode_ =
|
|
||||||
tunables.isDynamicHeapGrowthEnabled() && lastGCTime &&
|
|
||||||
lastGCTime + tunables.highFrequencyThresholdUsec() > currentTime;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
struct Callback {
|
struct Callback {
|
||||||
F op;
|
F op;
|
||||||
|
@ -302,6 +182,7 @@ class GCRuntime
|
||||||
void setDeterministic(bool enable);
|
void setDeterministic(bool enable);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
size_t maxBytesAllocated() { return maxBytes; }
|
||||||
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
|
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -422,6 +303,7 @@ class GCRuntime
|
||||||
|
|
||||||
double computeHeapGrowthFactor(size_t lastBytes);
|
double computeHeapGrowthFactor(size_t lastBytes);
|
||||||
size_t computeTriggerBytes(double growthFactor, size_t lastBytes, JSGCInvocationKind gckind);
|
size_t computeTriggerBytes(double growthFactor, size_t lastBytes, JSGCInvocationKind gckind);
|
||||||
|
size_t allocationThreshold() { return allocThreshold; }
|
||||||
|
|
||||||
JSGCMode gcMode() const { return mode; }
|
JSGCMode gcMode() const { return mode; }
|
||||||
void setGCMode(JSGCMode m) {
|
void setGCMode(JSGCMode m) {
|
||||||
|
@ -532,10 +414,6 @@ class GCRuntime
|
||||||
/* Track heap usage for this runtime. */
|
/* Track heap usage for this runtime. */
|
||||||
HeapUsage usage;
|
HeapUsage usage;
|
||||||
|
|
||||||
/* GC scheduling state and parameters. */
|
|
||||||
GCSchedulingTunables tunables;
|
|
||||||
GCSchedulingState schedulingState;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* Set of all GC chunks with at least one allocated thing. The
|
* Set of all GC chunks with at least one allocated thing. The
|
||||||
|
@ -557,6 +435,7 @@ class GCRuntime
|
||||||
|
|
||||||
js::RootedValueMap rootsHash;
|
js::RootedValueMap rootsHash;
|
||||||
|
|
||||||
|
size_t maxBytes;
|
||||||
size_t maxMallocBytes;
|
size_t maxMallocBytes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -572,7 +451,19 @@ class GCRuntime
|
||||||
|
|
||||||
JSGCMode mode;
|
JSGCMode mode;
|
||||||
|
|
||||||
|
size_t allocThreshold;
|
||||||
|
bool highFrequencyGC;
|
||||||
|
uint64_t highFrequencyTimeThreshold;
|
||||||
|
uint64_t highFrequencyLowLimitBytes;
|
||||||
|
uint64_t highFrequencyHighLimitBytes;
|
||||||
|
double highFrequencyHeapGrowthMax;
|
||||||
|
double highFrequencyHeapGrowthMin;
|
||||||
|
double lowFrequencyHeapGrowth;
|
||||||
|
bool dynamicHeapGrowth;
|
||||||
|
bool dynamicMarkSlice;
|
||||||
uint64_t decommitThreshold;
|
uint64_t decommitThreshold;
|
||||||
|
unsigned minEmptyChunkCount;
|
||||||
|
unsigned maxEmptyChunkCount;
|
||||||
|
|
||||||
/* During shutdown, the GC needs to clean up every possible object. */
|
/* During shutdown, the GC needs to clean up every possible object. */
|
||||||
bool cleanUpEverything;
|
bool cleanUpEverything;
|
||||||
|
@ -707,13 +598,13 @@ class GCRuntime
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These options control the zealousness of the GC. The fundamental values
|
* These options control the zealousness of the GC. The fundamental values
|
||||||
* are nextScheduled and gcDebugCompartmentGC. At every allocation,
|
* are nextScheduled and gcDebugCompartmentGC. At every allocation,
|
||||||
* nextScheduled is decremented. When it reaches zero, we do either a full
|
* nextScheduled is decremented. When it reaches zero, we do either a
|
||||||
* or a compartmental GC, based on debugCompartmentGC.
|
* full or a compartmental GC, based on debugCompartmentGC.
|
||||||
*
|
*
|
||||||
* At this point, if zeal_ is one of the types that trigger periodic
|
* At this point, if zeal_ is one of the types that trigger periodic
|
||||||
* collection, then nextScheduled is reset to the value of zealFrequency.
|
* collection, then nextScheduled is reset to the value of
|
||||||
* Otherwise, no additional GCs take place.
|
* zealFrequency. Otherwise, no additional GCs take place.
|
||||||
*
|
*
|
||||||
* You can control these values in several ways:
|
* You can control these values in several ways:
|
||||||
* - Pass the -Z flag to the shell (see the usage info for details)
|
* - Pass the -Z flag to the shell (see the usage info for details)
|
||||||
|
@ -723,10 +614,10 @@ class GCRuntime
|
||||||
* If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and
|
* If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and
|
||||||
* whenever a GC poke happens). This option is mainly useful to embedders.
|
* whenever a GC poke happens). This option is mainly useful to embedders.
|
||||||
*
|
*
|
||||||
* We use zeal_ == 4 to enable write barrier verification. See the comment
|
* We use zeal_ == 4 to enable write barrier verification. See the comment
|
||||||
* in jsgc.cpp for more information about this.
|
* in jsgc.cpp for more information about this.
|
||||||
*
|
*
|
||||||
* zeal_ values from 8 to 10 periodically run different types of
|
* zeal_ values from 8 to 10 periodically run different types of
|
||||||
* incremental GC.
|
* incremental GC.
|
||||||
*/
|
*/
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
|
|
|
@ -895,7 +895,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList
|
||||||
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
||||||
// overflowed, we disable the nursery. The next time we allocate, we'll fail
|
// overflowed, we disable the nursery. The next time we allocate, we'll fail
|
||||||
// because gcBytes >= gcMaxBytes.
|
// because gcBytes >= gcMaxBytes.
|
||||||
if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
|
if (rt->gc.usage.gcBytes() >= rt->gc.maxBytesAllocated())
|
||||||
disable();
|
disable();
|
||||||
|
|
||||||
TIME_END(total);
|
TIME_END(total);
|
||||||
|
|
|
@ -27,9 +27,11 @@ JS::Zone::Zone(JSRuntime *rt)
|
||||||
types(this),
|
types(this),
|
||||||
compartments(),
|
compartments(),
|
||||||
gcGrayRoots(),
|
gcGrayRoots(),
|
||||||
|
gcHeapGrowthFactor(3.0),
|
||||||
gcMallocBytes(0),
|
gcMallocBytes(0),
|
||||||
gcMallocGCTriggered(false),
|
gcMallocGCTriggered(false),
|
||||||
usage(&rt->gc.usage),
|
usage(&rt->gc.usage),
|
||||||
|
gcTriggerBytes(0),
|
||||||
data(nullptr),
|
data(nullptr),
|
||||||
isSystem(false),
|
isSystem(false),
|
||||||
usedByExclusiveThread(false),
|
usedByExclusiveThread(false),
|
||||||
|
@ -46,7 +48,6 @@ JS::Zone::Zone(JSRuntime *rt)
|
||||||
JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
|
JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
|
||||||
static_cast<JS::shadow::Zone *>(this));
|
static_cast<JS::shadow::Zone *>(this));
|
||||||
|
|
||||||
threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState);
|
|
||||||
setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
|
setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +62,8 @@ Zone::~Zone()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Zone::init(bool isSystemArg)
|
bool Zone::init()
|
||||||
{
|
{
|
||||||
isSystem = isSystemArg;
|
|
||||||
return gcZoneGroupEdges.init();
|
return gcZoneGroupEdges.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,40 +42,6 @@ class Allocator
|
||||||
JS::Zone *zone_;
|
JS::Zone *zone_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace gc {
|
|
||||||
|
|
||||||
// This class encapsulates the data that determines when we need to do a zone GC.
|
|
||||||
class ZoneHeapThreshold
|
|
||||||
{
|
|
||||||
// The "growth factor" for computing our next thresholds after a GC.
|
|
||||||
double gcHeapGrowthFactor_;
|
|
||||||
|
|
||||||
// GC trigger threshold for allocations on the GC heap.
|
|
||||||
size_t gcTriggerBytes_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ZoneHeapThreshold()
|
|
||||||
: gcHeapGrowthFactor_(3.0),
|
|
||||||
gcTriggerBytes_(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
|
|
||||||
size_t gcTriggerBytes() const { return gcTriggerBytes_; }
|
|
||||||
|
|
||||||
void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
|
|
||||||
const GCSchedulingTunables &tunables, const GCSchedulingState &state);
|
|
||||||
void updateForRemovedArena(const GCSchedulingTunables &tunables);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static double computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
|
|
||||||
const GCSchedulingTunables &tunables,
|
|
||||||
const GCSchedulingState &state);
|
|
||||||
static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
|
|
||||||
JSGCInvocationKind gckind,
|
|
||||||
const GCSchedulingTunables &tunables);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace gc
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -129,7 +95,7 @@ struct Zone : public JS::shadow::Zone,
|
||||||
{
|
{
|
||||||
explicit Zone(JSRuntime *rt);
|
explicit Zone(JSRuntime *rt);
|
||||||
~Zone();
|
~Zone();
|
||||||
bool init(bool isSystem);
|
bool init();
|
||||||
|
|
||||||
void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
|
void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
|
||||||
|
|
||||||
|
@ -139,6 +105,9 @@ struct Zone : public JS::shadow::Zone,
|
||||||
size_t *typePool,
|
size_t *typePool,
|
||||||
size_t *baselineStubsOptimized);
|
size_t *baselineStubsOptimized);
|
||||||
|
|
||||||
|
void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
|
||||||
|
void reduceGCTriggerBytes(size_t amount);
|
||||||
|
|
||||||
void resetGCMallocBytes();
|
void resetGCMallocBytes();
|
||||||
void setGCMaxMallocBytes(size_t value);
|
void setGCMaxMallocBytes(size_t value);
|
||||||
void updateMallocCounter(size_t nbytes) {
|
void updateMallocCounter(size_t nbytes) {
|
||||||
|
@ -257,6 +226,9 @@ struct Zone : public JS::shadow::Zone,
|
||||||
typedef js::HashSet<Zone *, js::DefaultHasher<Zone *>, js::SystemAllocPolicy> ZoneSet;
|
typedef js::HashSet<Zone *, js::DefaultHasher<Zone *>, js::SystemAllocPolicy> ZoneSet;
|
||||||
ZoneSet gcZoneGroupEdges;
|
ZoneSet gcZoneGroupEdges;
|
||||||
|
|
||||||
|
// The "growth factor" for computing our next thresholds after a GC.
|
||||||
|
double gcHeapGrowthFactor;
|
||||||
|
|
||||||
// Malloc counter to measure memory pressure for GC scheduling. It runs from
|
// Malloc counter to measure memory pressure for GC scheduling. It runs from
|
||||||
// gcMaxMallocBytes down to zero. This counter should be used only when it's
|
// gcMaxMallocBytes down to zero. This counter should be used only when it's
|
||||||
// not possible to know the size of a free.
|
// not possible to know the size of a free.
|
||||||
|
@ -275,8 +247,8 @@ struct Zone : public JS::shadow::Zone,
|
||||||
// Track heap usage under this Zone.
|
// Track heap usage under this Zone.
|
||||||
js::gc::HeapUsage usage;
|
js::gc::HeapUsage usage;
|
||||||
|
|
||||||
// Thresholds used to trigger GC.
|
// GC trigger threshold for allocations on the GC heap.
|
||||||
js::gc::ZoneHeapThreshold threshold;
|
size_t gcTriggerBytes;
|
||||||
|
|
||||||
// Per-zone data for use by an embedder.
|
// Per-zone data for use by an embedder.
|
||||||
void *data;
|
void *data;
|
||||||
|
|
309
js/src/jsgc.cpp
309
js/src/jsgc.cpp
|
@ -704,14 +704,15 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
|
||||||
* without emptying the list, the older chunks will stay at the tail
|
* without emptying the list, the older chunks will stay at the tail
|
||||||
* and are more likely to reach the max age.
|
* and are more likely to reach the max age.
|
||||||
*/
|
*/
|
||||||
|
JS_ASSERT(maxEmptyChunkCount >= minEmptyChunkCount);
|
||||||
Chunk *freeList = nullptr;
|
Chunk *freeList = nullptr;
|
||||||
unsigned freeChunkCount = 0;
|
unsigned freeChunkCount = 0;
|
||||||
for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
|
for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
|
||||||
Chunk *chunk = e.front();
|
Chunk *chunk = e.front();
|
||||||
JS_ASSERT(chunk->unused());
|
JS_ASSERT(chunk->unused());
|
||||||
JS_ASSERT(!chunkSet.has(chunk));
|
JS_ASSERT(!chunkSet.has(chunk));
|
||||||
if (releaseAll || freeChunkCount >= tunables.maxEmptyChunkCount() ||
|
if (releaseAll || freeChunkCount >= maxEmptyChunkCount ||
|
||||||
(freeChunkCount >= tunables.minEmptyChunkCount() &&
|
(freeChunkCount >= minEmptyChunkCount &&
|
||||||
(shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
|
(shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
|
||||||
{
|
{
|
||||||
e.removeAndPopFront();
|
e.removeAndPopFront();
|
||||||
|
@ -725,8 +726,8 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
|
||||||
e.popFront();
|
e.popFront();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JS_ASSERT(chunkPool.getEmptyCount() <= tunables.maxEmptyChunkCount());
|
JS_ASSERT(chunkPool.getEmptyCount() <= maxEmptyChunkCount);
|
||||||
JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= tunables.minEmptyChunkCount());
|
JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= minEmptyChunkCount);
|
||||||
JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
|
JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
|
||||||
return freeList;
|
return freeList;
|
||||||
}
|
}
|
||||||
|
@ -928,7 +929,7 @@ Chunk::allocateArena(Zone *zone, AllocKind thingKind)
|
||||||
JS_ASSERT(hasAvailableArenas());
|
JS_ASSERT(hasAvailableArenas());
|
||||||
|
|
||||||
JSRuntime *rt = zone->runtimeFromAnyThread();
|
JSRuntime *rt = zone->runtimeFromAnyThread();
|
||||||
if (!rt->isHeapMinorCollecting() && rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes()) {
|
if (!rt->isHeapMinorCollecting() && rt->gc.usage.gcBytes() >= rt->gc.maxBytesAllocated()) {
|
||||||
#ifdef JSGC_FJGENERATIONAL
|
#ifdef JSGC_FJGENERATIONAL
|
||||||
// This is an approximation to the best test, which would check that
|
// This is an approximation to the best test, which would check that
|
||||||
// this thread is currently promoting into the tenured area. I doubt
|
// this thread is currently promoting into the tenured area. I doubt
|
||||||
|
@ -949,7 +950,7 @@ Chunk::allocateArena(Zone *zone, AllocKind thingKind)
|
||||||
|
|
||||||
zone->usage.addGCArena();
|
zone->usage.addGCArena();
|
||||||
|
|
||||||
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
if (zone->usage.gcBytes() >= zone->gcTriggerBytes) {
|
||||||
AutoUnlockGC unlock(rt);
|
AutoUnlockGC unlock(rt);
|
||||||
TriggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
|
TriggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
|
||||||
}
|
}
|
||||||
|
@ -994,7 +995,7 @@ Chunk::releaseArena(ArenaHeader *aheader)
|
||||||
maybeLock.lock(rt);
|
maybeLock.lock(rt);
|
||||||
|
|
||||||
if (rt->gc.isBackgroundSweeping())
|
if (rt->gc.isBackgroundSweeping())
|
||||||
zone->threshold.updateForRemovedArena(rt->gc.tunables);
|
zone->reduceGCTriggerBytes(zone->gcHeapGrowthFactor * ArenaSize);
|
||||||
zone->usage.removeGCArena();
|
zone->usage.removeGCArena();
|
||||||
|
|
||||||
aheader->setAsNotAllocated();
|
aheader->setAsNotAllocated();
|
||||||
|
@ -1032,7 +1033,7 @@ GCRuntime::wantBackgroundAllocation() const
|
||||||
* of them.
|
* of them.
|
||||||
*/
|
*/
|
||||||
return helperState.canBackgroundAllocate() &&
|
return helperState.canBackgroundAllocate() &&
|
||||||
chunkPool.getEmptyCount() < tunables.minEmptyChunkCount() &&
|
chunkPool.getEmptyCount() < minEmptyChunkCount &&
|
||||||
chunkSet.count() >= 4;
|
chunkSet.count() >= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,6 +1118,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
||||||
usage(nullptr),
|
usage(nullptr),
|
||||||
systemAvailableChunkListHead(nullptr),
|
systemAvailableChunkListHead(nullptr),
|
||||||
userAvailableChunkListHead(nullptr),
|
userAvailableChunkListHead(nullptr),
|
||||||
|
maxBytes(0),
|
||||||
maxMallocBytes(0),
|
maxMallocBytes(0),
|
||||||
numArenasFreeCommitted(0),
|
numArenasFreeCommitted(0),
|
||||||
verifyPreData(nullptr),
|
verifyPreData(nullptr),
|
||||||
|
@ -1125,6 +1127,19 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
|
||||||
nextFullGCTime(0),
|
nextFullGCTime(0),
|
||||||
lastGCTime(0),
|
lastGCTime(0),
|
||||||
jitReleaseTime(0),
|
jitReleaseTime(0),
|
||||||
|
allocThreshold(30 * 1024 * 1024),
|
||||||
|
highFrequencyGC(false),
|
||||||
|
highFrequencyTimeThreshold(1000),
|
||||||
|
highFrequencyLowLimitBytes(100 * 1024 * 1024),
|
||||||
|
highFrequencyHighLimitBytes(500 * 1024 * 1024),
|
||||||
|
highFrequencyHeapGrowthMax(3.0),
|
||||||
|
highFrequencyHeapGrowthMin(1.5),
|
||||||
|
lowFrequencyHeapGrowth(1.5),
|
||||||
|
dynamicHeapGrowth(false),
|
||||||
|
dynamicMarkSlice(false),
|
||||||
|
decommitThreshold(32 * 1024 * 1024),
|
||||||
|
minEmptyChunkCount(1),
|
||||||
|
maxEmptyChunkCount(30),
|
||||||
cleanUpEverything(false),
|
cleanUpEverything(false),
|
||||||
grayBitsValid(false),
|
grayBitsValid(false),
|
||||||
isNeeded(0),
|
isNeeded(0),
|
||||||
|
@ -1283,7 +1298,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||||
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
|
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
|
||||||
* for default backward API compatibility.
|
* for default backward API compatibility.
|
||||||
*/
|
*/
|
||||||
tunables.setParameter(JSGC_MAX_BYTES, maxbytes);
|
maxBytes = maxbytes;
|
||||||
setMaxMallocBytes(maxbytes);
|
setMaxMallocBytes(maxbytes);
|
||||||
|
|
||||||
#ifndef JS_MORE_DETERMINISTIC
|
#ifndef JS_MORE_DETERMINISTIC
|
||||||
|
@ -1391,6 +1406,11 @@ void
|
||||||
GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
|
GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case JSGC_MAX_BYTES: {
|
||||||
|
JS_ASSERT(value >= usage.gcBytes());
|
||||||
|
maxBytes = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case JSGC_MAX_MALLOC_BYTES:
|
case JSGC_MAX_MALLOC_BYTES:
|
||||||
setMaxMallocBytes(value);
|
setMaxMallocBytes(value);
|
||||||
break;
|
break;
|
||||||
|
@ -1400,78 +1420,56 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
|
||||||
case JSGC_MARK_STACK_LIMIT:
|
case JSGC_MARK_STACK_LIMIT:
|
||||||
setMarkStackLimit(value);
|
setMarkStackLimit(value);
|
||||||
break;
|
break;
|
||||||
|
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
|
||||||
|
highFrequencyTimeThreshold = value;
|
||||||
|
break;
|
||||||
|
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
|
||||||
|
highFrequencyLowLimitBytes = value * 1024 * 1024;
|
||||||
|
break;
|
||||||
|
case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
|
||||||
|
highFrequencyHighLimitBytes = value * 1024 * 1024;
|
||||||
|
break;
|
||||||
|
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
|
||||||
|
highFrequencyHeapGrowthMax = value / 100.0;
|
||||||
|
MOZ_ASSERT(highFrequencyHeapGrowthMax / 0.85 > 1.0);
|
||||||
|
break;
|
||||||
|
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
|
||||||
|
highFrequencyHeapGrowthMin = value / 100.0;
|
||||||
|
MOZ_ASSERT(highFrequencyHeapGrowthMin / 0.85 > 1.0);
|
||||||
|
break;
|
||||||
|
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
|
||||||
|
lowFrequencyHeapGrowth = value / 100.0;
|
||||||
|
MOZ_ASSERT(lowFrequencyHeapGrowth / 0.9 > 1.0);
|
||||||
|
break;
|
||||||
|
case JSGC_DYNAMIC_HEAP_GROWTH:
|
||||||
|
dynamicHeapGrowth = value;
|
||||||
|
break;
|
||||||
|
case JSGC_DYNAMIC_MARK_SLICE:
|
||||||
|
dynamicMarkSlice = value;
|
||||||
|
break;
|
||||||
|
case JSGC_ALLOCATION_THRESHOLD:
|
||||||
|
allocThreshold = value * 1024 * 1024;
|
||||||
|
break;
|
||||||
case JSGC_DECOMMIT_THRESHOLD:
|
case JSGC_DECOMMIT_THRESHOLD:
|
||||||
decommitThreshold = value * 1024 * 1024;
|
decommitThreshold = value * 1024 * 1024;
|
||||||
break;
|
break;
|
||||||
case JSGC_MODE:
|
case JSGC_MIN_EMPTY_CHUNK_COUNT:
|
||||||
|
minEmptyChunkCount = value;
|
||||||
|
if (minEmptyChunkCount > maxEmptyChunkCount)
|
||||||
|
maxEmptyChunkCount = minEmptyChunkCount;
|
||||||
|
break;
|
||||||
|
case JSGC_MAX_EMPTY_CHUNK_COUNT:
|
||||||
|
maxEmptyChunkCount = value;
|
||||||
|
if (minEmptyChunkCount > maxEmptyChunkCount)
|
||||||
|
minEmptyChunkCount = maxEmptyChunkCount;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
JS_ASSERT(key == JSGC_MODE);
|
||||||
mode = JSGCMode(value);
|
mode = JSGCMode(value);
|
||||||
JS_ASSERT(mode == JSGC_MODE_GLOBAL ||
|
JS_ASSERT(mode == JSGC_MODE_GLOBAL ||
|
||||||
mode == JSGC_MODE_COMPARTMENT ||
|
mode == JSGC_MODE_COMPARTMENT ||
|
||||||
mode == JSGC_MODE_INCREMENTAL);
|
mode == JSGC_MODE_INCREMENTAL);
|
||||||
break;
|
return;
|
||||||
default:
|
|
||||||
tunables.setParameter(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value)
|
|
||||||
{
|
|
||||||
switch(key) {
|
|
||||||
case JSGC_MAX_BYTES:
|
|
||||||
gcMaxBytes_ = value;
|
|
||||||
break;
|
|
||||||
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
|
|
||||||
highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
|
|
||||||
break;
|
|
||||||
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
|
|
||||||
highFrequencyLowLimitBytes_ = value * 1024 * 1024;
|
|
||||||
if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_)
|
|
||||||
highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1;
|
|
||||||
JS_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
|
|
||||||
break;
|
|
||||||
case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
|
|
||||||
MOZ_ASSERT(value > 0);
|
|
||||||
highFrequencyHighLimitBytes_ = value * 1024 * 1024;
|
|
||||||
if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_)
|
|
||||||
highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1;
|
|
||||||
JS_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
|
|
||||||
break;
|
|
||||||
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
|
|
||||||
highFrequencyHeapGrowthMax_ = value / 100.0;
|
|
||||||
MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
|
|
||||||
break;
|
|
||||||
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
|
|
||||||
highFrequencyHeapGrowthMin_ = value / 100.0;
|
|
||||||
MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
|
|
||||||
break;
|
|
||||||
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
|
|
||||||
lowFrequencyHeapGrowth_ = value / 100.0;
|
|
||||||
MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
|
|
||||||
break;
|
|
||||||
case JSGC_DYNAMIC_HEAP_GROWTH:
|
|
||||||
dynamicHeapGrowthEnabled_ = value;
|
|
||||||
break;
|
|
||||||
case JSGC_DYNAMIC_MARK_SLICE:
|
|
||||||
dynamicMarkSliceEnabled_ = value;
|
|
||||||
break;
|
|
||||||
case JSGC_ALLOCATION_THRESHOLD:
|
|
||||||
gcZoneAllocThresholdBase_ = value * 1024 * 1024;
|
|
||||||
break;
|
|
||||||
case JSGC_MIN_EMPTY_CHUNK_COUNT:
|
|
||||||
minEmptyChunkCount_ = value;
|
|
||||||
if (minEmptyChunkCount_ > maxEmptyChunkCount_)
|
|
||||||
maxEmptyChunkCount_ = minEmptyChunkCount_;
|
|
||||||
JS_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
|
|
||||||
break;
|
|
||||||
case JSGC_MAX_EMPTY_CHUNK_COUNT:
|
|
||||||
maxEmptyChunkCount_ = value;
|
|
||||||
if (minEmptyChunkCount_ > maxEmptyChunkCount_)
|
|
||||||
minEmptyChunkCount_ = maxEmptyChunkCount_;
|
|
||||||
JS_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Unknown GC parameter.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,7 +1478,7 @@ GCRuntime::getParameter(JSGCParamKey key)
|
||||||
{
|
{
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case JSGC_MAX_BYTES:
|
case JSGC_MAX_BYTES:
|
||||||
return uint32_t(tunables.gcMaxBytes());
|
return uint32_t(maxBytes);
|
||||||
case JSGC_MAX_MALLOC_BYTES:
|
case JSGC_MAX_MALLOC_BYTES:
|
||||||
return maxMallocBytes;
|
return maxMallocBytes;
|
||||||
case JSGC_BYTES:
|
case JSGC_BYTES:
|
||||||
|
@ -1496,27 +1494,27 @@ GCRuntime::getParameter(JSGCParamKey key)
|
||||||
case JSGC_MARK_STACK_LIMIT:
|
case JSGC_MARK_STACK_LIMIT:
|
||||||
return marker.maxCapacity();
|
return marker.maxCapacity();
|
||||||
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
|
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
|
||||||
return tunables.highFrequencyThresholdUsec();
|
return highFrequencyTimeThreshold;
|
||||||
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
|
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
|
||||||
return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
|
return highFrequencyLowLimitBytes / 1024 / 1024;
|
||||||
case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
|
case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
|
||||||
return tunables.highFrequencyHighLimitBytes() / 1024 / 1024;
|
return highFrequencyHighLimitBytes / 1024 / 1024;
|
||||||
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
|
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
|
||||||
return uint32_t(tunables.highFrequencyHeapGrowthMax() * 100);
|
return uint32_t(highFrequencyHeapGrowthMax * 100);
|
||||||
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
|
case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
|
||||||
return uint32_t(tunables.highFrequencyHeapGrowthMin() * 100);
|
return uint32_t(highFrequencyHeapGrowthMin * 100);
|
||||||
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
|
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
|
||||||
return uint32_t(tunables.lowFrequencyHeapGrowth() * 100);
|
return uint32_t(lowFrequencyHeapGrowth * 100);
|
||||||
case JSGC_DYNAMIC_HEAP_GROWTH:
|
case JSGC_DYNAMIC_HEAP_GROWTH:
|
||||||
return tunables.isDynamicHeapGrowthEnabled();
|
return dynamicHeapGrowth;
|
||||||
case JSGC_DYNAMIC_MARK_SLICE:
|
case JSGC_DYNAMIC_MARK_SLICE:
|
||||||
return tunables.isDynamicMarkSliceEnabled();
|
return dynamicMarkSlice;
|
||||||
case JSGC_ALLOCATION_THRESHOLD:
|
case JSGC_ALLOCATION_THRESHOLD:
|
||||||
return tunables.gcZoneAllocThresholdBase() / 1024 / 1024;
|
return allocThreshold / 1024 / 1024;
|
||||||
case JSGC_MIN_EMPTY_CHUNK_COUNT:
|
case JSGC_MIN_EMPTY_CHUNK_COUNT:
|
||||||
return tunables.minEmptyChunkCount();
|
return minEmptyChunkCount;
|
||||||
case JSGC_MAX_EMPTY_CHUNK_COUNT:
|
case JSGC_MAX_EMPTY_CHUNK_COUNT:
|
||||||
return tunables.maxEmptyChunkCount();
|
return maxEmptyChunkCount;
|
||||||
default:
|
default:
|
||||||
JS_ASSERT(key == JSGC_NUMBER);
|
JS_ASSERT(key == JSGC_NUMBER);
|
||||||
return uint32_t(number);
|
return uint32_t(number);
|
||||||
|
@ -1723,84 +1721,71 @@ GCRuntime::onTooMuchMalloc()
|
||||||
mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
|
mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ double
|
size_t
|
||||||
ZoneHeapThreshold::computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
|
GCRuntime::computeTriggerBytes(double growthFactor, size_t lastBytes, JSGCInvocationKind gckind)
|
||||||
const GCSchedulingTunables &tunables,
|
|
||||||
const GCSchedulingState &state)
|
|
||||||
{
|
{
|
||||||
if (!tunables.isDynamicHeapGrowthEnabled())
|
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, allocThreshold);
|
||||||
return 3.0;
|
double trigger = double(base) * growthFactor;
|
||||||
|
return size_t(Min(double(maxBytes), trigger));
|
||||||
|
}
|
||||||
|
|
||||||
// For small zones, our collection heuristics do not matter much: favor
|
double
|
||||||
// something simple in this case.
|
GCRuntime::computeHeapGrowthFactor(size_t lastBytes)
|
||||||
if (lastBytes < 1 * 1024 * 1024)
|
{
|
||||||
return tunables.lowFrequencyHeapGrowth();
|
/*
|
||||||
|
* The heap growth factor depends on the heap size after a GC and the GC frequency.
|
||||||
|
* For low frequency GCs (more than 1sec between GCs) we let the heap grow to 150%.
|
||||||
|
* For high frequency GCs we let the heap grow depending on the heap size:
|
||||||
|
* lastBytes < highFrequencyLowLimit: 300%
|
||||||
|
* lastBytes > highFrequencyHighLimit: 150%
|
||||||
|
* otherwise: linear interpolation between 150% and 300% based on lastBytes
|
||||||
|
*/
|
||||||
|
|
||||||
// If GC's are not triggering in rapid succession, use a lower threshold so
|
double factor;
|
||||||
// that we will collect garbage sooner.
|
if (!dynamicHeapGrowth) {
|
||||||
if (!state.inHighFrequencyGCMode())
|
factor = 3.0;
|
||||||
return tunables.lowFrequencyHeapGrowth();
|
} else if (lastBytes < 1 * 1024 * 1024) {
|
||||||
|
factor = lowFrequencyHeapGrowth;
|
||||||
|
} else {
|
||||||
|
JS_ASSERT(highFrequencyHighLimitBytes > highFrequencyLowLimitBytes);
|
||||||
|
if (highFrequencyGC) {
|
||||||
|
if (lastBytes <= highFrequencyLowLimitBytes) {
|
||||||
|
factor = highFrequencyHeapGrowthMax;
|
||||||
|
} else if (lastBytes >= highFrequencyHighLimitBytes) {
|
||||||
|
factor = highFrequencyHeapGrowthMin;
|
||||||
|
} else {
|
||||||
|
double k = (highFrequencyHeapGrowthMin - highFrequencyHeapGrowthMax)
|
||||||
|
/ (double)(highFrequencyHighLimitBytes - highFrequencyLowLimitBytes);
|
||||||
|
factor = (k * (lastBytes - highFrequencyLowLimitBytes)
|
||||||
|
+ highFrequencyHeapGrowthMax);
|
||||||
|
JS_ASSERT(factor <= highFrequencyHeapGrowthMax
|
||||||
|
&& factor >= highFrequencyHeapGrowthMin);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
factor = lowFrequencyHeapGrowth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The heap growth factor depends on the heap size after a GC and the GC
|
|
||||||
// frequency. For low frequency GCs (more than 1sec between GCs) we let
|
|
||||||
// the heap grow to 150%. For high frequency GCs we let the heap grow
|
|
||||||
// depending on the heap size:
|
|
||||||
// lastBytes < highFrequencyLowLimit: 300%
|
|
||||||
// lastBytes > highFrequencyHighLimit: 150%
|
|
||||||
// otherwise: linear interpolation between 300% and 150% based on lastBytes
|
|
||||||
|
|
||||||
// Use shorter names to make the operation comprehensible.
|
|
||||||
double minRatio = tunables.highFrequencyHeapGrowthMin();
|
|
||||||
double maxRatio = tunables.highFrequencyHeapGrowthMax();
|
|
||||||
double lowLimit = tunables.highFrequencyLowLimitBytes();
|
|
||||||
double highLimit = tunables.highFrequencyHighLimitBytes();
|
|
||||||
|
|
||||||
if (lastBytes <= lowLimit)
|
|
||||||
return maxRatio;
|
|
||||||
|
|
||||||
if (lastBytes >= highLimit)
|
|
||||||
return minRatio;
|
|
||||||
|
|
||||||
double factor = maxRatio - ((maxRatio - minRatio) * ((lastBytes - lowLimit) /
|
|
||||||
(highLimit - lowLimit)));
|
|
||||||
JS_ASSERT(factor >= minRatio);
|
|
||||||
JS_ASSERT(factor <= maxRatio);
|
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ size_t
|
void
|
||||||
ZoneHeapThreshold::computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
|
Zone::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
|
||||||
JSGCInvocationKind gckind,
|
|
||||||
const GCSchedulingTunables &tunables)
|
|
||||||
{
|
{
|
||||||
size_t base = gckind == GC_SHRINK
|
GCRuntime &gc = runtimeFromMainThread()->gc;
|
||||||
? lastBytes
|
gcHeapGrowthFactor = gc.computeHeapGrowthFactor(lastBytes);
|
||||||
: Max(lastBytes, tunables.gcZoneAllocThresholdBase());
|
gcTriggerBytes = gc.computeTriggerBytes(gcHeapGrowthFactor, lastBytes, gckind);
|
||||||
double trigger = double(base) * growthFactor;
|
|
||||||
return size_t(Min(double(tunables.gcMaxBytes()), trigger));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ZoneHeapThreshold::updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
|
Zone::reduceGCTriggerBytes(size_t amount)
|
||||||
const GCSchedulingTunables &tunables,
|
|
||||||
const GCSchedulingState &state)
|
|
||||||
{
|
{
|
||||||
gcHeapGrowthFactor_ = computeZoneHeapGrowthFactorForHeapSize(lastBytes, tunables, state);
|
|
||||||
gcTriggerBytes_ = computeZoneTriggerBytes(gcHeapGrowthFactor_, lastBytes, gckind, tunables);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ZoneHeapThreshold::updateForRemovedArena(const GCSchedulingTunables &tunables)
|
|
||||||
{
|
|
||||||
double amount = ArenaSize * gcHeapGrowthFactor_;
|
|
||||||
|
|
||||||
JS_ASSERT(amount > 0);
|
JS_ASSERT(amount > 0);
|
||||||
JS_ASSERT(gcTriggerBytes_ >= amount);
|
JS_ASSERT(gcTriggerBytes >= amount);
|
||||||
|
GCRuntime &gc = runtimeFromAnyThread()->gc;
|
||||||
if (gcTriggerBytes_ - amount < tunables.gcZoneAllocThresholdBase() * gcHeapGrowthFactor_)
|
if (gcTriggerBytes - amount < gc.allocationThreshold() * gcHeapGrowthFactor)
|
||||||
return;
|
return;
|
||||||
|
gcTriggerBytes -= amount;
|
||||||
gcTriggerBytes_ -= amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator::Allocator(Zone *zone)
|
Allocator::Allocator(Zone *zone)
|
||||||
|
@ -2202,7 +2187,7 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
||||||
|
|
||||||
bool runGC = cx->allowGC() && allowGC &&
|
bool runGC = cx->allowGC() && allowGC &&
|
||||||
cx->asJSContext()->runtime()->gc.incrementalState != NO_INCREMENTAL &&
|
cx->asJSContext()->runtime()->gc.incrementalState != NO_INCREMENTAL &&
|
||||||
zone->usage.gcBytes() > zone->threshold.gcTriggerBytes();
|
zone->usage.gcBytes() > zone->gcTriggerBytes;
|
||||||
|
|
||||||
JS_ASSERT_IF(cx->isJSContext() && allowGC,
|
JS_ASSERT_IF(cx->isJSContext() && allowGC,
|
||||||
!cx->asJSContext()->runtime()->currentThreadHasExclusiveAccess());
|
!cx->asJSContext()->runtime()->currentThreadHasExclusiveAccess());
|
||||||
|
@ -2449,9 +2434,9 @@ GCRuntime::maybeGC(Zone *zone)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double factor = schedulingState.inHighFrequencyGCMode() ? 0.85 : 0.9;
|
double factor = highFrequencyGC ? 0.85 : 0.9;
|
||||||
if (zone->usage.gcBytes() > 1024 * 1024 &&
|
if (zone->usage.gcBytes() > 1024 * 1024 &&
|
||||||
zone->usage.gcBytes() >= factor * zone->threshold.gcTriggerBytes() &&
|
zone->usage.gcBytes() >= factor * zone->gcTriggerBytes &&
|
||||||
incrementalState == NO_INCREMENTAL &&
|
incrementalState == NO_INCREMENTAL &&
|
||||||
!isBackgroundSweeping())
|
!isBackgroundSweeping())
|
||||||
{
|
{
|
||||||
|
@ -4528,10 +4513,11 @@ GCRuntime::endSweepPhase(JSGCInvocationKind gckind, bool lastGC)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t currentTime = PRMJ_Now();
|
uint64_t currentTime = PRMJ_Now();
|
||||||
schedulingState.updateHighFrequencyMode(lastGCTime, currentTime, tunables);
|
highFrequencyGC = dynamicHeapGrowth && lastGCTime &&
|
||||||
|
lastGCTime + highFrequencyTimeThreshold * PRMJ_USEC_PER_MSEC > currentTime;
|
||||||
|
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
zone->threshold.updateAfterGC(zone->usage.gcBytes(), gckind, tunables, schedulingState);
|
zone->setGCLastBytes(zone->usage.gcBytes(), gckind);
|
||||||
if (zone->isCollecting()) {
|
if (zone->isCollecting()) {
|
||||||
JS_ASSERT(zone->isGCFinished());
|
JS_ASSERT(zone->isGCFinished());
|
||||||
zone->setGCState(Zone::NoGC);
|
zone->setGCState(Zone::NoGC);
|
||||||
|
@ -4947,7 +4933,7 @@ GCRuntime::budgetIncrementalGC(int64_t *budget)
|
||||||
|
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
if (zone->usage.gcBytes() >= zone->gcTriggerBytes) {
|
||||||
*budget = SliceBudget::Unlimited;
|
*budget = SliceBudget::Unlimited;
|
||||||
stats.nonincremental("allocation trigger");
|
stats.nonincremental("allocation trigger");
|
||||||
}
|
}
|
||||||
|
@ -5228,7 +5214,7 @@ GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64
|
||||||
int64_t budget;
|
int64_t budget;
|
||||||
if (millis)
|
if (millis)
|
||||||
budget = SliceBudget::TimeBudget(millis);
|
budget = SliceBudget::TimeBudget(millis);
|
||||||
else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
|
else if (highFrequencyGC && dynamicMarkSlice)
|
||||||
budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
|
budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
|
||||||
else
|
else
|
||||||
budget = sliceBudget;
|
budget = sliceBudget;
|
||||||
|
@ -5446,10 +5432,13 @@ js::NewCompartment(JSContext *cx, Zone *zone, JSPrincipals *principals,
|
||||||
|
|
||||||
zoneHolder.reset(zone);
|
zoneHolder.reset(zone);
|
||||||
|
|
||||||
const JSPrincipals *trusted = rt->trustedPrincipals();
|
if (!zone->init())
|
||||||
bool isSystem = principals && principals == trusted;
|
|
||||||
if (!zone->init(isSystem))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
zone->setGCLastBytes(8192, GC_NORMAL);
|
||||||
|
|
||||||
|
const JSPrincipals *trusted = rt->trustedPrincipals();
|
||||||
|
zone->isSystem = principals && principals == trusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedJSDeletePtr<JSCompartment> compartment(cx->new_<JSCompartment>(zone, options));
|
ScopedJSDeletePtr<JSCompartment> compartment(cx->new_<JSCompartment>(zone, options));
|
||||||
|
|
|
@ -291,7 +291,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||||
SetMarkStackLimit(this, atoi(size));
|
SetMarkStackLimit(this, atoi(size));
|
||||||
|
|
||||||
ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
|
ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
|
||||||
if (!atomsZone || !atomsZone->init(true))
|
if (!atomsZone || !atomsZone->init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JS::CompartmentOptions options;
|
JS::CompartmentOptions options;
|
||||||
|
@ -303,6 +303,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||||
atomsZone->compartments.append(atomsCompartment.get());
|
atomsZone->compartments.append(atomsCompartment.get());
|
||||||
|
|
||||||
atomsCompartment->isSystem = true;
|
atomsCompartment->isSystem = true;
|
||||||
|
atomsZone->isSystem = true;
|
||||||
|
atomsZone->setGCLastBytes(8192, GC_NORMAL);
|
||||||
|
|
||||||
atomsZone.forget();
|
atomsZone.forget();
|
||||||
this->atomsCompartment_ = atomsCompartment.forget();
|
this->atomsCompartment_ = atomsCompartment.forget();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче