diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index aa428eb18581..e0c1c8a197a2 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -181,7 +181,8 @@ struct GCSizes { #define FOR_EACH_SIZE(macro) \ macro(_, _, marker) \ - macro(_, _, nursery) \ + macro(_, _, nurseryCommitted) \ + macro(_, _, nurseryDecommitted) \ macro(_, _, storeBufferVals) \ macro(_, _, storeBufferCells) \ macro(_, _, storeBufferSlots) \ diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index c17603a0600c..7af3aa0506a2 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -70,14 +70,14 @@ js::Nursery::init() JSRuntime *rt = runtime(); rt->gcNurseryStart_ = uintptr_t(heap); + currentStart_ = start(); rt->gcNurseryEnd_ = chunk(LastNurseryChunk).end(); numActiveChunks_ = 1; - setCurrentChunk(0); #ifdef JS_GC_ZEAL JS_POISON(heap, FreshNursery, NurserySize); #endif - for (int i = 0; i < NumNurseryChunks; ++i) - chunk(i).trailer.runtime = rt; + setCurrentChunk(0); + updateDecommittedRegion(); #ifdef PROFILE_NURSERY char *env = getenv("JS_MINORGC_TIME"); @@ -103,6 +103,7 @@ js::Nursery::enable() return; numActiveChunks_ = 1; setCurrentChunk(0); + currentStart_ = position(); #ifdef JS_GC_ZEAL if (runtime()->gcZeal_ == ZealGenerationalGCValue) enterZealMode(); @@ -112,11 +113,12 @@ js::Nursery::enable() void js::Nursery::disable() { + JS_ASSERT(isEmpty()); if (!isEnabled()) return; - JS_ASSERT(isEmpty()); numActiveChunks_ = 0; currentEnd_ = 0; + updateDecommittedRegion(); } bool @@ -168,6 +170,7 @@ js::Nursery::allocate(size_t size) { JS_ASSERT(isEnabled()); JS_ASSERT(!runtime()->isHeapBusy()); + JS_ASSERT(position() >= currentStart_); if (position() + size > currentEnd()) { if (currentChunk_ + 1 == numActiveChunks_) @@ -862,33 +865,35 @@ js::Nursery::sweep(JSRuntime *rt) chunk(i).trailer.runtime = runtime(); if (rt->gcZeal_ == ZealGenerationalGCValue) { - /* Undo any grow or shrink the collection may have done. */ - numActiveChunks_ = NumNurseryChunks; + MOZ_ASSERT(numActiveChunks_ == NumNurseryChunks); /* Only reset the alloc point when we are close to the end. */ if (currentChunk_ + 1 == NumNurseryChunks) setCurrentChunk(0); - - /* Set current start position for isEmpty checks. */ - currentStart_ = position(); - - return; - } + } else #endif + { + setCurrentChunk(0); + } - setCurrentChunk(0); + /* Set current start position for isEmpty checks. */ + currentStart_ = position(); } void js::Nursery::growAllocableSpace() { + MOZ_ASSERT_IF(runtime()->gcZeal_ == ZealGenerationalGCValue, numActiveChunks_ == NumNurseryChunks); numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks); } void js::Nursery::shrinkAllocableSpace() { + if (runtime()->gcZeal_ == ZealGenerationalGCValue) + return; numActiveChunks_ = Max(numActiveChunks_ - 1, 1); + updateDecommittedRegion(); } #endif /* JSGC_GENERATIONAL */ diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 7b6536022298..7b248cb01b67 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -15,6 +15,7 @@ #include "ds/BitArray.h" #include "gc/Heap.h" +#include "gc/Memory.h" #include "js/GCAPI.h" #include "js/HashTable.h" #include "js/HeapAPI.h" @@ -29,6 +30,7 @@ namespace js { class ObjectElements; class HeapSlot; +void SetGCZeal(JSRuntime *, uint8_t, uint32_t); namespace gc { class Cell; @@ -123,27 +125,8 @@ class Nursery /* Forward a slots/elements pointer stored in an Ion frame. */ void forwardBufferPointer(HeapSlot **pSlotsElems); - size_t sizeOfHeap() { return start() ? NurserySize : 0; } - -#ifdef JS_GC_ZEAL - /* - * In debug and zeal builds, these bytes indicate the state of an unused - * segment of nursery-allocated memory. - */ - static const uint8_t FreshNursery = 0x2a; - static const uint8_t SweptNursery = 0x2b; - static const uint8_t AllocatedThing = 0x2c; - void enterZealMode() { - if (isEnabled()) - numActiveChunks_ = NumNurseryChunks; - } - void leaveZealMode() { - if (isEnabled()) { - JS_ASSERT(isEmpty()); - setCurrentChunk(0); - } - } -#endif + size_t sizeOfHeapCommitted() { return numActiveChunks_ * gc::ChunkSize; } + size_t sizeOfHeapDecommitted() { return (NumNurseryChunks - numActiveChunks_) * gc::ChunkSize; } private: /* @@ -156,7 +139,7 @@ class Nursery /* Pointer to the first unallocated byte in the nursery. */ uintptr_t position_; - /* Pointer to the logic start of the Nursery. */ + /* Pointer to the logical start of the Nursery. */ uintptr_t currentStart_; /* Pointer to the last byte of space in the current chunk. */ @@ -211,8 +194,18 @@ class Nursery JS_ASSERT(chunkno < numActiveChunks_); currentChunk_ = chunkno; position_ = chunk(chunkno).start(); - currentStart_ = chunk(0).start(); currentEnd_ = chunk(chunkno).end(); + chunk(chunkno).trailer.runtime = runtime(); + } + + void updateDecommittedRegion() { +#ifndef JS_GC_ZEAL + if (numActiveChunks_ < NumNurseryChunks) { + uintptr_t decommitStart = chunk(numActiveChunks_).start(); + JS_ASSERT(decommitStart == AlignBytes(decommitStart, 1 << 20)); + gc::MarkPagesUnused(runtime(), (void *)decommitStart, heapEnd() - decommitStart); + } +#endif } MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const { @@ -284,11 +277,36 @@ class Nursery static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); +#ifdef JS_GC_ZEAL + /* + * In debug and zeal builds, these bytes indicate the state of an unused + * segment of nursery-allocated memory. + */ + static const uint8_t FreshNursery = 0x2a; + static const uint8_t SweptNursery = 0x2b; + static const uint8_t AllocatedThing = 0x2c; + void enterZealMode() { + if (isEnabled()) + numActiveChunks_ = NumNurseryChunks; + } + void leaveZealMode() { + if (isEnabled()) { + JS_ASSERT(isEmpty()); + setCurrentChunk(0); + currentStart_ = start(); + } + } +#else + void enterZealMode() {} + void leaveZealMode() {} +#endif + friend class gc::MinorCollectionTracer; friend class jit::CodeGenerator; friend class jit::MacroAssembler; friend class jit::ICStubCompiler; friend class jit::BaselineCompiler; + friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t); }; } /* namespace js */ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 4aea953122f0..e22a2f0d9b7a 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -628,7 +628,8 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim rtSizes->gc.marker += gcMarker.sizeOfExcludingThis(mallocSizeOf); #ifdef JSGC_GENERATIONAL - rtSizes->gc.nursery += gcNursery.sizeOfHeap(); + rtSizes->gc.nurseryCommitted += gcNursery.sizeOfHeapCommitted(); + rtSizes->gc.nurseryDecommitted += gcNursery.sizeOfHeapDecommitted(); gcStoreBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); #endif } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 75f2319e66a8..6f57bdef8b8b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2347,9 +2347,9 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, KIND_HEAP, rtStats.runtime.gc.marker, "The GC mark stack and gray roots."); - RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery"), - KIND_NONHEAP, rtStats.runtime.gc.nursery, - "The GC nursery."); + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"), + KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted, + "Memory being used by the GC's nursery."); RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"), KIND_HEAP, rtStats.runtime.gc.storeBufferVals, @@ -2393,6 +2393,11 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, "GC arenas in non-empty chunks that is decommitted, i.e. it takes up " "address space but no physical memory or swap space."); + REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"), + rtStats.runtime.gc.nurseryDecommitted, + "Memory allocated to the GC's nursery this is decommitted, i.e. it takes up " + "address space but no physical memory or swap space."); + REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), rtStats.gcHeapUnusedChunks, "Empty GC chunks which will soon be released unless claimed for new "