Bug 1395509 - Split out zone memory allocation framework into separate base class r=sfink

This splits out the allocation functions and allocation tracking state into a new base class, ZoneAllocator, which lives in its own header file. We can include this for the common case of allocating malloc memory for GC things without dragging in the full complexity of Zone.h.

Differential Revision: https://phabricator.services.mozilla.com/D33180
This commit is contained in:
Jon Coppeard 2019-05-30 18:52:42 +01:00
Родитель 304f0abb3f
Коммит d1d9ec2280
25 изменённых файлов: 405 добавлений и 335 удалений

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

@ -12,7 +12,7 @@
#include "jsapi.h"
#include "builtin/SelfHostingDefines.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
#include "js/GCVector.h"
#include "js/Id.h"
#include "js/UniquePtr.h"

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

@ -10,6 +10,7 @@
#include "gc/ArenaList.h"
#include "gc/Heap.h"
#include "gc/Zone.h"
void js::gc::SortedArenaListSegment::append(Arena* arena) {
MOZ_ASSERT(arena);

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

@ -8,7 +8,6 @@
#include "builtin/TypedObject.h"
#include "gc/Policy.h"
#include "gc/Zone.h"
#include "js/HashTable.h"
#include "js/Value.h"
#include "vm/BigIntType.h" // JS::BigInt

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

@ -9,7 +9,7 @@
#include "gc/FreeOp.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
namespace js {

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

@ -3414,6 +3414,10 @@ bool GCRuntime::triggerGC(JS::GCReason reason) {
return true;
}
void js::gc::MaybeAllocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc) {
rt->gc.maybeAllocTriggerZoneGC(Zone::from(zoneAlloc));
}
void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, size_t nbytes) {
if (!CurrentThreadCanAccessRuntime(rt)) {
// Zones in use by a helper thread can't be collected.

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

@ -31,6 +31,7 @@ class AutoLockGC;
class AutoLockGCBgAlloc;
class AutoLockHelperThreadState;
class VerifyPreTracer;
class ZoneAllocator;
namespace gc {

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

@ -310,10 +310,13 @@
#include "mozilla/Atomics.h"
#include "mozilla/DebugOnly.h"
#include "gc/GCEnum.h"
#include "js/HashTable.h"
#include "threading/ProtectedData.h"
namespace js {
class AutoLockGC;
class ZoneAllocPolicy;
namespace gc {
@ -661,7 +664,7 @@ class MemoryTracker {
~MemoryTracker();
void fixupAfterMovingGC();
void adopt(MemoryTracker& other, Zone* newZone);
void adopt(MemoryTracker& other);
void trackMemory(Cell* cell, size_t nbytes, MemoryUse use);
void untrackMemory(Cell* cell, size_t nbytes, MemoryUse use);

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

@ -9,6 +9,7 @@
#include "gc/WeakMap.h"
#include "gc/Zone.h"
#include "js/TraceKind.h"
#include "vm/JSContext.h"

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

@ -12,13 +12,9 @@
#include "gc/Barrier.h"
#include "gc/DeletePolicy.h"
#include "gc/Tracer.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
#include "js/HashTable.h"
namespace JS {
class Zone;
} // namespace JS
namespace js {
class GCMarker;

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

@ -17,24 +17,6 @@ inline bool JS::Zone::requireGCTracer() const {
}
#endif
inline void JS::Zone::updateAllGCMallocCountersOnGCStart() {
gcMallocCounter.updateOnGCStart();
jitCodeCounter.updateOnGCStart();
}
inline void JS::Zone::updateAllGCMallocCountersOnGCEnd(
const js::AutoLockGC& lock) {
auto& gc = runtimeFromAnyThread()->gc;
gcMallocCounter.updateOnGCEnd(gc.tunables, lock);
jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
}
inline js::gc::TriggerKind JS::Zone::shouldTriggerGCForTooMuchMalloc() {
auto& gc = runtimeFromAnyThread()->gc;
return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
jitCodeCounter.shouldTriggerGC(gc.tunables));
}
/* static */ inline js::HashNumber JS::Zone::UniqueIdToHash(uint64_t uid) {
return mozilla::HashGeneric(uid);
}

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

@ -31,8 +31,46 @@ using namespace js::gc;
Zone* const Zone::NotOnList = reinterpret_cast<Zone*>(1);
ZoneAllocator::ZoneAllocator(JSRuntime* rt)
: JS::shadow::Zone(rt, &rt->gc.marker), zoneSize(&rt->gc.heapSize) {
AutoLockGC lock(rt);
threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables,
rt->gc.schedulingState, lock);
setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock);
jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock);
}
ZoneAllocator::~ZoneAllocator() {
MOZ_ASSERT_IF(runtimeFromAnyThread()->gc.shutdownCollectedEverything(),
gcMallocBytes == 0);
}
void ZoneAllocator::fixupAfterMovingGC() {
#ifdef DEBUG
gcMallocTracker.fixupAfterMovingGC();
#endif
}
void js::ZoneAllocator::updateAllGCMallocCountersOnGCStart() {
gcMallocCounter.updateOnGCStart();
jitCodeCounter.updateOnGCStart();
}
void js::ZoneAllocator::updateAllGCMallocCountersOnGCEnd(
const js::AutoLockGC& lock) {
auto& gc = runtimeFromAnyThread()->gc;
gcMallocCounter.updateOnGCEnd(gc.tunables, lock);
jitCodeCounter.updateOnGCEnd(gc.tunables, lock);
}
js::gc::TriggerKind js::ZoneAllocator::shouldTriggerGCForTooMuchMalloc() {
auto& gc = runtimeFromAnyThread()->gc;
return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables),
jitCodeCounter.shouldTriggerGC(gc.tunables));
}
JS::Zone::Zone(JSRuntime* rt)
: JS::shadow::Zone(rt, &rt->gc.marker),
: ZoneAllocator(rt),
// Note: don't use |this| before initializing helperThreadUse_!
// ProtectedData checks in CheckZone::check may read this field.
helperThreadUse_(HelperThreadUse::None),
@ -57,9 +95,6 @@ JS::Zone::Zone(JSRuntime* rt)
functionToStringCache_(this),
keepAtomsCount(this, 0),
purgeAtomsDeferred(this, 0),
zoneSize(&rt->gc.heapSize),
threshold(),
gcDelayBytes(0),
tenuredStrings(this, 0),
allocNurseryStrings(this, true),
propertyTree_(this, this),
@ -80,12 +115,6 @@ JS::Zone::Zone(JSRuntime* rt)
/* Ensure that there are no vtables to mess us up here. */
MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
static_cast<JS::shadow::Zone*>(this));
AutoLockGC lock(rt);
threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables,
rt->gc.schedulingState, lock);
setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock);
jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock);
}
Zone::~Zone() {
@ -107,8 +136,6 @@ Zone::~Zone() {
regExps().clear();
}
#endif
MOZ_ASSERT_IF(rt->gc.shutdownCollectedEverything(), gcMallocBytes == 0);
}
bool Zone::init(bool isSystemArg) {
@ -469,9 +496,7 @@ void Zone::clearTables() {
}
void Zone::fixupAfterMovingGC() {
#ifdef DEBUG
gcMallocTracker.fixupAfterMovingGC();
#endif
ZoneAllocator::fixupAfterMovingGC();
fixupInitialShapeTable();
}
@ -557,8 +582,9 @@ void Zone::traceAtomCache(JSTracer* trc) {
}
}
void* Zone::onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
size_t nbytes, void* reallocPtr) {
void* ZoneAllocator::onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr) {
if (!js::CurrentThreadCanAccessRuntime(runtime_)) {
return nullptr;
}
@ -566,24 +592,26 @@ void* Zone::onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
reallocPtr);
}
void Zone::reportAllocationOverflow() const {
void ZoneAllocator::reportAllocationOverflow() const {
js::ReportAllocationOverflow(nullptr);
}
void JS::Zone::maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
TriggerKind trigger) {
void ZoneAllocator::maybeTriggerGCForTooMuchMalloc(
js::gc::MemoryCounter& counter, TriggerKind trigger) {
JSRuntime* rt = runtimeFromAnyThread();
if (!js::CurrentThreadCanAccessRuntime(rt)) {
return;
}
bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting();
auto zone = JS::Zone::from(this);
bool wouldInterruptGC =
rt->gc.isIncrementalGCInProgress() && !zone->isCollecting();
if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables)) {
return;
}
if (!rt->gc.triggerZoneGC(this, JS::GCReason::TOO_MUCH_MALLOC,
if (!rt->gc.triggerZoneGC(zone, JS::GCReason::TOO_MUCH_MALLOC,
counter.bytes(), counter.maxBytes())) {
return;
}
@ -593,7 +621,7 @@ void JS::Zone::maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
#ifdef DEBUG
void MemoryTracker::adopt(MemoryTracker& other, Zone* newZone) {
void MemoryTracker::adopt(MemoryTracker& other) {
LockGuard<Mutex> lock(mutex);
AutoEnterOOMUnsafeRegion oomUnsafe;

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

@ -12,6 +12,7 @@
#include "mozilla/SegmentedVector.h"
#include "gc/FindSCCs.h"
#include "gc/ZoneAllocator.h"
#include "js/GCHashTable.h"
#include "vm/MallocProvider.h"
#include "vm/Runtime.h"
@ -136,15 +137,17 @@ namespace JS {
//
// We always guarantee that a zone has at least one live compartment by refusing
// to delete the last compartment in a live zone.
class Zone : public JS::shadow::Zone,
public js::gc::GraphNodeBase<JS::Zone>,
public js::MallocProvider<JS::Zone> {
class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
public:
explicit Zone(JSRuntime* rt);
~Zone();
MOZ_MUST_USE bool init(bool isSystem);
void destroy(js::FreeOp* fop);
static JS::Zone* from(ZoneAllocator* zoneAlloc) {
return static_cast<Zone*>(zoneAlloc);
}
private:
enum class HelperThreadUse : uint32_t { None, Pending, Active };
mozilla::Atomic<HelperThreadUse, mozilla::SequentiallyConsistent,
@ -222,11 +225,6 @@ class Zone : public JS::shadow::Zone,
std::forward<Args>(args)...);
}
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr);
void reportAllocationOverflow() const;
void beginSweepTypes();
bool hasMarkedRealms();
@ -444,54 +442,6 @@ class Zone : public JS::shadow::Zone,
private:
js::ZoneData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
// Malloc counter to measure memory pressure for GC scheduling. This counter
// is used for allocations where the size of the allocation is not known on
// free. Currently this is used for all internal malloc allocations.
js::gc::MemoryCounter gcMallocCounter;
// Malloc counter used for allocations where size information is
// available. Used for some internal and all tracked external allocations.
mozilla::Atomic<size_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
gcMallocBytes;
#ifdef DEBUG
// In debug builds, malloc allocations can be tracked to make debugging easier
// (possible?) if allocation and free sizes don't balance.
js::gc::MemoryTracker gcMallocTracker;
#endif
// Counter of JIT code executable memory for GC scheduling. Also imprecise,
// since wasm can generate code that outlives a zone.
js::gc::MemoryCounter jitCodeCounter;
void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
JSRuntime* rt = runtimeFromAnyThread();
counter.update(nbytes);
auto trigger = counter.shouldTriggerGC(rt->gc.tunables);
if (MOZ_LIKELY(trigger == js::gc::NoTrigger) ||
trigger <= counter.triggered()) {
return;
}
maybeTriggerGCForTooMuchMalloc(counter, trigger);
}
void maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
js::gc::TriggerKind trigger);
public:
// Check allocation threshold and trigger a zone GC if necessary.
void maybeAllocTriggerZoneGC() {
JSRuntime* rt = runtimeFromAnyThread();
if (totalBytes() >= threshold.gcTriggerBytes() &&
rt->heapState() == JS::HeapState::Idle) {
rt->gc.maybeAllocTriggerZoneGC(this);
}
}
private:
js::MainThreadData<js::UniquePtr<js::RegExpZone>> regExps_;
public:
@ -503,100 +453,6 @@ class Zone : public JS::shadow::Zone,
bool addTypeDescrObject(JSContext* cx, HandleObject obj);
void setGCMaxMallocBytes(size_t value, const js::AutoLockGC& lock) {
gcMallocCounter.setMax(value, lock);
}
void updateMallocCounter(size_t nbytes) {
updateMemoryCounter(gcMallocCounter, nbytes);
}
void adoptMallocBytes(Zone* other) {
gcMallocCounter.adopt(other->gcMallocCounter);
gcMallocBytes += other->gcMallocBytes;
other->gcMallocBytes = 0;
#ifdef DEBUG
gcMallocTracker.adopt(other->gcMallocTracker, this);
#endif
}
size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
void updateJitCodeMallocBytes(size_t nbytes) {
updateMemoryCounter(jitCodeCounter, nbytes);
}
void updateAllGCMallocCountersOnGCStart();
void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock);
js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc();
// Memory accounting APIs for malloc memory owned by GC cells.
void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes += nbytes;
// We don't currently check GC triggers here.
#ifdef DEBUG
gcMallocTracker.trackMemory(cell, nbytes, use);
#endif
}
void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
#ifdef DEBUG
gcMallocTracker.untrackMemory(cell, nbytes, use);
#endif
}
void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
#ifdef DEBUG
gcMallocTracker.swapMemory(a, b, use);
#endif
}
#ifdef DEBUG
void registerPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.registerPolicy(policy);
}
void unregisterPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.unregisterPolicy(policy);
}
#endif
void incPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes += nbytes;
#ifdef DEBUG
gcMallocTracker.incPolicyMemory(policy, nbytes);
#endif
maybeAllocTriggerZoneGC();
}
void decPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
#ifdef DEBUG
gcMallocTracker.decPolicyMemory(policy, nbytes);
#endif
}
size_t totalBytes() const { return zoneSize.gcBytes() + gcMallocBytes; }
void keepAtoms() { keepAtomsCount++; }
void releaseAtoms();
bool hasKeptAtoms() const { return keepAtomsCount; }
@ -647,16 +503,6 @@ class Zone : public JS::shadow::Zone,
return functionToStringCache_.ref();
}
// Track heap size under this Zone.
js::gc::HeapSize zoneSize;
// Thresholds used to trigger GC.
js::gc::ZoneHeapThreshold threshold;
// Amount of data to allocate before triggering a new incremental slice for
// the current GC.
js::MainThreadData<size_t> gcDelayBytes;
js::ZoneData<uint32_t> tenuredStrings;
js::ZoneData<bool> allocNurseryStrings;
@ -777,107 +623,4 @@ class Zone : public JS::shadow::Zone,
} // namespace JS
namespace js {
/*
* Allocation policy that performs precise memory tracking on the zone. This
* should be used for all containers associated with a GC thing or a zone.
*
* Since it doesn't hold a JSContext (those may not live long enough), it can't
* report out-of-memory conditions itself; the caller must check for OOM and
* take the appropriate action.
*
* FIXME bug 647103 - replace these *AllocPolicy names.
*/
class ZoneAllocPolicy : public MallocProvider<ZoneAllocPolicy> {
JS::Zone* zone_;
#ifdef DEBUG
friend class js::gc::MemoryTracker; // Can clear |zone_| on merge.
#endif
public:
MOZ_IMPLICIT ZoneAllocPolicy(JS::Zone* z) : zone_(z) {
#ifdef DEBUG
zone()->registerPolicy(this);
#endif
}
ZoneAllocPolicy(ZoneAllocPolicy& other) : ZoneAllocPolicy(other.zone_) {}
ZoneAllocPolicy(ZoneAllocPolicy&& other) : ZoneAllocPolicy(other.zone_) {}
~ZoneAllocPolicy() {
#ifdef DEBUG
if (zone_) {
zone_->unregisterPolicy(this);
}
#endif
}
// Public methods required to fulfill the AllocPolicy interface.
template <typename T>
void free_(T* p, size_t numElems) {
if (p) {
decMemory(numElems * sizeof(T));
js_free(p);
}
}
MOZ_MUST_USE bool checkSimulatedOOM() const {
return !js::oom::ShouldFailWithOOM();
}
void reportAllocOverflow() const { reportAllocationOverflow(); }
// Internal methods called by the MallocProvider implementation.
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr) {
return zone()->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
}
void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
void updateMallocCounter(size_t nbytes) {
zone()->incPolicyMemory(this, nbytes);
}
private:
Zone* zone() const {
MOZ_ASSERT(zone_);
return zone_;
}
void decMemory(size_t nbytes) { zone_->decPolicyMemory(this, nbytes); }
};
// Convenience functions for memory accounting on the zone.
// Associate malloc memory with a GC thing. This call must be matched by a
// following call to RemoveCellMemory with the same size and use. The total
// amount of malloc memory associated with a zone is used to trigger GC.
inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes, MemoryUse use) {
if (nbytes) {
cell->zone()->addCellMemory(cell, nbytes, use);
}
}
inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
AddCellMemory(&cell->asTenured(), nbytes, use);
}
}
// Remove association between malloc memory and a GC thing. This call must
// follow a call to AddCellMemory with the same size and use.
inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
MemoryUse use) {
if (nbytes) {
cell->zoneFromAnyThread()->removeCellMemory(cell, nbytes, use);
}
}
inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
RemoveCellMemory(&cell->asTenured(), nbytes, use);
}
}
} // namespace js
#endif // gc_Zone_h

