Class Allocator.

The Allocator class does the heavy lifting for Archiveopteryx memory allocation system, a simple garbage collector for event-driven servers.

Our GC system is based on the notion of eternal objects and safe GC points. Eternal objects must be declared by calling addEternal. Collectible objects are allocated by calling alloc(), or alternatively by inheriting Garbage. Most classes inherit from Garbage.

The free() function mark all objects that can be reached from the eternal ones, and afterwards frees anything which isn't reachable. It can be called whenever there are no pointers into the heap, ie. only during the main event loop.

Each single instance of the Allocator class allocates memory blocks of a given size. There are static functions to the heavy loading, such as free() to free all unreachable memory, allocate() to allocate something, allocator() to find an Allocator responsible for a given size and finally rounded(), to find the largest size which will fit comfortably in an allocation block, rounded().

The EString and UString classes can call rounded() to optimize their memory usage.

Allocator::Allocator( uint s )

This private constructor creates an Allocator to dispense objects of size at most s - sizeof(void*) bytes.

static void Allocator::addEternal( const void * p, const char * t )

Records that *p is an allocation root, i.e. that whatever it points to is a valid object. t is a description of this root (e.g. "array of connection objects").

void * Allocator::alloc( uint s, uint n )

Allocates s bytes of collectible memory, which may contain up to n pointers. If n is too large to be contained within s bytes, alloc() uses the largest number that will fit. The default value is UINT_MAX, which in practice means that the entire object may consist of pointers.

Note that s is a uint, not a size_t. In our universe, it isn't possible to allocate more than 4GB at a time. So it is.

void * Allocator::allocate( uint size, uint pointers )

Allocates a chunk of memory (which may contain up to pointers pointers), notes that at most size bytes are in use, and returns a pointer to it.

static uint Allocator::allocated()

Returns the number of bytes allocated since the last memory sweep.

Allocator * Allocator::allocator( uint size )

Returns a pointer to the Allocator responsible for size. size need not be rounded.

void * Allocator::block( uint i )

Returns a pointer to block no. i in this Allocator. The pointer is to the management word, not the payload.

uint Allocator::chunkSize() const

Returns the amount of memory gobbled up when this Allocator allocates memory. This is a little bigger than the biggest object this Allocator can provide.

void Allocator::dealloc( void * p )

Deallocates the object at p.

This is never strictly necessary, however, if a very large number of objects are allocated and deallocated, it may be beneficial. This function exists because it was beneficial in EString::reserve().

void Allocator::deallocate( void * p )

Deallocates the object at p, provided that it's within this Allocator. Calling this function is never necessary, since free() does the same job. However, EString helps out by doing it occasionally.

static void Allocator::free()

Frees all memory that's no longer in use. This can take some time.

static uint Allocator::inUse()

Returns the number of bytes in use after the last sweep.

void Allocator::mark( void * p )

This private helper checks that p is a valid pointer to unmarked GCable memory, marks it, and puts it on a stack so that mark() can process it and add its children to the stack.

void Allocator::mark()

This private helper processes all the stacked pointers, scanning them for valid pointers and marking any that exist.

static Allocator * Allocator::owner( const void * p )

Returns a pointer to the Allocator that manages p.

Should go away. As soon as possible.

static void Allocator::removeEternal( const void * p )

Records that *p is no longer an allocation root. The object may have been deleted.

static void Allocator::removeEternal( void * p )

Records that *p is no longer an allocation root. The object may have been deleted.

static uint Allocator::rounded( uint size )

Returns the biggest number of bytes which can be allocated at the same effective cost as size.

Suppose allocating 24, 25 or 28 bytes all cause Allocator to use 32 bytes, but 29 causes Allocator to use 48. Then rounded(24), rounded(25) and rounded(28) all return 28, while rounded(29) might return something like 44.

This can be used by EString and UString to optimize their memory usage. Perhaps also by other classes.

void Allocator::setNumPointers( const void * p, uint n )

Records that p contains at most n pointers, all located at the start of the object. The rest of the object is not scanned for pointers during garbage collection, which can be helpful if the object contains either very large string/text data or apparently random binary data.

Scanning long strings is slow. Binary data can give false alarms during pointer scanning, which will lead ot memory not being freed.

static void Allocator::setReporting( bool report )

Instructs the Allocator to log various statistics if report is true, and to be entirely silent if report is false.

The initial value is false.

static uint Allocator::sizeOf( void * p )

Returns the amount of memory allocated to hold p and any object to which p points.

As a side effect, this marks p and the other objects so they won't be freed during the next collection.

void Allocator::sweep()

Sweeps this allocator, freeing all unmarked memory blocks and unmarking all memory blocks.

Allocator::~Allocator()

Destroys the object and frees its allocated memory.

This web page based on source code belonging to The Archiveopteryx Developers. All rights reserved.