зеркало из https://github.com/mozilla/gecko-dev.git
Bug 808467 - Rewrite the cycle collector's memory reporter. r=mccr8.
--HG-- extra : rebase_source : 9944d59772654a1df8db3227f1a8b18ae3dcfeda
This commit is contained in:
Родитель
9d0bbc706e
Коммит
ab8ff34585
|
@ -6,7 +6,7 @@
|
|||
|
||||
//
|
||||
// This file implements a garbage-cycle collector based on the paper
|
||||
//
|
||||
//
|
||||
// Concurrent Cycle Collection in Reference Counted Systems
|
||||
// Bacon & Rajan (2001), ECOOP 2001 / Springer LNCS vol 2072
|
||||
//
|
||||
|
@ -320,7 +320,6 @@ public:
|
|||
{
|
||||
mSentinelAndBlocks[0].block = nullptr;
|
||||
mSentinelAndBlocks[1].block = nullptr;
|
||||
mNumBlocks = 0;
|
||||
}
|
||||
|
||||
~EdgePool()
|
||||
|
@ -336,9 +335,6 @@ public:
|
|||
while (b) {
|
||||
Block *next = b->Next();
|
||||
delete b;
|
||||
NS_ASSERTION(mNumBlocks > 0,
|
||||
"Expected EdgePool mNumBlocks to be positive.");
|
||||
mNumBlocks--;
|
||||
b = next;
|
||||
}
|
||||
|
||||
|
@ -362,20 +358,17 @@ private:
|
|||
mPointers[BlockSize - 2].block = nullptr; // sentinel
|
||||
mPointers[BlockSize - 1].block = nullptr; // next block pointer
|
||||
}
|
||||
Block*& Next()
|
||||
{ return mPointers[BlockSize - 1].block; }
|
||||
PtrInfoOrBlock* Start()
|
||||
{ return &mPointers[0]; }
|
||||
PtrInfoOrBlock* End()
|
||||
{ return &mPointers[BlockSize - 2]; }
|
||||
Block*& Next() { return mPointers[BlockSize - 1].block; }
|
||||
PtrInfoOrBlock* Start() { return &mPointers[0]; }
|
||||
PtrInfoOrBlock* End() { return &mPointers[BlockSize - 2]; }
|
||||
};
|
||||
|
||||
// Store the null sentinel so that we can have valid iterators
|
||||
// before adding any edges and without adding any blocks.
|
||||
PtrInfoOrBlock mSentinelAndBlocks[2];
|
||||
uint32_t mNumBlocks;
|
||||
|
||||
Block*& Blocks() { return mSentinelAndBlocks[1].block; }
|
||||
Block*& Blocks() { return mSentinelAndBlocks[1].block; }
|
||||
Block* Blocks() const { return mSentinelAndBlocks[1].block; }
|
||||
|
||||
public:
|
||||
class Iterator
|
||||
|
@ -419,8 +412,7 @@ public:
|
|||
Builder(EdgePool &aPool)
|
||||
: mCurrent(&aPool.mSentinelAndBlocks[0]),
|
||||
mBlockEnd(&aPool.mSentinelAndBlocks[0]),
|
||||
mNextBlockPtr(&aPool.Blocks()),
|
||||
mNumBlocks(aPool.mNumBlocks)
|
||||
mNextBlockPtr(&aPool.Blocks())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -438,7 +430,6 @@ public:
|
|||
mCurrent = b->Start();
|
||||
mBlockEnd = b->End();
|
||||
mNextBlockPtr = &b->Next();
|
||||
mNumBlocks++;
|
||||
}
|
||||
(mCurrent++)->ptrInfo = aEdge;
|
||||
}
|
||||
|
@ -446,13 +437,17 @@ public:
|
|||
// mBlockEnd points to space for null sentinel
|
||||
PtrInfoOrBlock *mCurrent, *mBlockEnd;
|
||||
Block **mNextBlockPtr;
|
||||
uint32_t &mNumBlocks;
|
||||
};
|
||||
|
||||
size_t BlocksSize() const {
|
||||
return sizeof(Block) * mNumBlocks;
|
||||
size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
|
||||
size_t n = 0;
|
||||
Block *b = Blocks();
|
||||
while (b) {
|
||||
n += aMallocSizeOf(b);
|
||||
b = b->Next();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
enum NodeColor { black, white, grey };
|
||||
|
@ -537,7 +532,7 @@ private:
|
|||
// We create and destroy Block using NS_Alloc/NS_Free rather
|
||||
// than new and delete to avoid calling its constructor and
|
||||
// destructor.
|
||||
Block() { NS_NOTREACHED("should never be called"); }
|
||||
Block() { NS_NOTREACHED("should never be called"); }
|
||||
~Block() { NS_NOTREACHED("should never be called"); }
|
||||
|
||||
Block* mNext;
|
||||
|
@ -547,8 +542,7 @@ private:
|
|||
public:
|
||||
NodePool()
|
||||
: mBlocks(nullptr),
|
||||
mLast(nullptr),
|
||||
mNumBlocks(0)
|
||||
mLast(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -571,9 +565,6 @@ public:
|
|||
while (b) {
|
||||
Block *n = b->mNext;
|
||||
NS_Free(b);
|
||||
NS_ASSERTION(mNumBlocks > 0,
|
||||
"Expected NodePool mNumBlocks to be positive.");
|
||||
mNumBlocks--;
|
||||
b = n;
|
||||
}
|
||||
|
||||
|
@ -588,8 +579,7 @@ public:
|
|||
Builder(NodePool& aPool)
|
||||
: mNextBlock(&aPool.mBlocks),
|
||||
mNext(aPool.mLast),
|
||||
mBlockEnd(nullptr),
|
||||
mNumBlocks(aPool.mNumBlocks)
|
||||
mBlockEnd(nullptr)
|
||||
{
|
||||
NS_ASSERTION(aPool.mBlocks == nullptr && aPool.mLast == nullptr,
|
||||
"pool not empty");
|
||||
|
@ -605,7 +595,6 @@ public:
|
|||
mBlockEnd = block->mEntries + BlockSize;
|
||||
block->mNext = nullptr;
|
||||
mNextBlock = &block->mNext;
|
||||
mNumBlocks++;
|
||||
}
|
||||
return new (mNext++) PtrInfo(aPointer, aParticipant);
|
||||
}
|
||||
|
@ -613,7 +602,6 @@ public:
|
|||
Block **mNextBlock;
|
||||
PtrInfo *&mNext;
|
||||
PtrInfo *mBlockEnd;
|
||||
uint32_t &mNumBlocks;
|
||||
};
|
||||
|
||||
class Enumerator;
|
||||
|
@ -657,14 +645,21 @@ public:
|
|||
PtrInfo *mNext, *mBlockEnd, *&mLast;
|
||||
};
|
||||
|
||||
size_t BlocksSize() const {
|
||||
return sizeof(Block) * mNumBlocks;
|
||||
size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
|
||||
// We don't measure the things pointed to by mEntries[] because those
|
||||
// pointers are non-owning.
|
||||
size_t n = 0;
|
||||
Block *b = mBlocks;
|
||||
while (b) {
|
||||
n += aMallocSizeOf(b);
|
||||
b = b->mNext;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
private:
|
||||
Block *mBlocks;
|
||||
PtrInfo *mLast;
|
||||
uint32_t mNumBlocks;
|
||||
};
|
||||
|
||||
|
||||
|
@ -688,13 +683,17 @@ struct GCGraph
|
|||
|
||||
GCGraph() : mRootCount(0) {
|
||||
}
|
||||
~GCGraph() {
|
||||
~GCGraph() {
|
||||
}
|
||||
|
||||
size_t BlocksSize() const {
|
||||
return mNodes.BlocksSize() + mEdges.BlocksSize();
|
||||
}
|
||||
void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
|
||||
size_t *aNodesSize, size_t *aEdgesSize) const {
|
||||
*aNodesSize = mNodes.SizeOfExcludingThis(aMallocSizeOf);
|
||||
*aEdgesSize = mEdges.SizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
// These fields are deliberately not measured:
|
||||
// - mWeakMaps entries, because the pointers are non-owning
|
||||
}
|
||||
};
|
||||
|
||||
// XXX Would be nice to have an nsHashSet<KeyType> API that has
|
||||
|
@ -736,17 +735,30 @@ struct nsPurpleBuffer
|
|||
private:
|
||||
struct Block {
|
||||
Block *mNext;
|
||||
// Try to match the size of a jemalloc bucket.
|
||||
nsPurpleBufferEntry mEntries[1360];
|
||||
// Try to match the size of a jemalloc bucket, to minimize slop bytes.
|
||||
// - On 32-bit platforms sizeof(nsPurpleBufferEntry) is 12, so mEntries
|
||||
// is 16,380 bytes, which leaves 4 bytes for mNext.
|
||||
// - On 64-bit platforms sizeof(nsPurpleBufferEntry) is 24, so mEntries
|
||||
// is 32,544 bytes, which leaves 8 bytes for mNext.
|
||||
nsPurpleBufferEntry mEntries[1365];
|
||||
|
||||
Block() : mNext(nullptr) {}
|
||||
Block() : mNext(nullptr) {
|
||||
#ifndef DEBUG_CC
|
||||
// Ensure Block is the right size (see above).
|
||||
MOZ_STATIC_ASSERT(
|
||||
sizeof(Block) == 16384 || // 32-bit
|
||||
sizeof(Block) == 32768, // 64-bit
|
||||
"ill-sized nsPurpleBuffer::Block"
|
||||
);
|
||||
#endif
|
||||
}
|
||||
void StaticAsserts();
|
||||
};
|
||||
public:
|
||||
// This class wraps a linked list of the elements in the purple
|
||||
// buffer.
|
||||
|
||||
nsCycleCollectorParams &mParams;
|
||||
uint32_t mNumBlocksAlloced;
|
||||
uint32_t mCount;
|
||||
Block mFirstBlock;
|
||||
nsPurpleBufferEntry *mFreeList;
|
||||
|
@ -755,10 +767,10 @@ public:
|
|||
PointerSet mNormalObjects; // duplicates our blocks
|
||||
nsCycleCollectorStats &mStats;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
nsPurpleBuffer(nsCycleCollectorParams ¶ms,
|
||||
nsCycleCollectorStats &stats)
|
||||
nsCycleCollectorStats &stats)
|
||||
: mParams(params),
|
||||
mStats(stats)
|
||||
{
|
||||
|
@ -766,7 +778,7 @@ public:
|
|||
mNormalObjects.Init();
|
||||
}
|
||||
#else
|
||||
nsPurpleBuffer(nsCycleCollectorParams ¶ms)
|
||||
nsPurpleBuffer(nsCycleCollectorParams ¶ms)
|
||||
: mParams(params)
|
||||
{
|
||||
InitBlocks();
|
||||
|
@ -780,7 +792,6 @@ public:
|
|||
|
||||
void InitBlocks()
|
||||
{
|
||||
mNumBlocksAlloced = 0;
|
||||
mCount = 0;
|
||||
mFreeList = nullptr;
|
||||
StartBlock(&mFirstBlock);
|
||||
|
@ -805,16 +816,13 @@ public:
|
|||
{
|
||||
if (mCount > 0)
|
||||
UnmarkRemainingPurple(&mFirstBlock);
|
||||
Block *b = mFirstBlock.mNext;
|
||||
Block *b = mFirstBlock.mNext;
|
||||
while (b) {
|
||||
if (mCount > 0)
|
||||
UnmarkRemainingPurple(b);
|
||||
Block *next = b->mNext;
|
||||
delete b;
|
||||
b = next;
|
||||
NS_ASSERTION(mNumBlocksAlloced > 0,
|
||||
"Expected positive mNumBlocksAlloced.");
|
||||
mNumBlocksAlloced--;
|
||||
}
|
||||
mFirstBlock.mNext = nullptr;
|
||||
}
|
||||
|
@ -865,7 +873,6 @@ public:
|
|||
if (!b) {
|
||||
return nullptr;
|
||||
}
|
||||
mNumBlocksAlloced++;
|
||||
StartBlock(b);
|
||||
|
||||
// Add the new block as the second block in the list.
|
||||
|
@ -916,11 +923,28 @@ public:
|
|||
return mCount;
|
||||
}
|
||||
|
||||
size_t BlocksSize() const
|
||||
size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
|
||||
{
|
||||
return sizeof(Block) * mNumBlocksAlloced;
|
||||
}
|
||||
size_t n = 0;
|
||||
|
||||
// Don't measure mFirstBlock because it's within |this|.
|
||||
const Block *block = mFirstBlock.mNext;
|
||||
while (block) {
|
||||
n += aMallocSizeOf(block);
|
||||
block = block->mNext;
|
||||
}
|
||||
|
||||
// These fields are deliberately not measured:
|
||||
// - mParams: because it only contains scalars.
|
||||
// - mFreeList: because it points into the purple buffer, which is
|
||||
// within mFirstBlock and thus within |this|.
|
||||
// - mNormalObjects, mStats: because they're DEBUG_CC-only.
|
||||
//
|
||||
// We also don't measure the things pointed to by mEntries[] because
|
||||
// those pointers are non-owning.
|
||||
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
|
@ -977,8 +1001,6 @@ nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Top level structure for the cycle collector.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1054,6 +1076,13 @@ struct nsCycleCollector
|
|||
mGraph.mRootCount = 0;
|
||||
}
|
||||
|
||||
void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
|
||||
size_t *aObjectSize,
|
||||
size_t *aGraphNodesSize,
|
||||
size_t *aGraphEdgesSize,
|
||||
size_t *aWhiteNodeSize,
|
||||
size_t *aPurpleBufferSize) const;
|
||||
|
||||
#ifdef DEBUG_CC
|
||||
nsCycleCollectorStats mStats;
|
||||
FILE *mPtrLog;
|
||||
|
@ -1098,6 +1127,7 @@ public:
|
|||
|
||||
|
||||
static nsCycleCollector *sCollector = nullptr;
|
||||
static nsIMemoryMultiReporter *sCollectorReporter = nullptr;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2963,30 +2993,103 @@ nsCycleCollector::WasFreed(nsISupports *n)
|
|||
// Memory reporter
|
||||
////////////////////////
|
||||
|
||||
static int64_t
|
||||
GetCycleCollectorSize()
|
||||
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(CycleCollectorMallocSizeOf,
|
||||
"cycle-collector")
|
||||
|
||||
void
|
||||
nsCycleCollector::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
|
||||
size_t *aObjectSize,
|
||||
size_t *aGraphNodesSize,
|
||||
size_t *aGraphEdgesSize,
|
||||
size_t *aWhiteNodeSize,
|
||||
size_t *aPurpleBufferSize) const
|
||||
{
|
||||
if (!sCollector)
|
||||
return 0;
|
||||
int64_t size = sizeof(nsCycleCollector) +
|
||||
sCollector->mPurpleBuf.BlocksSize() +
|
||||
sCollector->mGraph.BlocksSize();
|
||||
if (sCollector->mWhiteNodes)
|
||||
size += sCollector->mWhiteNodes->Capacity() * sizeof(PtrInfo*);
|
||||
return size;
|
||||
*aObjectSize = aMallocSizeOf(this);
|
||||
|
||||
mGraph.SizeOfExcludingThis(aMallocSizeOf, aGraphNodesSize, aGraphEdgesSize);
|
||||
|
||||
// No need to measure what the entries point to; the pointers are
|
||||
// non-owning.
|
||||
*aWhiteNodeSize = mWhiteNodes
|
||||
? mWhiteNodes->SizeOfIncludingThis(aMallocSizeOf)
|
||||
: 0;
|
||||
|
||||
*aPurpleBufferSize = mPurpleBuf.SizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
// These fields are deliberately not measured:
|
||||
// - mResults: because it's tiny and only contains scalars.
|
||||
// - mJSRuntime: because it's non-owning and measured by JS reporters.
|
||||
// - mParams: because it only contains scalars.
|
||||
// - mStats, mPtrLog, mExpectedGarbage: because they're DEBUG_CC-only.
|
||||
}
|
||||
|
||||
NS_MEMORY_REPORTER_IMPLEMENT(CycleCollector,
|
||||
"explicit/cycle-collector",
|
||||
KIND_HEAP,
|
||||
UNITS_BYTES,
|
||||
GetCycleCollectorSize,
|
||||
"Memory used by the cycle collector. This "
|
||||
"includes the cycle collector structure, the "
|
||||
"purple buffer, the graph, and the white nodes. "
|
||||
"The latter two are expected to be empty when the "
|
||||
"cycle collector is idle.")
|
||||
class CycleCollectorMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetName(nsACString &name)
|
||||
{
|
||||
name.AssignLiteral("cycle-collector");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb,
|
||||
nsISupports *aClosure)
|
||||
{
|
||||
if (!sCollector)
|
||||
return NS_OK;
|
||||
|
||||
size_t objectSize, graphNodesSize, graphEdgesSize, whiteNodesSize,
|
||||
purpleBufferSize;
|
||||
sCollector->SizeOfIncludingThis(CycleCollectorMallocSizeOf,
|
||||
&objectSize, &graphNodesSize,
|
||||
&graphEdgesSize, &whiteNodesSize,
|
||||
&purpleBufferSize);
|
||||
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
size_t amount = _amount; /* evaluate |_amount| just once */ \
|
||||
if (amount > 0) { \
|
||||
nsresult rv; \
|
||||
rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
nsIMemoryReporter::KIND_HEAP, \
|
||||
nsIMemoryReporter::UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aClosure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
REPORT("explicit/cycle-collector/collector-object", objectSize,
|
||||
"Memory used for the cycle collector object itself.");
|
||||
|
||||
REPORT("explicit/cycle-collector/graph-nodes", graphNodesSize,
|
||||
"Memory used for the nodes of the cycle collector's graph. "
|
||||
"This should be zero when the collector is idle.");
|
||||
|
||||
REPORT("explicit/cycle-collector/graph-edges", graphEdgesSize,
|
||||
"Memory used for the edges of the cycle collector's graph. "
|
||||
"This should be zero when the collector is idle.");
|
||||
|
||||
REPORT("explicit/cycle-collector/white-nodes", whiteNodesSize,
|
||||
"Memory used for the cycle collector's white nodes array. "
|
||||
"This should be zero when the collector is idle.");
|
||||
|
||||
REPORT("explicit/cycle-collector/purple-buffer", purpleBufferSize,
|
||||
"Memory used for the cycle collector's purple buffer.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetExplicitNonHeap(int64_t *n)
|
||||
{
|
||||
// This reporter does neither "explicit" nor NONHEAP measurements.
|
||||
*n = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(CycleCollectorMultiReporter, nsIMemoryMultiReporter)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Module public API (exported in nsCycleCollector.h)
|
||||
|
@ -3001,15 +3104,20 @@ nsCycleCollector_registerJSRuntime(nsCycleCollectionJSRuntime *rt)
|
|||
sCollector->RegisterJSRuntime(rt);
|
||||
if (regMemReport) {
|
||||
regMemReport = false;
|
||||
NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(CycleCollector));
|
||||
sCollectorReporter = new CycleCollectorMultiReporter;
|
||||
NS_RegisterMemoryMultiReporter(sCollectorReporter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
nsCycleCollector_forgetJSRuntime()
|
||||
{
|
||||
if (sCollector)
|
||||
sCollector->ForgetJSRuntime();
|
||||
if (sCollectorReporter) {
|
||||
NS_UnregisterMemoryMultiReporter(sCollectorReporter);
|
||||
sCollectorReporter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsPurpleBufferEntry*
|
||||
|
|
Загрузка…
Ссылка в новой задаче