304
js/src/gc/ZoneAllocator.h Normal file
Просмотреть файл

@ -0,0 +1,304 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Public header for allocating memory associated with GC things.
*/
#ifndef gc_ZoneAllocator_h
#define gc_ZoneAllocator_h
#include "gc/Scheduling.h"
#include "vm/Runtime.h" // For JSRuntime::gc.
namespace JS {
class Zone;
} // namespace JS
namespace js {
namespace gc {
void MaybeAllocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc);
}
// Base class of JS::Zone that provides malloc memory allocation and accounting.
class ZoneAllocator : public JS::shadow::Zone,
public js::MallocProvider<JS::Zone> {
protected:
explicit ZoneAllocator(JSRuntime* rt);
~ZoneAllocator();
void fixupAfterMovingGC();
public:
static ZoneAllocator* from(JS::Zone* zone) {
// This is a safe downcast, but the compiler hasn't seen the definition yet.
return reinterpret_cast<ZoneAllocator*>(zone);
}
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr);
void reportAllocationOverflow() const;
void setGCMaxMallocBytes(size_t value, const js::AutoLockGC& lock) {
gcMallocCounter.setMax(value, lock);
}
void updateMallocCounter(size_t nbytes) {
updateMemoryCounter(gcMallocCounter, nbytes);
}
void adoptMallocBytes(ZoneAllocator* other) {
gcMallocCounter.adopt(other->gcMallocCounter);
gcMallocBytes += other->gcMallocBytes;
other->gcMallocBytes = 0;
#ifdef DEBUG
gcMallocTracker.adopt(other->gcMallocTracker);
#endif
}
size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
void updateJitCodeMallocBytes(size_t nbytes) {
updateMemoryCounter(jitCodeCounter, nbytes);
}
void updateAllGCMallocCountersOnGCStart();
void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock);
js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc();
// Memory accounting APIs for malloc memory owned by GC cells.
void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes += nbytes;
// We don't currently check GC triggers here.
#ifdef DEBUG
gcMallocTracker.trackMemory(cell, nbytes, use);
#endif
}
void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
#ifdef DEBUG
gcMallocTracker.untrackMemory(cell, nbytes, use);
#endif
}
void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
#ifdef DEBUG
gcMallocTracker.swapMemory(a, b, use);
#endif
}
#ifdef DEBUG
void registerPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.registerPolicy(policy);
}
void unregisterPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.unregisterPolicy(policy);
}
#endif
void incPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
mozilla::DebugOnly<size_t> initialBytes(gcMallocBytes);
MOZ_ASSERT(initialBytes + nbytes > initialBytes);
gcMallocBytes += nbytes;
#ifdef DEBUG
gcMallocTracker.incPolicyMemory(policy, nbytes);
#endif
maybeAllocTriggerZoneGC();
}
void decPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
MOZ_ASSERT(gcMallocBytes >= nbytes);
gcMallocBytes -= nbytes;
#ifdef DEBUG
gcMallocTracker.decPolicyMemory(policy, nbytes);
#endif
}
size_t totalBytes() const { return zoneSize.gcBytes() + gcMallocBytes; }
// Check allocation threshold and trigger a zone GC if necessary.
void maybeAllocTriggerZoneGC() {
JSRuntime* rt = runtimeFromAnyThread();
if (totalBytes() >= threshold.gcTriggerBytes() &&
rt->heapState() == JS::HeapState::Idle) {
gc::MaybeAllocTriggerZoneGC(rt, this);
}
}
private:
void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
JSRuntime* rt = runtimeFromAnyThread();
counter.update(nbytes);
auto trigger = counter.shouldTriggerGC(rt->gc.tunables);
if (MOZ_LIKELY(trigger == js::gc::NoTrigger) ||
trigger <= counter.triggered()) {
return;
}
maybeTriggerGCForTooMuchMalloc(counter, trigger);
}
void maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
js::gc::TriggerKind trigger);
public:
// Track heap size under this Zone.
js::gc::HeapSize zoneSize;
// Thresholds used to trigger GC.
js::gc::ZoneHeapThreshold threshold;
// Amount of data to allocate before triggering a new incremental slice for
// the current GC.
js::MainThreadData<size_t> gcDelayBytes;
private:
// Malloc counter to measure memory pressure for GC scheduling. This counter
// is used for allocations where the size of the allocation is not known on
// free. Currently this is used for all internal malloc allocations.
js::gc::MemoryCounter gcMallocCounter;
// Malloc counter used for allocations where size information is
// available. Used for some internal and all tracked external allocations.
mozilla::Atomic<size_t, mozilla::Relaxed,
mozilla::recordreplay::Behavior::DontPreserve>
gcMallocBytes;
#ifdef DEBUG
// In debug builds, malloc allocations can be tracked to make debugging easier
// (possible?) if allocation and free sizes don't balance.
js::gc::MemoryTracker gcMallocTracker;
#endif
// Counter of JIT code executable memory for GC scheduling. Also imprecise,
// since wasm can generate code that outlives a zone.
js::gc::MemoryCounter jitCodeCounter;
friend class js::gc::GCRuntime;
};
/*
* Allocation policy that performs precise memory tracking on the zone. This
* should be used for all containers associated with a GC thing or a zone.
*
* Since it doesn't hold a JSContext (those may not live long enough), it can't
* report out-of-memory conditions itself; the caller must check for OOM and
* take the appropriate action.
*
* FIXME bug 647103 - replace these *AllocPolicy names.
*/
class ZoneAllocPolicy : public MallocProvider<ZoneAllocPolicy> {
ZoneAllocator* zone_;
#ifdef DEBUG
friend class js::gc::MemoryTracker; // Can clear |zone_| on merge.
#endif
public:
MOZ_IMPLICIT ZoneAllocPolicy(ZoneAllocator* z) : zone_(z) {
#ifdef DEBUG
zone()->registerPolicy(this);
#endif
}
ZoneAllocPolicy(ZoneAllocPolicy& other) : ZoneAllocPolicy(other.zone_) {}
ZoneAllocPolicy(ZoneAllocPolicy&& other) : ZoneAllocPolicy(other.zone_) {}
~ZoneAllocPolicy() {
#ifdef DEBUG
if (zone_) {
zone_->unregisterPolicy(this);
}
#endif
}
// Public methods required to fulfill the AllocPolicy interface.
template <typename T>
void free_(T* p, size_t numElems) {
if (p) {
decMemory(numElems * sizeof(T));
js_free(p);
}
}
MOZ_MUST_USE bool checkSimulatedOOM() const {
return !js::oom::ShouldFailWithOOM();
}
void reportAllocOverflow() const { reportAllocationOverflow(); }
// Internal methods called by the MallocProvider implementation.
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr) {
return zone()->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
}
void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
void updateMallocCounter(size_t nbytes) {
zone()->incPolicyMemory(this, nbytes);
}
private:
ZoneAllocator* zone() const {
MOZ_ASSERT(zone_);
return zone_;
}
void decMemory(size_t nbytes) { zone_->decPolicyMemory(this, nbytes); }
};
// Convenience functions for memory accounting on the zone.
// Associate malloc memory with a GC thing. This call must be matched by a
// following call to RemoveCellMemory with the same size and use. The total
// amount of malloc memory associated with a zone is used to trigger GC.
inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes, MemoryUse use) {
if (nbytes) {
ZoneAllocator::from(cell->zone())->addCellMemory(cell, nbytes, use);
}
}
inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
AddCellMemory(&cell->asTenured(), nbytes, use);
}
}
// Remove association between malloc memory and a GC thing. This call must
// follow a call to AddCellMemory with the same size and use.
inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
MemoryUse use) {
if (nbytes) {
auto zoneBase = ZoneAllocator::from(cell->zoneFromAnyThread());
zoneBase->removeCellMemory(cell, nbytes, use);
}
}
inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
RemoveCellMemory(&cell->asTenured(), nbytes, use);
}
}
} // namespace js
#endif // gc_ZoneAllocator_h

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

