зеркало из https://github.com/mozilla/gecko-dev.git
402 строки
11 KiB
C++
402 строки
11 KiB
C++
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is [Open Source Virtual Machine.].
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Adobe System Incorporated.
|
|
* Portions created by the Initial Developer are Copyright (C) 2004-2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Adobe AS3 Team
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifndef __GCAlloc__
|
|
#define __GCAlloc__
|
|
|
|
|
|
namespace MMgc
|
|
{
|
|
/**
|
|
*
|
|
* This is a fast, fixed-size memory allocator for garbage-collected
|
|
* objects.
|
|
*
|
|
* Memory is allocated from the system on 4096-byte aligned boundaries,
|
|
* which corresponds to the size of an OS page in Windows XP. Allocation
|
|
* of pages is performed via the GCPageAlloc class.
|
|
*
|
|
* In each 4096-byte block, there is a block header with marked bits,
|
|
* finalize bits, the pointer to the next free item and "recycling"
|
|
* free item linked list.
|
|
*
|
|
* The bits of the "marked" bitmap are controlled by the SetMark method.
|
|
*
|
|
* The bits of the "finalize" bitmap are set when an item is
|
|
* allocated. The value for the finalize bit is passed in as a
|
|
* parameter to the allocation call.
|
|
*
|
|
* When the Sweep method is invoked, all objects that are not marked
|
|
* with the specified mark flag are disposed of. If the corresponding
|
|
* finalize bit is set, the GCObject destructor is invoked on that
|
|
* item.
|
|
*
|
|
* When an allocation is requested and there are no more free
|
|
* entries, GCAlloc will request that a garbage collection take
|
|
* place. It will allocate new blocks if more than 20% of its
|
|
* blocks are used after the collection, targeting a 5:1
|
|
* heap size / minimim heap size ratio.
|
|
*
|
|
*/
|
|
class GCAlloc : public GCAllocObject
|
|
{
|
|
friend class GC;
|
|
public:
|
|
enum ItemBit { kMark=1, kQueued=2, kFinalize=4, kHasWeakRef=8, kFreelist=kMark|kQueued };
|
|
|
|
GCAlloc(GC* gc, int itemSize, bool containsPointers, bool isRC, int sizeClassIndex);
|
|
~GCAlloc();
|
|
|
|
void* Alloc(size_t size, int flags);
|
|
static void Free(void *ptr);
|
|
void Finalize();
|
|
size_t GetItemSize() { return m_itemSize; }
|
|
void ClearMarks();
|
|
#ifdef _DEBUG
|
|
void CheckMarks();
|
|
#endif
|
|
|
|
static int SetMark(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
int index = GetIndex(block, item);
|
|
int mask = kMark << ((index&7)<<2);
|
|
uint32 *bits = &block->GetBits()[index>>3];
|
|
int set = *bits & mask;
|
|
*bits |= mask;
|
|
*bits &= ~(kQueued << ((index&7)<<2));
|
|
return set;
|
|
}
|
|
|
|
static int SetQueued(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return SetBit(block, GetIndex(block, item), kQueued);
|
|
}
|
|
|
|
static int SetFinalize(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return SetBit(block, GetIndex(block, item), kFinalize);
|
|
}
|
|
|
|
static int IsWhite(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
|
|
// not a real item
|
|
if(item < block->items)
|
|
return false;
|
|
|
|
if(FindBeginning(item) != item)
|
|
return false;
|
|
|
|
return IsWhite(block, GetIndex(block, item));
|
|
}
|
|
|
|
|
|
static int GetMark(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
|
|
// Return the "marked" bit
|
|
return GetBit(block, GetIndex(block, item), kMark);
|
|
}
|
|
|
|
static void *FindBeginning(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
|
|
return block->items + block->size * GetIndex(block, item);
|
|
}
|
|
|
|
static void ClearFinalized(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
ClearBits(block, GetIndex(block, item), kFinalize);
|
|
}
|
|
|
|
static int IsFinalized(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return GetBit(block, GetIndex(block, item), kFinalize);
|
|
}
|
|
static int HasWeakRef(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return GetBit(block, GetIndex(block, item), kHasWeakRef);
|
|
}
|
|
|
|
static bool ContainsPointers(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return block->alloc->ContainsPointers();
|
|
}
|
|
|
|
static bool IsRCObject(const void *item)
|
|
{
|
|
// Zero low 12 bits of address to get to the Block header
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
return item >= block->items && block->alloc->IsRCObject();
|
|
}
|
|
|
|
static bool IsUnmarkedPointer(const void *val);
|
|
|
|
int GetNumAlloc() const { return m_numAlloc; }
|
|
int GetMaxAlloc() const { return m_maxAlloc; }
|
|
int GetNumBlocks() const { return m_numBlocks; }
|
|
|
|
bool ContainsPointers() const { return containsPointers; }
|
|
bool IsRCObject() const { return containsRCObjects; }
|
|
|
|
void GetBitsPages(void **pages);
|
|
|
|
static void SetHasWeakRef(const void *item, bool to)
|
|
{
|
|
GCBlock *block = (GCBlock*) ((uintptr)item & ~0xFFF);
|
|
if(to) {
|
|
SetBit(block, GetIndex(block, item), kHasWeakRef);
|
|
} else {
|
|
ClearBits(block, GetIndex(block, item), kHasWeakRef);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const static int kBlockSize = 4096;
|
|
|
|
struct GCBlock;
|
|
|
|
friend struct GCAlloc::GCBlock;
|
|
|
|
struct GCBlock
|
|
{
|
|
GC *gc;
|
|
GCBlock* next;
|
|
uint32 size;
|
|
GCAlloc *alloc;
|
|
GCBlock* prev;
|
|
char* nextItem;
|
|
void* firstFree; // first item on free list
|
|
GCBlock *prevFree;
|
|
GCBlock *nextFree;
|
|
uint32* bits;
|
|
short numItems;
|
|
bool needsSweeping:1;
|
|
bool finalizeState:1; // whether we've been visited during the Finalize stage
|
|
char *items;
|
|
|
|
int GetCount() const
|
|
{
|
|
if (nextItem) {
|
|
return GCAlloc::GetIndex(this, nextItem);
|
|
} else {
|
|
return alloc->m_itemsPerBlock;
|
|
}
|
|
}
|
|
|
|
uint32 *GetBits() const
|
|
{
|
|
return bits;
|
|
}
|
|
|
|
void FreeItem(void *item, int index);
|
|
|
|
bool IsFull() const
|
|
{
|
|
bool full = (nextItem == firstFree);
|
|
// the only time nextItem and firstFree should be equal is when they
|
|
// are both zero which is also when we are full, assert to be sure
|
|
GCAssert(!full || nextItem==0);
|
|
GCAssert(!full || numItems == alloc->m_itemsPerBlock);
|
|
return full;
|
|
}
|
|
};
|
|
|
|
// The list of chunk blocks
|
|
GCBlock* m_firstBlock;
|
|
GCBlock* m_lastBlock;
|
|
|
|
// The lowest priority block that has free items
|
|
GCBlock* m_firstFree;
|
|
|
|
// List of blocks that need sweeping
|
|
GCBlock* m_needsSweeping;
|
|
|
|
int m_itemsPerBlock;
|
|
size_t m_itemSize;
|
|
int m_numBitmapBytes;
|
|
int m_sizeClassIndex;
|
|
|
|
bool m_bitsInPage;
|
|
|
|
int m_maxAlloc;
|
|
int m_numAlloc;
|
|
int m_numBlocks;
|
|
|
|
// fast divide numbers
|
|
uint16 multiple;
|
|
uint16 shift;
|
|
|
|
bool containsPointers;
|
|
bool containsRCObjects;
|
|
bool m_finalized;
|
|
|
|
bool IsOnEitherList(GCBlock *b)
|
|
{
|
|
return b->nextFree != NULL || b->prevFree != NULL || b == m_firstFree || b == m_needsSweeping;
|
|
}
|
|
|
|
GCBlock* CreateChunk();
|
|
void UnlinkChunk(GCBlock *b);
|
|
void FreeChunk(GCBlock* b);
|
|
void AddToFreeList(GCBlock *b)
|
|
{
|
|
GCAssert(!IsOnEitherList(b));
|
|
b->prevFree = NULL;
|
|
b->nextFree = m_firstFree;
|
|
if (m_firstFree) {
|
|
GCAssert(m_firstFree->prevFree == 0 && m_firstFree != b);
|
|
m_firstFree->prevFree = b;
|
|
}
|
|
m_firstFree = b;
|
|
}
|
|
|
|
void RemoveFromFreeList(GCBlock *b)
|
|
{
|
|
GCAssert(m_firstFree == b || b->prevFree != NULL);
|
|
if ( m_firstFree == b )
|
|
m_firstFree = b->nextFree;
|
|
else
|
|
b->prevFree->nextFree = b->nextFree;
|
|
|
|
if (b->nextFree)
|
|
b->nextFree->prevFree = b->prevFree;
|
|
b->nextFree = b->prevFree = NULL;
|
|
}
|
|
|
|
void AddToSweepList(GCBlock *b)
|
|
{
|
|
GCAssert(!IsOnEitherList(b) && !b->needsSweeping);
|
|
b->prevFree = NULL;
|
|
b->nextFree = m_needsSweeping;
|
|
if (m_needsSweeping) {
|
|
GCAssert(m_needsSweeping->prevFree == 0);
|
|
m_needsSweeping->prevFree = b;
|
|
}
|
|
m_needsSweeping = b;
|
|
b->needsSweeping = true;
|
|
}
|
|
|
|
void RemoveFromSweepList(GCBlock *b)
|
|
{
|
|
GCAssert(m_needsSweeping == b || b->prevFree != NULL);
|
|
if ( m_needsSweeping == b )
|
|
m_needsSweeping = b->nextFree;
|
|
else
|
|
b->prevFree->nextFree = b->nextFree;
|
|
|
|
if (b->nextFree)
|
|
b->nextFree->prevFree = b->prevFree;
|
|
b->needsSweeping = false;
|
|
b->nextFree = b->prevFree = NULL;
|
|
}
|
|
|
|
bool Sweep(GCBlock *b);
|
|
void SweepGuts(GCBlock *b);
|
|
|
|
|
|
void ClearMarks(GCAlloc::GCBlock* block);
|
|
void SweepNeedsSweeping();
|
|
|
|
bool IsLastFreeBlock(GCBlock *b) { return m_firstFree == NULL || (m_firstFree == b && b->nextFree == NULL); }
|
|
|
|
static int ConservativeGetMark(const void *item, bool bogusPointerReturnValue);
|
|
|
|
static int GetIndex(const GCBlock *block, const void *item)
|
|
{
|
|
int index = (((char*) item - block->items) * block->alloc->multiple) >> block->alloc->shift;
|
|
#ifdef _DEBUG
|
|
GCAssert(((char*) item - block->items) / block->size == (uint32) index);
|
|
#endif
|
|
return index;
|
|
}
|
|
|
|
static int IsWhite(GCBlock *block, int index)
|
|
{
|
|
return (block->GetBits()[index>>3] & ((kMark|kQueued)<<((index&7)<<2))) == 0;
|
|
}
|
|
|
|
static int SetBit(GCBlock *block, int index, int bit)
|
|
{
|
|
int mask = bit << ((index&7)<<2);
|
|
int set = (block->GetBits()[index>>3] & mask);
|
|
block->GetBits()[index>>3] |= mask;
|
|
return set;
|
|
}
|
|
|
|
static int GetBit(GCBlock *block, int index, int bit)
|
|
{
|
|
int mask = bit << ((index&7)<<2);
|
|
return block->GetBits()[index>>3] & mask;
|
|
}
|
|
|
|
static void ClearBits(GCBlock *block, int index, int bits)
|
|
{
|
|
int mask = bits << ((index&7)<<2);
|
|
block->GetBits()[index>>3] &= ~mask;
|
|
}
|
|
|
|
void ComputeMultiplyShift(uint16 d, uint16 &muli, uint16 &shft);
|
|
|
|
protected:
|
|
GC *m_gc;
|
|
|
|
};
|
|
}
|
|
|
|
#endif /* __GCAlloc__ */
|