From 0e9cbbecc94b46cc164198788143e51953103f23 Mon Sep 17 00:00:00 2001 From: "sfraser%netscape.com" Date: Fri, 22 Jan 1999 00:20:06 +0000 Subject: [PATCH] Various optimizations in the memory allocators as fix for bug 2254. Also now zap the contents of allocated and freed blocks. --- .../src/nsAllocatorManager.cp | 57 ++++++++++++---- .../src/nsAllocatorManager.h | 5 +- .../src/nsFixedSizeAllocator.cp | 32 ++++++--- .../src/nsFixedSizeAllocator.h | 14 ++-- .../src/nsLargeHeapAllocator.cp | 10 ++- .../src/nsLargeHeapAllocator.h | 5 +- .../MacMemoryAllocator/src/nsMemAllocator.cp | 38 ++++++++++- .../MacMemoryAllocator/src/nsMemAllocator.h | 15 +++- .../src/nsSmallHeapAllocator.cp | 68 ++++++++++++++----- .../src/nsSmallHeapAllocator.h | 27 ++++++-- 10 files changed, 209 insertions(+), 62 deletions(-) diff --git a/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.cp b/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.cp index 515c9192ef03..3decabe174dc 100644 --- a/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.cp +++ b/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.cp @@ -17,6 +17,7 @@ #include #include +#include #include // for placement new @@ -162,6 +163,7 @@ void nsHeapZoneHeader::DisposeZonePtr(Ptr thePtr, Boolean &outWasLastChunk) const SInt32 nsAllocatorManager::kNumMasterPointerBlocks = 30; const SInt32 nsAllocatorManager::kApplicationStackSizeIncrease = (32 * 1024); const SInt32 nsAllocatorManager::kHeapZoneHeapPercentage = 60; +const SInt32 nsAllocatorManager::kSmallHeapByteRange = 16; nsAllocatorManager* nsAllocatorManager::sAllocatorManager = nil; @@ -174,11 +176,11 @@ nsAllocatorManager::nsAllocatorManager() , mLastHeapZone(nil) //-------------------------------------------------------------------- { - mMinSmallBlockSize = 44; // some magic numbers for now - mMinLargeBlockSize = 256; + mMinSmallBlockSize = 129; //128; //44; // some magic numbers for now + mMinLargeBlockSize = 256; //512; //256; mNumFixedSizeAllocators = mMinSmallBlockSize / 4; - mNumSmallBlockAllocators = 1 + (mMinLargeBlockSize - mMinSmallBlockSize) / 4; + mNumSmallBlockAllocators = 1 + (mMinLargeBlockSize - mMinSmallBlockSize) / kSmallHeapByteRange; //can't use new yet! mFixedSizeAllocators = (nsMemAllocator **)NewPtrClear(mNumFixedSizeAllocators * sizeof(nsMemAllocator*)); @@ -194,7 +196,7 @@ nsAllocatorManager::nsAllocatorManager() mFixedSizeAllocators[i] = (nsMemAllocator *)NewPtr(sizeof(nsFixedSizeAllocator)); if (mFixedSizeAllocators[i] == nil) throw((OSErr)memFullErr); - new (mFixedSizeAllocators[i]) nsFixedSizeAllocator((i + 1) * 4); + new (mFixedSizeAllocators[i]) nsFixedSizeAllocator(i * 4 + (i > 0) * 1, (i + 1) * 4); } for (SInt32 i = 0; i < mNumSmallBlockAllocators; i ++) @@ -202,14 +204,15 @@ nsAllocatorManager::nsAllocatorManager() mSmallBlockAllocators[i] = (nsMemAllocator *)NewPtr(sizeof(nsSmallHeapAllocator)); if (mSmallBlockAllocators[i] == nil) throw((OSErr)memFullErr); - new (mSmallBlockAllocators[i]) nsSmallHeapAllocator(); + SInt32 minBytes = mMinSmallBlockSize + i * kSmallHeapByteRange; // lower bound of block size + new (mSmallBlockAllocators[i]) nsSmallHeapAllocator(minBytes, minBytes + kSmallHeapByteRange - 1); } mLargeAllocator = (nsMemAllocator *)NewPtr(sizeof(nsLargeHeapAllocator)); if (mLargeAllocator == nil) throw((OSErr)memFullErr); - new (mLargeAllocator) nsLargeHeapAllocator(); - + new (mLargeAllocator) nsLargeHeapAllocator(mMinLargeBlockSize, 0x7FFFFFFF); + // make the heap zone for our subheaps UInt32 heapZoneSize; @@ -258,7 +261,8 @@ nsMemAllocator* nsAllocatorManager::GetAllocatorForBlockSize(size_t blockSize) return mFixedSizeAllocators[ (blockSize == 0) ? 0 : ((blockSize + 3) >> 2) - 1 ]; if (blockSize < mMinLargeBlockSize) - return mSmallBlockAllocators[ ((blockSize + 3) >> 2) - mNumFixedSizeAllocators ]; + return mSmallBlockAllocators[ ((blockSize - mMinSmallBlockSize + kSmallHeapByteRange) / kSmallHeapByteRange) - 1 ]; + //return mSmallBlockAllocators[ ((blockSize + (kSmallHeapByteRange - 1)) / kSmallHeapByteRange) - mNumFixedSizeAllocators ]; return mLargeAllocator; } @@ -304,7 +308,8 @@ nsHeapZoneHeader* nsAllocatorManager::MakeNewHeapZone(Size zoneSize, Size minZon // block size multiple. All blocks should be multiples of this size, // to reduce heap fragmentation const Size nsAllocatorManager::kChunkSizeMultiple = 2 * 1024; -const Size nsAllocatorManager::kMacMemoryPtrOvehead = 16; // this overhead is documented in IM:Memory 2-22 +const Size nsAllocatorManager::kMaxChunkSize = 48 * 1024; +const Size nsAllocatorManager::kMacMemoryPtrOvehead = 16; // this overhead is documented in IM:Memory 2-22 const Size nsAllocatorManager::kTempMemHeapZoneSize = 1024 * 1024; // 1MB temp handles const Size nsAllocatorManager::kTempMemHeapMinZoneSize = 256 * 1024; // min 256K handle @@ -318,7 +323,7 @@ Ptr nsAllocatorManager::AllocateSubheap(Size preferredSize, Size &outActualSize) // calculate an ideal chunk size by rounding up preferredSize = kChunkSizeMultiple * ((preferredSize + (kChunkSizeMultiple - 1)) / kChunkSizeMultiple); - // take into accound the memory manager's pointer overhead (16 btyes), to avoid fragmentation + // take into account the memory manager's pointer overhead (16 btyes), to avoid fragmentation preferredSize += ((preferredSize / kChunkSizeMultiple) - 1) * kMacMemoryPtrOvehead; outActualSize = preferredSize; @@ -332,7 +337,10 @@ Ptr nsAllocatorManager::AllocateSubheap(Size preferredSize, Size &outActualSize) } // we failed to allocate. Let's make a new heap zone - thisHeapZone = MakeNewHeapZone(kTempMemHeapZoneSize, kTempMemHeapMinZoneSize); + UInt32 prefZoneSize = preferredSize + sizeof(nsHeapZoneHeader) + 512; // for zone overhead + UInt32 zoneSize = (kTempMemHeapZoneSize > prefZoneSize) ? kTempMemHeapZoneSize : prefZoneSize; + UInt32 minZoneSize = (kTempMemHeapMinZoneSize > prefZoneSize) ? kTempMemHeapMinZoneSize : prefZoneSize; + thisHeapZone = MakeNewHeapZone(zoneSize, minZoneSize); if (thisHeapZone) return thisHeapZone->AllocateZonePtr(preferredSize); @@ -450,7 +458,6 @@ void nsAllocatorManager::DumpMemoryStats() //-------------------------------------------------------------------- { UInt32 i; - char outString[ 1024 ]; PRFileDesc *outFile; // Enter a valid, UNIX-style full path on your system to get this @@ -461,6 +468,31 @@ void nsAllocatorManager::DumpMemoryStats() return; } + WriteString(outFile, "\n\n--------------------------------------------------------------------------------\n"); + WriteString(outFile, "Max heap usage chart (* = 1024 bytes)\n"); + WriteString(outFile, "--------------------------------------------------------------------------------\n\n"); + + UInt32 totHeapUsed = 0; + + for (i = 0; i < mNumFixedSizeAllocators; i ++) + { + mFixedSizeAllocators[i]->DumpHeapUsage(outFile); + totHeapUsed += mFixedSizeAllocators[i]->GetMaxHeapUsage(); + } + + for (i = 0; i < mNumSmallBlockAllocators; i ++) + { + mSmallBlockAllocators[i]->DumpHeapUsage(outFile); + totHeapUsed += mSmallBlockAllocators[i]->GetMaxHeapUsage(); + } + + char outString[256]; + sprintf(outString, "Total heap space used by allocators: %ldk\n", totHeapUsed / 1024); + + WriteString(outFile, "--------------------------------------------------------------------------------\n"); + WriteString(outFile, outString); + WriteString(outFile, "--------------------------------------------------------------------------------\n\n"); + for (i = 0; i < mNumFixedSizeAllocators; i ++) { mFixedSizeAllocators[i]->DumpMemoryStats(outFile); @@ -492,7 +524,6 @@ void WriteString(PRFileDesc *file, const char * string) #endif - #pragma mark - diff --git a/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.h b/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.h index 191d4493bb5e..f90129823b66 100644 --- a/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.h +++ b/lib/mac/MacMemoryAllocator/src/nsAllocatorManager.h @@ -103,7 +103,10 @@ class nsAllocatorManager static const SInt32 kTempMemHeapMinZoneSize; static const Size kChunkSizeMultiple; - + static const Size kMaxChunkSize; + + static const SInt32 kSmallHeapByteRange; + static nsAllocatorManager* GetAllocatorManager() { return sAllocatorManager ? sAllocatorManager : CreateAllocatorManager(); } nsAllocatorManager(); diff --git a/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.cp b/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.cp index cf8de98a0301..9707ecfcc39d 100644 --- a/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.cp +++ b/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.cp @@ -26,9 +26,8 @@ const UInt32 FixedMemoryBlock::kFixedSizeBlockOverhead = sizeof(FixedMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE; //-------------------------------------------------------------------- -nsFixedSizeAllocator::nsFixedSizeAllocator(size_t blockSize) -: nsMemAllocator() -, mBlockSize(blockSize) +nsFixedSizeAllocator::nsFixedSizeAllocator(size_t minBlockSize, size_t maxBlockSize) +: nsMemAllocator(minBlockSize, maxBlockSize) , mChunkWithSpace(nil) //-------------------------------------------------------------------- { @@ -46,11 +45,11 @@ nsFixedSizeAllocator::~nsFixedSizeAllocator() nsHeapChunk* nsFixedSizeAllocator::FindChunkWithSpace(size_t blockSize) const //-------------------------------------------------------------------- { - nsFixedSizeHeapChunk* chunk = (nsFixedSizeHeapChunk *)mFirstChunk; - if (mChunkWithSpace && mChunkWithSpace->GetFreeList()) return mChunkWithSpace; + nsFixedSizeHeapChunk* chunk = (nsFixedSizeHeapChunk *)mFirstChunk; + // Try to find an existing chunk with a free block. while (chunk != nil) { @@ -84,9 +83,11 @@ void *nsFixedSizeAllocator::AllocatorMakeBlock(size_t blockSize) blockHeader->SetHeaderTag(kUsedBlockHeaderTag); blockHeader->SetTrailerTag(GetAllocatorBlockSize(), kUsedBlockTrailerTag); + blockHeader->ZapBlockContents(blockSize, kUsedMemoryFillPattern); + UInt32 paddedSize = (blockSize + 3) & ~3; blockHeader->SetPaddingBytes(paddedSize - blockSize); - blockHeader->FillPaddingBytes(mBlockSize); + blockHeader->FillPaddingBytes(mMaxBlockSize); #endif #if STATS_MAC_MEMORY @@ -107,7 +108,9 @@ void nsFixedSizeAllocator::AllocatorFreeBlock(void *freeBlock) #if DEBUG_HEAP_INTEGRITY MEM_ASSERT(blockHeader->HasHeaderTag(kUsedBlockHeaderTag), "Bad block header"); MEM_ASSERT(blockHeader->GetTrailerTag(GetAllocatorBlockSize()) == kUsedBlockTrailerTag, "Bad block trailer"); - MEM_ASSERT(blockHeader->CheckPaddingBytes(mBlockSize), "Block bounds have been overwritten"); + MEM_ASSERT(blockHeader->CheckPaddingBytes(GetAllocatorBlockSize()), "Block bounds have been overwritten"); + + blockHeader->ZapBlockContents(GetAllocatorBlockSize(), kFreeMemoryFillPattern); #endif #if STATS_MAC_MEMORY @@ -128,6 +131,7 @@ void nsFixedSizeAllocator::AllocatorFreeBlock(void *freeBlock) { if (chunk == mChunkWithSpace) mChunkWithSpace = nil; + FreeChunk(chunk); } else @@ -142,7 +146,7 @@ void *nsFixedSizeAllocator::AllocatorResizeBlock(void *block, size_t newSize) //-------------------------------------------------------------------- { // let blocks shrink to at most 16 bytes below this allocator's block size - if (newSize > mBlockSize || newSize <= mBlockSize - kMaxBlockResizeSlop) + if (newSize > mMaxBlockSize || newSize <= mMaxBlockSize - kMaxBlockResizeSlop) return nil; FixedMemoryBlock* blockHeader = FixedMemoryBlock::GetBlockHeader(block); @@ -150,14 +154,14 @@ void *nsFixedSizeAllocator::AllocatorResizeBlock(void *block, size_t newSize) #if DEBUG_HEAP_INTEGRITY MEM_ASSERT(blockHeader->HasHeaderTag(kUsedBlockHeaderTag), "Bad block header"); MEM_ASSERT(blockHeader->GetTrailerTag(GetAllocatorBlockSize()) == kUsedBlockTrailerTag, "Bad block trailer"); - MEM_ASSERT(blockHeader->CheckPaddingBytes(mBlockSize), "Block bounds have been overwritten"); + MEM_ASSERT(blockHeader->CheckPaddingBytes(mMaxBlockSize), "Block bounds have been overwritten"); // if we shrunk the block to below this allocator's normal size range, then these // padding bytes won't be any use. But they are tested using mBlockSize, so we // have to udpate them anyway. UInt32 paddedSize = (newSize + 3) & ~3; blockHeader->SetPaddingBytes(paddedSize - newSize); - blockHeader->FillPaddingBytes(mBlockSize); + blockHeader->FillPaddingBytes(mMaxBlockSize); #endif #if STATS_MAC_MEMORY @@ -173,7 +177,7 @@ void *nsFixedSizeAllocator::AllocatorResizeBlock(void *block, size_t newSize) size_t nsFixedSizeAllocator::AllocatorGetBlockSize(void *thisBlock) //-------------------------------------------------------------------- { - return mBlockSize; + return mMaxBlockSize; } @@ -183,7 +187,13 @@ nsHeapChunk *nsFixedSizeAllocator::AllocateChunk(size_t requestedBlockSize) //-------------------------------------------------------------------- { Size actualChunkSize; + + // adapt the chunk size if we have already allocated a number of chunks, and it's not above a max size + if (mNumChunks > 4 && mBaseChunkSize < nsAllocatorManager::kMaxChunkSize) + mBaseChunkSize *= 2; + Ptr chunkMemory = nsAllocatorManager::GetAllocatorManager()->AllocateSubheap(mBaseChunkSize, actualChunkSize); + if (!chunkMemory) return nil; // use placement new to initialize the chunk in the memory block nsHeapChunk *newHeapChunk = new (chunkMemory) nsFixedSizeHeapChunk(this, actualChunkSize); diff --git a/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.h b/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.h index bec487ea3f4e..3e61cc4678d4 100644 --- a/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.h +++ b/lib/mac/MacMemoryAllocator/src/nsFixedSizeAllocator.h @@ -15,6 +15,7 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ +#include class nsMemAllocator; class nsFixedSizeHeapChunk; @@ -67,6 +68,12 @@ struct FixedMemoryBlock } UInt32 GetPaddingBytes() { return blockHeader.blockPadding; } + void ZapBlockContents(UInt32 blockSize, UInt8 pattern) + { + memset(&memory, pattern, blockSize); + } + + // inline, so won't crash if this is a bad block Boolean HasHeaderTag(MemoryBlockTag inHeaderTag) { return blockHeader.header.headerTag == inHeaderTag; } @@ -106,7 +113,7 @@ class nsFixedSizeAllocator : public nsMemAllocator public: - nsFixedSizeAllocator(size_t blockSize); + nsFixedSizeAllocator(size_t minBlockSize, size_t maxBlockSize); ~nsFixedSizeAllocator(); virtual void * AllocatorMakeBlock(size_t blockSize); @@ -119,7 +126,7 @@ class nsFixedSizeAllocator : public nsMemAllocator virtual nsHeapChunk* FindChunkWithSpace(size_t blockSize) const; - UInt32 GetAllocatorBlockSize() { return mBlockSize; } + UInt32 GetAllocatorBlockSize() { return mMaxBlockSize; } protected: @@ -127,9 +134,6 @@ class nsFixedSizeAllocator : public nsMemAllocator kMaxBlockResizeSlop = 16 }; - UInt32 mBlockSize; // upper bound for blocks allocated in this heap - // does not include block overhead - nsFixedSizeHeapChunk *mChunkWithSpace; // cheap optimization diff --git a/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.cp b/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.cp index 1b5530eca214..768f09d32482 100644 --- a/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.cp +++ b/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.cp @@ -26,8 +26,8 @@ const UInt32 LargeBlockHeader::kLargeBlockOverhead = sizeof(LargeBlockHeader) + MEMORY_BLOCK_TAILER_SIZE; //-------------------------------------------------------------------- -nsLargeHeapAllocator::nsLargeHeapAllocator() -: nsMemAllocator() +nsLargeHeapAllocator::nsLargeHeapAllocator(size_t minBlockSize, size_t maxBlockSize) +: nsMemAllocator(minBlockSize, maxBlockSize) //-------------------------------------------------------------------- { mBaseChunkSize = mTempChunkSize = (64 * 1024); @@ -127,6 +127,8 @@ void nsLargeHeapAllocator::AllocatorFreeBlock(void *freeBlock) MEM_ASSERT(blockHeader->HasHeaderTag(kUsedBlockHeaderTag), "Bad block header on free"); MEM_ASSERT(blockHeader->HasTrailerTag(blockHeader->GetBlockSize(), kUsedBlockTrailerTag), "Bad block trailer on free"); MEM_ASSERT(blockHeader->CheckPaddingBytes(), "Block overwrote bounds"); + + blockHeader->ZapBlockContents(kFreeMemoryFillPattern); #endif #if STATS_MAC_MEMORY @@ -168,6 +170,7 @@ nsHeapChunk *nsLargeHeapAllocator::AllocateChunk(size_t requestedBlockSize) chunkSize = paddedBlockSize; Ptr chunkMemory = nsAllocatorManager::GetAllocatorManager()->AllocateSubheap(chunkSize, actualChunkSize); + if (!chunkMemory) return nil; // use placement new to initialize the chunk in the memory block nsHeapChunk *newHeapChunk = new (chunkMemory) nsLargeHeapChunk(this, actualChunkSize); @@ -238,6 +241,7 @@ LargeBlockHeader* nsLargeHeapChunk::GetSpaceForBlock(UInt32 blockSize) UInt32 allocSize = ((blockSize + 3) & ~3) + LargeBlockHeader::kLargeBlockOverhead; if (allocSize > mTotalFree) return nil; + //Boolean expectFailure = (allocSize > mTotalFree); /* scan through the blocks in this chunk looking for a big enough free block */ /* we never allocate the head block */ @@ -280,12 +284,14 @@ LargeBlockHeader* nsLargeHeapChunk::GetSpaceForBlock(UInt32 blockSize) blockHeader->SetHeaderTag(kUsedBlockHeaderTag); blockHeader->SetTrailerTag(blockHeader->GetBlockSize(), kUsedBlockTrailerTag); blockHeader->SetPaddingBytes(((blockSize + 3) & ~3) - blockSize); + blockHeader->ZapBlockContents(kUsedMemoryFillPattern); blockHeader->FillPaddingBytes(); #endif mTotalFree -= blockHeader->GetBlockHeapUsageSize(); IncrementUsedBlocks(); + //MEM_ASSERT(!expectFailure, "I though this would fail!"); return blockHeader; } diff --git a/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.h b/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.h index 27a8652fb8f8..a0f615b95729 100644 --- a/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.h +++ b/lib/mac/MacMemoryAllocator/src/nsLargeHeapAllocator.h @@ -15,6 +15,7 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ +#include class nsMemAllocator; class nsLargeHeapChunk; @@ -66,6 +67,8 @@ struct LargeBlockHeader } UInt32 GetPaddingBytes() { return paddingBytes; } + void ZapBlockContents(UInt8 pattern) { memset(&memory, pattern, GetBlockSize()); } + // inline, so won't crash if this is a bad block Boolean HasHeaderTag(MemoryBlockTag inHeaderTag) { return header.headerTag == inHeaderTag; } @@ -108,7 +111,7 @@ class nsLargeHeapAllocator : public nsMemAllocator public: - nsLargeHeapAllocator(); + nsLargeHeapAllocator(size_t minBlockSize, size_t maxBlockSize); ~nsLargeHeapAllocator(); diff --git a/lib/mac/MacMemoryAllocator/src/nsMemAllocator.cp b/lib/mac/MacMemoryAllocator/src/nsMemAllocator.cp index 19c7a29e3129..c4739fe4f79f 100644 --- a/lib/mac/MacMemoryAllocator/src/nsMemAllocator.cp +++ b/lib/mac/MacMemoryAllocator/src/nsMemAllocator.cp @@ -47,9 +47,12 @@ nsHeapChunk::~nsHeapChunk() #pragma mark - //-------------------------------------------------------------------- -nsMemAllocator::nsMemAllocator() +nsMemAllocator::nsMemAllocator(size_t minBlockSize, size_t maxBlockSize) : mFirstChunk(nil) , mLastChunk(nil) +, mNumChunks(0) +, mMinBlockSize(minBlockSize) +, mMaxBlockSize(maxBlockSize) #if DEBUG_HEAP_INTEGRITY , mSignature(kMemAllocatorSignature) #endif @@ -115,6 +118,8 @@ void nsMemAllocator::AddToChunkList(nsHeapChunk *inNewChunk) mLastChunk = inNewChunk; + mNumChunks ++; + #if STATS_MAC_MEMORY mCurSubheapCount ++; if (mCurSubheapCount > mMaxSubheapCount) @@ -158,6 +163,8 @@ void nsMemAllocator::RemoveFromChunkList(nsHeapChunk *inChunk) mLastChunk = prevChunk; } + mNumChunks --; + #if STATS_MAC_MEMORY mCurSubheapCount --; @@ -206,6 +213,31 @@ void nsMemAllocator::AccountForFreedBlock(size_t logicalSize) mCurBlockSpaceUsed -= logicalSize; } +//-------------------------------------------------------------------- +void nsMemAllocator::DumpHeapUsage(PRFileDesc *outFile) +//-------------------------------------------------------------------- +{ + char outString[ 1024 ]; + + sprintf(outString, "%04ld ", mMaxBlockSize); + + WriteString(outFile, outString); + + char *p = outString; + SInt32 numStars = mMaxHeapSpaceUsed / 1024; + if (numStars > 1021) + numStars = 1021; + + for (SInt32 i = 0; i < numStars; i ++) + *p++ = '*'; + + if (numStars == 1021) + *p++ = 'É'; + *p++ = '\n'; + *p = '\0'; + + WriteString(outFile, outString); +} //-------------------------------------------------------------------- void nsMemAllocator::DumpMemoryStats(PRFileDesc *outFile) @@ -213,8 +245,10 @@ void nsMemAllocator::DumpMemoryStats(PRFileDesc *outFile) { char outString[ 1024 ]; + sprintf(outString, "Stats for heap of blocks %ld - %ld bytes\n", mMinBlockSize, mMaxBlockSize); + WriteString ( outFile, "\n\n--------------------------------------------------------------------------------\n" ); - WriteString(outFile, "Stats for heap\n"); + WriteString(outFile, outString); WriteString ( outFile, "--------------------------------------------------------------------------------\n" ); WriteString ( outFile, " Current Max\n" ); WriteString ( outFile, " ---------- -------\n" ); diff --git a/lib/mac/MacMemoryAllocator/src/nsMemAllocator.h b/lib/mac/MacMemoryAllocator/src/nsMemAllocator.h index c8b783c070bf..320435254182 100644 --- a/lib/mac/MacMemoryAllocator/src/nsMemAllocator.h +++ b/lib/mac/MacMemoryAllocator/src/nsMemAllocator.h @@ -33,8 +33,8 @@ enum { kUsedBlockTrailerTag = 'used', kRefdBlockHeaderTag = 'REFD', kRefdBlockTrailerTag = 'refd', - kFreeMemoryFillPattern = 0xEF, - kUsedMemoryFillPattern = 0xDB + kUsedMemoryFillPattern = 0xDB, + kFreeMemoryFillPattern = 0xEF //if you don't want to crash hard, change to 0x04 or 0x05, }; @@ -131,7 +131,7 @@ class nsMemAllocator { public: - nsMemAllocator(); + nsMemAllocator(size_t minBlockSize, size_t maxBlockSize); virtual ~nsMemAllocator() = 0; static size_t GetBlockSize(void *thisBlock); @@ -171,9 +171,15 @@ class nsMemAllocator nsHeapChunk *mFirstChunk; // pointer to first subheap managed by this allocator nsHeapChunk *mLastChunk; // pointer to last subheap managed by this allocator + UInt32 mMinBlockSize; // smallest block normally handled by this allocator (inclusive) + UInt32 mMaxBlockSize; // largest block handled by this allocator (inclusive) + + UInt32 mNumChunks; // number of chunks in list + UInt32 mBaseChunkSize; // size of subheap allocated at startup UInt32 mTempChunkSize; // size of additional subheaps + #if STATS_MAC_MEMORY public: @@ -183,6 +189,9 @@ class nsMemAllocator void AccountForResizedBlock(size_t oldLogicalSize, size_t newLogicalSize); void DumpMemoryStats(PRFileDesc *statsFile); + void DumpHeapUsage(PRFileDesc *statsFile); + + UInt32 GetMaxHeapUsage() { return mMaxHeapSpaceUsed; } private: diff --git a/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.cp b/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.cp index 3e277832147e..ccb2f03c2e4f 100644 --- a/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.cp +++ b/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.cp @@ -27,11 +27,13 @@ const UInt32 SmallHeapBlock::kBlockOverhead = sizeof(SmallHeapBlock) + MEMORY_BL //-------------------------------------------------------------------- -nsSmallHeapAllocator::nsSmallHeapAllocator() -: nsMemAllocator() +nsSmallHeapAllocator::nsSmallHeapAllocator(size_t minBlockSize, size_t maxBlockSize) +: nsMemAllocator(minBlockSize, maxBlockSize) +, mChunkWithSpace(nil) //-------------------------------------------------------------------- { - mBaseChunkSize = mTempChunkSize = (nsAllocatorManager::kChunkSizeMultiple); + // this gets rounded up when we allocate chunks + mBaseChunkSize = mTempChunkSize = 64 * (mMaxBlockSize + SmallHeapBlock::kBlockOverhead); //(nsAllocatorManager::kChunkSizeMultiple); } //-------------------------------------------------------------------- @@ -45,6 +47,13 @@ nsSmallHeapAllocator::~nsSmallHeapAllocator() void *nsSmallHeapAllocator::AllocatorMakeBlock(size_t blockSize) //-------------------------------------------------------------------- { + // try the cheap way first + if (mChunkWithSpace) + { + void *foundBlock = mChunkWithSpace->GetSpaceForBlock(blockSize); + if (foundBlock) return foundBlock; + } + nsSmallHeapChunk *chunk = (nsSmallHeapChunk *)mFirstChunk; // walk through all of our chunks, trying to allocate memory from somewhere @@ -61,6 +70,7 @@ void *nsSmallHeapAllocator::AllocatorMakeBlock(size_t blockSize) chunk = (nsSmallHeapChunk *)AllocateChunk(blockSize); if (!chunk) return nil; + mChunkWithSpace = chunk; return chunk->GetSpaceForBlock(blockSize); } @@ -75,6 +85,8 @@ void nsSmallHeapAllocator::AllocatorFreeBlock(void *freeBlock) MEM_ASSERT(deadBlock->HasHeaderTag(kUsedBlockHeaderTag), "Bad block header on free"); MEM_ASSERT(deadBlock->HasTrailerTag(deadBlock->GetBlockSize(), kUsedBlockTrailerTag), "Bad block trailer on free"); MEM_ASSERT(deadBlock->CheckPaddingBytes(), "Block has overwritten its bounds"); + + deadBlock->ZapBlockContents(kFreeMemoryFillPattern); #endif nsSmallHeapChunk *chunk = deadBlock->GetOwningChunk(); @@ -88,7 +100,16 @@ void nsSmallHeapAllocator::AllocatorFreeBlock(void *freeBlock) // if this chunk is completely empty and it's not the first chunk then free it if (chunk->IsEmpty() && chunk!= mFirstChunk) + { + if (chunk == mChunkWithSpace) + mChunkWithSpace = nil; + FreeChunk(chunk); + } + else + { + mChunkWithSpace = chunk; // we know is has some space now, probably + } } @@ -148,6 +169,7 @@ nsHeapChunk *nsSmallHeapAllocator::AllocateChunk(size_t blockSize) Size actualChunkSize; Ptr chunkMemory = nsAllocatorManager::GetAllocatorManager()->AllocateSubheap(mBaseChunkSize, actualChunkSize); + if (!chunkMemory) return nil; // use placement new to initialize the chunk in the memory block nsHeapChunk *newHeapChunk = new (chunkMemory) nsSmallHeapChunk(this, actualChunkSize); @@ -176,9 +198,10 @@ void nsSmallHeapAllocator::FreeChunk(nsHeapChunk *chunkToFree) //-------------------------------------------------------------------- nsSmallHeapChunk::nsSmallHeapChunk( nsMemAllocator *inOwningAllocator, - Size heapSize) : - nsHeapChunk(inOwningAllocator, heapSize), - mOverflow(nil) + Size heapSize) +: nsHeapChunk(inOwningAllocator, heapSize) +, mOverflow(nil) +, mTotalFree(0) //-------------------------------------------------------------------- { // init the bin ptrs @@ -215,6 +238,9 @@ nsSmallHeapChunk::nsSmallHeapChunk( newRawBlockTrailer->SetOwningChunk(nil); AddBlockToFreeList(newFreeOverflowBlock); +#if DEBUG_HEAP_INTEGRITY + mInitialFree = mTotalFree; +#endif } @@ -222,6 +248,7 @@ nsSmallHeapChunk::nsSmallHeapChunk( nsSmallHeapChunk::~nsSmallHeapChunk() //-------------------------------------------------------------------- { + MEM_ASSERT(mUsedBlocks != 0 || mTotalFree == mInitialFree, "Bad free measure"); } @@ -233,6 +260,9 @@ void *nsSmallHeapChunk::GetSpaceForBlock(size_t blockSize) UInt32 roundedBlockSize = (blockSize + 3) & ~3; MEM_ASSERT(roundedBlockSize <= nsSmallHeapChunk::kMaximumBlockSize, "Block is too big for this allocator!"); + if (mTotalFree < roundedBlockSize) return nil; + //Boolean expectFailure = (mTotalFree < roundedBlockSize); + // Try to find the best fit in one of the bins. UInt32 startingBinNum = (roundedBlockSize - kDefaultSmallHeadMinSize) >> 2; @@ -341,6 +371,7 @@ done: #if DEBUG_HEAP_INTEGRITY allocatedBlock->SetHeaderTag(kUsedBlockHeaderTag); allocatedBlock->SetTrailerTag(allocatedBlock->GetBlockSize(), kUsedBlockTrailerTag); + allocatedBlock->ZapBlockContents(kUsedMemoryFillPattern); allocatedBlock->SetPaddingBytes(roundedBlockSize - blockSize); allocatedBlock->FillPaddingBytes(); #endif @@ -350,6 +381,8 @@ done: GetOwningAllocator()->AccountForNewBlock(blockSize); #endif + //MEM_ASSERT(!expectFailure, "I though this would fail!"); + IncrementUsedBlocks(); return &allocatedBlock->memory; } @@ -574,17 +607,16 @@ void nsSmallHeapChunk::RemoveBlockFromFreeList(SmallHeapBlock *removeBlock) else { UInt32 nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2; -#if DEBUG_HEAP_INTEGRITY - if (blockSize < kDefaultSmallHeadMinSize) - DebugStr("\pBad block size"); - if (nextBlockBin >= kDefaultSmallHeapBins) - DebugStr("\pBad bin index"); -#endif + + MEM_ASSERT(blockSize >= kDefaultSmallHeadMinSize, "Bad block size"); + MEM_ASSERT(nextBlockBin < kDefaultSmallHeapBins, "Bad bin index"); + mBins[nextBlockBin] = nextFree; } } removeBlock->SetBlockUsed(); + mTotalFree -= removeBlock->GetBlockHeapUsage(); } @@ -593,6 +625,8 @@ void nsSmallHeapChunk::RemoveBlockFromFreeList(SmallHeapBlock *removeBlock) void nsSmallHeapChunk::AddBlockToFreeList(SmallHeapBlock *addBlock) //-------------------------------------------------------------------- { + mTotalFree += addBlock->GetBlockHeapUsage(); + addBlock->SetBlockUnused(); UInt32 blockSize = addBlock->GetBlockSize(); @@ -609,12 +643,10 @@ void nsSmallHeapChunk::AddBlockToFreeList(SmallHeapBlock *addBlock) else { UInt32 nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2; -#if DEBUG_HEAP_INTEGRITY - if (blockSize < kDefaultSmallHeadMinSize) - DebugStr("\pBad block size"); - if (nextBlockBin >= kDefaultSmallHeapBins) - DebugStr("\pBad bin index"); -#endif + + MEM_ASSERT(blockSize >= kDefaultSmallHeadMinSize, "Bad block size"); + MEM_ASSERT(nextBlockBin < kDefaultSmallHeapBins, "Bad bin index"); + SmallHeapBlock *tempBlock = mBins[nextBlockBin]; addBlock->SetNextFree(tempBlock); if (tempBlock) tempBlock->SetPrevFree(addBlock); diff --git a/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.h b/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.h index ca9c60b28b40..578d7f89aca7 100644 --- a/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.h +++ b/lib/mac/MacMemoryAllocator/src/nsSmallHeapAllocator.h @@ -15,6 +15,7 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ +#include class nsMemAllocator; class nsSmallHeapChunk; @@ -66,14 +67,18 @@ struct SmallHeapBlock *lastLong &= ~mask; *lastLong |= (mask & kBlockPaddingBytes); } + Boolean CheckPaddingBytes() { UInt32 padding = blockFlags & kBlockPaddingMask; long *lastLong = (long *)((char *)&memory + blockSize - sizeof(long)); UInt32 mask = (1 << (8 * padding)) - 1; return (*lastLong & mask) == (mask & kBlockPaddingBytes); } + UInt32 GetPaddingBytes() { return (blockFlags & kBlockPaddingMask); } + void ZapBlockContents(UInt8 pattern) { memset(&memory, pattern, blockSize); } + // inline, so won't crash if this is a bad block Boolean HasHeaderTag(MemoryBlockTag inHeaderTag) { return info.inUseInfo.freeProc.headerTag == inHeaderTag; } @@ -130,7 +135,7 @@ class nsSmallHeapAllocator : public nsMemAllocator public: - nsSmallHeapAllocator(); + nsSmallHeapAllocator(size_t minBlockSize, size_t maxBlockSize); ~nsSmallHeapAllocator(); virtual void * AllocatorMakeBlock(size_t blockSize); @@ -144,7 +149,7 @@ class nsSmallHeapAllocator : public nsMemAllocator protected: - + nsSmallHeapChunk* mChunkWithSpace; // cheap optimization }; @@ -167,20 +172,30 @@ class nsSmallHeapChunk : public nsHeapChunk enum { kDefaultSmallHeadMinSize = 4L, - kDefaultSmallHeapBins = 64L, + kDefaultSmallHeapBins = 128L, kMaximumBinBlockSize = kDefaultSmallHeadMinSize + 4L * kDefaultSmallHeapBins - 1, kMaximumBlockSize = 0xFFFF }; - SmallHeapBlock** GetBins(UInt32 binIndex) { return mBins + binIndex; } + SmallHeapBlock** GetBins(UInt32 binIndex) + { + MEM_ASSERT(binIndex < kDefaultSmallHeapBins, "Bad bin index!"); + return mBins + binIndex; + } + SmallHeapBlock* GetOverflow() { return mOverflow; } void RemoveBlockFromFreeList(SmallHeapBlock *removeBlock); void AddBlockToFreeList(SmallHeapBlock *addBlock); - SmallHeapBlock *mOverflow; - SmallHeapBlock *mBins[kDefaultSmallHeapBins]; + UInt32 mTotalFree; +#if DEBUG + UInt32 mInitialFree; +#endif + SmallHeapBlock* mOverflow; + SmallHeapBlock* mBins[kDefaultSmallHeapBins]; SmallHeapBlock mMemory[]; + };