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:
Benjamin Bouvier 2017-02-21 16:09:07 +01:00
Родитель 96cbc67918
Коммит ec65139cde
4 изменённых файлов: 81 добавлений и 108 удалений

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

@ -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;
}