TheSourcery | RecentChanges | Preferences | Index | RSS

Showing revision 8

The idea behind ROM's memory scheme

Travel back in time to 1992-93 for a minute. ROM was written on a Unix system which by design is a batch system. Processes in Unix only eat memory and never actually free it anyway. I don't know that this is still done this way today, but then the kernel sources are available for anyone to look at. More than likely today's mallocs have been rewritten to do something very similar to what ROM does, and perhaps better. Anyway it was discovered that it was much faster to allocate memory once in a big chunk and simply exit the process when the program finished and let the operating system do the job of freeing it. Applications mananged that chunk of memory instead of constantly freeing and allocating memory, especially long running processes like servers. Servers were written in such a way as to keep track of memory they had already allocated and reuse it.

ROMs particular memory management scheme has two levels, alloc_perm() and alloc_mem(). The Routine alloc_perm() is called by objects that keep track of their own free list, and alloc_mem() is called for general memory that's kept track of in a generic free list.

Looking at the BUFFER structures and routines, you will note that BUFFER structures themselves use alloc_perm() and the routines that handle them manage their own free list, via buf_free(). The INVALIDATE and VALIDATE macros are used to set a flag indicating these structs are dead objects and can be reused, or test the flag to see if they are live objects. So free_mem() is never called for alloc_perm() objects. Objects allocated via alloc_perm() will never be freed during the runtime life of the server. However dead objects will be reused assuming like BUFFER the private free list is managed correctly.

Now the character arrays that allocated as part of BUFFER structure use alloc_mem() and that memory is tracked by the generic free list, rgFreeList[]. This memory when your done with it is returned to the generic free list.

BUFFER *buf_free;  <--- The free list for BUFFER

BUFFER *new_buf()


 BUFFER *buffer;

 if (buf_free == NULL) 

   buffer = alloc_perm(sizeof(*buffer));  <--- empty free list, allocate a new one



   buffer = buf_free;

   buf_free = buf_free->next;  <--- fetch one off the free list



 buffer->next = NULL;

 buffer->state = BUFFER_SAFE;

 buffer->size = get_size(BASE_BUF);


 buffer->string = alloc_mem(buffer->size);  <--- its string uses generic free list

 buffer->string[0] = '\0';

 VALIDATE(buffer);   <--- mark it valid

 return buffer;



void free_buf(BUFFER *buffer)


 if (!IS_VALID(buffer))


 free_mem(buffer->string,buffer->size);  <--- return its string to generic free list

 buffer->string = NULL;

 buffer->size   = 0;

 buffer->state  = BUFFER_FREED;

 INVALIDATE(buffer);  <--- mark it invalid

 buffer->next = buf_free;  <--- return it to free list

 buf_free     = buffer;



I'd call this a high-water mark memory model as that is how it behaves. You will see memory go up and not go down. However it will be recyled and reused constantly. The problem with this scheme is that it can hide some nasty program errors that can remain hidden for a long time. It allows you to accidently reference objects (by ignoring the valid flag) that have long been discarded by the system and not get a SIGSEGV. It is however efficient and sensible. However newer mallocs from current C runtime libraries may be more efficient.

This scheme gives memory leak detectors like valgrind, purify and electric fence fits, because their hooks are placed in malloc() and free(). There's nothing wrong with it though.

P.S. There is also a third mechanism that is used to allocate a large pool of unmutable string space at boot up time when areas are loaded. Some other Diku derivatives such as Envy attempt to hash strings and share like strings (i.e. SSM) in order to save memory.

P.P.S. KaVir? informs me that this memory management mechanism first shows up in Merc 2.0/2.1 and indeed it do. I would expect to find it then in more derivatives than just those descended from ROM.
Merc uses 1 used and freed memory list for all allocations, rom takes things a bit further (too far if you ask me) keeping seperate memory lists for every structure. As far as I know most merc derivatives have a memory manager, many of them modified or rewritten.

TheSourcery | RecentChanges | Preferences | Index | RSS
Edit revision 8 of this page | View other revisions | View current revision
Edited June 25, 2004 6:38 am by Scandum (diff)
All material on this Wiki is the property of the contributing authors.
©2004-2006 by the contributing authors.
Ideas, requests, problems regarding this site? Send feedback.