@ -27,6 +27,7 @@
#include "jit/ExecutableAllocator.h"
#include "gc/Zone.h"
#include "jit/JitRealm.h"
#include "js/MemoryMetrics.h"

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

@ -14,6 +14,7 @@
#include "jit/JitOptions.h"
#include "vm/JSContext.h"
#include "vm/Realm.h"
#include "vm/TypeInference.h"
namespace js {
namespace jit {

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

@ -12,6 +12,8 @@
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"
#include "gc/Zone.h"
#if defined(JS_CODEGEN_X86)
# include "jit/x86/MacroAssembler-x86-inl.h"
#elif defined(JS_CODEGEN_X64)

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

@ -7,6 +7,7 @@
#include "threading/ProtectedData.h"
#include "gc/Heap.h"
#include "gc/Zone.h"
#include "vm/HelperThreads.h"
#include "vm/JSContext.h"

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

@ -553,7 +553,7 @@ bool StartOffThreadIonFree(jit::IonBuilder* builder,
struct AllCompilations {};
struct ZonesInState {
JSRuntime* runtime;
JS::Zone::GCState state;
JS::shadow::Zone::GCState state;
};
struct CompilationsUsingNursery {
JSRuntime* runtime;
@ -582,7 +582,7 @@ inline void CancelOffThreadIonCompile(Zone* zone) {
}
inline void CancelOffThreadIonCompile(JSRuntime* runtime,
JS::Zone::GCState state) {
JS::shadow::Zone::GCState state) {
CancelOffThreadIonCompile(CompilationSelector(ZonesInState{runtime, state}),
true);
}

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

@ -10,6 +10,7 @@
#include "vm/JSContext.h"
#include "builtin/Object.h"
#include "gc/Zone.h"
#include "jit/JitFrames.h"
#include "proxy/Proxy.h"
#include "vm/BigIntType.h"

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

@ -9,6 +9,8 @@
#include "vm/ObjectGroup.h"
#include "gc/Zone.h"
namespace js {
inline bool ObjectGroup::needsSweep() {

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

@ -927,24 +927,6 @@ class ErrorCopier {
~ErrorCopier();
};
class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
JS::Zone* zone;
bool saved;
public:
explicit AutoSuppressAllocationMetadataBuilder(JSContext* cx)
: AutoSuppressAllocationMetadataBuilder(cx->realm()->zone()) {}
explicit AutoSuppressAllocationMetadataBuilder(JS::Zone* zone)
: zone(zone), saved(zone->suppressAllocationMetadataBuilder) {
zone->suppressAllocationMetadataBuilder = true;
}
~AutoSuppressAllocationMetadataBuilder() {
zone->suppressAllocationMetadataBuilder = saved;
}
};
} /* namespace js */
#endif /* vm_Realm_h */

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

@ -19,7 +19,7 @@
#include "gc/Barrier.h"
#include "gc/Heap.h"
#include "gc/Marking.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
#include "js/AllocPolicy.h"
#include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
#include "js/UbiNode.h"

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

@ -367,6 +367,24 @@ inline bool ObjectGroup::hasUnanalyzedPreliminaryObjects() {
maybePreliminaryObjectsDontCheckGeneration();
}
class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
JS::Zone* zone;
bool saved;
public:
explicit AutoSuppressAllocationMetadataBuilder(JSContext* cx)
: AutoSuppressAllocationMetadataBuilder(cx->realm()->zone()) {}
explicit AutoSuppressAllocationMetadataBuilder(JS::Zone* zone)
: zone(zone), saved(zone->suppressAllocationMetadataBuilder) {
zone->suppressAllocationMetadataBuilder = true;
}
~AutoSuppressAllocationMetadataBuilder() {
zone->suppressAllocationMetadataBuilder = saved;
}
};
/*
* Structure for type inference entry point functions. All functions which can
* change type information must use this, and functions which depend on

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

@ -20,7 +20,6 @@
#include "jsnum.h"
#include "builtin/Array.h"
#include "gc/Zone.h"
#include "jit/AtomicOperations.h"
#include "js/Conversions.h"
#include "js/Value.h"

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

@ -21,6 +21,7 @@
#include "builtin/TypedObject.h"
#include "gc/Barrier.h"
#include "gc/Zone.h"
#include "vm/SharedMem.h"
#include "wasm/WasmCode.h"
#include "wasm/WasmDebug.h"