зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1341090: Refactor malloc counters in Zone and GCRuntime; r=jonco
MozReview-Commit-ID: K52KzVc4zSV --HG-- extra : rebase_source : d540aef55c42f06fa9be1861c0f48b31d469597c extra : histedit_source : e7cd0f0e433121d438bfc112e8fa708ed897e5dd
This commit is contained in:
Родитель
96cbc67918
Коммит
ec65139cde
|
@ -596,6 +596,56 @@ typedef HashMap<Value*, const char*, DefaultHasher<Value*>, SystemAllocPolicy> R
|
|||
|
||||
using AllocKinds = mozilla::EnumSet<AllocKind>;
|
||||
|
||||
template <typename T>
|
||||
class MemoryCounter
|
||||
{
|
||||
// Bytes counter to measure memory pressure for GC scheduling. It runs
|
||||
// from maxBytes down to zero.
|
||||
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> bytes_;
|
||||
|
||||
// GC trigger threshold for memory allocations.
|
||||
js::ActiveThreadData<size_t> maxBytes_;
|
||||
|
||||
// Whether a GC has been triggered as a result of bytes falling below
|
||||
// zero.
|
||||
//
|
||||
// This should be a bool, but Atomic only supports 32-bit and pointer-sized
|
||||
// types.
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> triggered_;
|
||||
|
||||
public:
|
||||
MemoryCounter()
|
||||
: bytes_(0),
|
||||
maxBytes_(0),
|
||||
triggered_(false)
|
||||
{ }
|
||||
|
||||
void reset() {
|
||||
bytes_ = maxBytes_;
|
||||
triggered_ = false;
|
||||
}
|
||||
|
||||
void setMax(size_t newMax) {
|
||||
// For compatibility treat any value that exceeds PTRDIFF_T_MAX to
|
||||
// mean that value.
|
||||
maxBytes_ = (ptrdiff_t(newMax) >= 0) ? newMax : size_t(-1) >> 1;
|
||||
reset();
|
||||
}
|
||||
|
||||
bool update(T* owner, size_t bytes) {
|
||||
bytes_ -= ptrdiff_t(bytes);
|
||||
if (MOZ_UNLIKELY(isTooMuchMalloc())) {
|
||||
if (!triggered_)
|
||||
triggered_ = owner->triggerGCForTooMuchMalloc();
|
||||
}
|
||||
return triggered_;
|
||||
}
|
||||
|
||||
ptrdiff_t bytes() const { return bytes_; }
|
||||
size_t maxBytes() const { return maxBytes_; }
|
||||
bool isTooMuchMalloc() const { return bytes_ <= 0; }
|
||||
};
|
||||
|
||||
class GCRuntime
|
||||
{
|
||||
public:
|
||||
|
@ -667,8 +717,6 @@ class GCRuntime
|
|||
void setDeterministic(bool enable);
|
||||
#endif
|
||||
|
||||
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
|
||||
|
||||
uint64_t nextCellUniqueId() {
|
||||
MOZ_ASSERT(nextCellUniqueId_ > 0);
|
||||
uint64_t uid = ++nextCellUniqueId_;
|
||||
|
@ -725,12 +773,13 @@ class GCRuntime
|
|||
MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
|
||||
bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); }
|
||||
int32_t getMallocBytes() const { return mallocCounter.bytes(); }
|
||||
size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); }
|
||||
bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); }
|
||||
void resetMallocBytes() { mallocCounter.reset(); }
|
||||
void setMaxMallocBytes(size_t value);
|
||||
int32_t getMallocBytes() const { return mallocBytesUntilGC; }
|
||||
void resetMallocBytes();
|
||||
bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
|
||||
void updateMallocCounter(JS::Zone* zone, size_t nbytes);
|
||||
void onTooMuchMalloc();
|
||||
|
||||
void setGCCallback(JSGCCallback callback, void* data);
|
||||
void callGCCallback(JSGCStatus status) const;
|
||||
|
@ -1031,8 +1080,6 @@ class GCRuntime
|
|||
|
||||
ActiveThreadData<RootedValueMap> rootsHash;
|
||||
|
||||
ActiveThreadData<size_t> maxMallocBytes;
|
||||
|
||||
// An incrementing id used to assign unique ids to cells that require one.
|
||||
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
|
||||
|
||||
|
@ -1248,17 +1295,7 @@ class GCRuntime
|
|||
CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;
|
||||
CallbackVector<JSWeakPointerCompartmentCallback> updateWeakPointerCompartmentCallbacks;
|
||||
|
||||
/*
|
||||
* Malloc counter to measure memory pressure for GC scheduling. It runs
|
||||
* from maxMallocBytes down to zero.
|
||||
*/
|
||||
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytesUntilGC;
|
||||
|
||||
/*
|
||||
* Whether a GC has been triggered as a result of mallocBytesUntilGC
|
||||
* falling below zero.
|
||||
*/
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
|
||||
MemoryCounter<GCRuntime> mallocCounter;
|
||||
|
||||
/*
|
||||
* The trace operations to trace embedding-specific GC roots. One is for
|
||||
|
|
|
@ -39,9 +39,6 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
|
|||
gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
|
||||
gcZoneGroupEdges_(group),
|
||||
typeDescrObjects_(group, this, SystemAllocPolicy()),
|
||||
gcMallocBytes(0),
|
||||
gcMaxMallocBytes(0),
|
||||
gcMallocGCTriggered(false),
|
||||
markedAtoms_(group),
|
||||
usage(&rt->gc.usage),
|
||||
threshold(),
|
||||
|
@ -110,33 +107,6 @@ Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
|
|||
needsIncrementalBarrier_ = needs;
|
||||
}
|
||||
|
||||
void
|
||||
Zone::resetGCMallocBytes()
|
||||
{
|
||||
gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
|
||||
gcMallocGCTriggered = false;
|
||||
}
|
||||
|
||||
void
|
||||
Zone::setGCMaxMallocBytes(size_t value)
|
||||
{
|
||||
/*
|
||||
* For compatibility treat any value that exceeds PTRDIFF_T_MAX to
|
||||
* mean that value.
|
||||
*/
|
||||
gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
|
||||
resetGCMallocBytes();
|
||||
}
|
||||
|
||||
void
|
||||
Zone::onTooMuchMalloc()
|
||||
{
|
||||
if (!gcMallocGCTriggered) {
|
||||
GCRuntime& gc = runtimeFromAnyThread()->gc;
|
||||
gcMallocGCTriggered = gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Zone::beginSweepTypes(FreeOp* fop, bool releaseTypes)
|
||||
{
|
||||
|
|
|
@ -166,16 +166,6 @@ struct Zone : public JS::shadow::Zone,
|
|||
size_t* shapeTables,
|
||||
size_t* atomsMarkBitmaps);
|
||||
|
||||
void resetGCMallocBytes();
|
||||
void setGCMaxMallocBytes(size_t value);
|
||||
void updateMallocCounter(size_t nbytes) {
|
||||
// Note: this code may be run from worker threads. We tolerate any
|
||||
// thread races when updating gcMallocBytes.
|
||||
gcMallocBytes -= ptrdiff_t(nbytes);
|
||||
if (MOZ_UNLIKELY(isTooMuchMalloc()))
|
||||
onTooMuchMalloc();
|
||||
}
|
||||
|
||||
// Iterate over all cells in the zone. See the definition of ZoneCellIter
|
||||
// in jsgcinlines.h for the possible arguments and documentation.
|
||||
template <typename T, typename... Args>
|
||||
|
@ -183,11 +173,8 @@ struct Zone : public JS::shadow::Zone,
|
|||
return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), mozilla::Forward<Args>(args)...);
|
||||
}
|
||||
|
||||
bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
|
||||
void onTooMuchMalloc();
|
||||
|
||||
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
|
||||
void* reallocPtr = nullptr) {
|
||||
void* reallocPtr = nullptr) {
|
||||
if (!js::CurrentThreadCanAccessRuntime(runtime_))
|
||||
return nullptr;
|
||||
return runtimeFromActiveCooperatingThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
|
||||
|
@ -399,25 +386,27 @@ struct Zone : public JS::shadow::Zone,
|
|||
js::SystemAllocPolicy>;
|
||||
private:
|
||||
js::ZoneGroupData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
|
||||
|
||||
// Malloc counter to measure memory pressure for GC scheduling. This
|
||||
// counter should be used only when it's not possible to know the size of
|
||||
// a free.
|
||||
js::gc::MemoryCounter<Zone> gcMallocCounter;
|
||||
|
||||
public:
|
||||
JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
|
||||
|
||||
bool addTypeDescrObject(JSContext* cx, HandleObject obj);
|
||||
|
||||
// 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
|
||||
// not possible to know the size of a free.
|
||||
mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
|
||||
bool triggerGCForTooMuchMalloc() {
|
||||
return runtimeFromAnyThread()->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
|
||||
}
|
||||
|
||||
// GC trigger threshold for allocations on the C heap.
|
||||
js::ActiveThreadData<size_t> gcMaxMallocBytes;
|
||||
|
||||
// Whether a GC has been triggered as a result of gcMallocBytes falling
|
||||
// below zero.
|
||||
//
|
||||
// This should be a bool, but Atomic only supports 32-bit and pointer-sized
|
||||
// types.
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> gcMallocGCTriggered;
|
||||
void resetGCMallocBytes() { gcMallocCounter.reset(); }
|
||||
void setGCMaxMallocBytes(size_t value) { gcMallocCounter.setMax(value); }
|
||||
void updateMallocCounter(size_t nbytes) { gcMallocCounter.update(this, nbytes); }
|
||||
size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
|
||||
size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
|
||||
bool isTooMuchMalloc() const { return gcMallocCounter.isTooMuchMalloc(); }
|
||||
|
||||
private:
|
||||
// Bitmap of atoms marked by this zone.
|
||||
|
|
|
@ -816,7 +816,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
|||
marker(rt),
|
||||
usage(nullptr),
|
||||
mMemProfiler(rt),
|
||||
maxMallocBytes(0),
|
||||
nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
|
||||
numArenasFreeCommitted(0),
|
||||
verifyPreData(nullptr),
|
||||
|
@ -864,8 +863,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
|||
incrementalLimit(0),
|
||||
#endif
|
||||
fullCompartmentChecks(false),
|
||||
mallocBytesUntilGC(0),
|
||||
mallocGCTriggered(false),
|
||||
alwaysPreserveCode(false),
|
||||
#ifdef DEBUG
|
||||
arenasEmptyAtShutdown(true),
|
||||
|
@ -1259,7 +1256,7 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock)
|
|||
case JSGC_MAX_BYTES:
|
||||
return uint32_t(tunables.gcMaxBytes());
|
||||
case JSGC_MAX_MALLOC_BYTES:
|
||||
return maxMallocBytes;
|
||||
return mallocCounter.maxBytes();
|
||||
case JSGC_BYTES:
|
||||
return uint32_t(usage.gcBytes());
|
||||
case JSGC_MODE:
|
||||
|
@ -1539,40 +1536,19 @@ js::RemoveRawValueRoot(JSContext* cx, Value* vp)
|
|||
void
|
||||
GCRuntime::setMaxMallocBytes(size_t value)
|
||||
{
|
||||
/*
|
||||
* For compatibility treat any value that exceeds PTRDIFF_T_MAX to
|
||||
* mean that value.
|
||||
*/
|
||||
maxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
|
||||
resetMallocBytes();
|
||||
mallocCounter.setMax(value);
|
||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
||||
zone->setGCMaxMallocBytes(value);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::resetMallocBytes()
|
||||
{
|
||||
mallocBytesUntilGC = ptrdiff_t(maxMallocBytes);
|
||||
mallocGCTriggered = false;
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::updateMallocCounter(JS::Zone* zone, size_t nbytes)
|
||||
{
|
||||
mallocBytesUntilGC -= ptrdiff_t(nbytes);
|
||||
if (MOZ_UNLIKELY(isTooMuchMalloc()))
|
||||
onTooMuchMalloc();
|
||||
else if (zone)
|
||||
bool triggered = mallocCounter.update(this, nbytes);
|
||||
if (!triggered && zone)
|
||||
zone->updateMallocCounter(nbytes);
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::onTooMuchMalloc()
|
||||
{
|
||||
if (!mallocGCTriggered)
|
||||
mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
|
||||
}
|
||||
|
||||
double
|
||||
ZoneHeapThreshold::allocTrigger(bool highFrequencyGC) const
|
||||
{
|
||||
|
@ -4186,7 +4162,7 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock)
|
|||
/* Save existing mark bits. */
|
||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
||||
ChunkBitmap* bitmap = &chunk->bitmap;
|
||||
ChunkBitmap* entry = js_new<ChunkBitmap>();
|
||||
ChunkBitmap* entry = js_new<ChunkBitmap>();
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
|
@ -7621,7 +7597,8 @@ static bool
|
|||
ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(double(cx->zone()->threshold.allocTrigger(cx->runtime()->gc.schedulingState.inHighFrequencyGCMode())));
|
||||
bool highFrequency = cx->runtime()->gc.schedulingState.inHighFrequencyGCMode();
|
||||
args.rval().setNumber(double(cx->zone()->threshold.allocTrigger(highFrequency)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7629,7 +7606,7 @@ static bool
|
|||
ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(double(cx->zone()->gcMallocBytes));
|
||||
args.rval().setNumber(double(cx->zone()->GCMallocBytes()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7637,7 +7614,7 @@ static bool
|
|||
ZoneMaxMallocGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(double(cx->zone()->gcMaxMallocBytes));
|
||||
args.rval().setNumber(double(cx->zone()->GCMaxMallocBytes()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче