Friday, August 3, 2007

The D Programming Language: Disabling Garbage Collection

I have become impressed with the D programming language (especially since it has a GNU Compiler front end!). However, it has a garbage collector! How terrible for systems programmers (ahem). So here is a way to manually manage memory with the classes.

class foo
{
     new(uint sz)
     {
         void* p;
         p = std.c.stdlib.malloc(sz);
         if (!p)
             throw new OutOfMemory();
         gc.addRange(p, p + sz);
         return p;
     }
     delete(void* p)
     {
         if (p)
         {   gc.removeRange(p);
             std.c.stdlib.free(p);
         }
     }
}
This example is available and analyzed elsewhere on the web, from the founder of the D programming language:
The critical features of new() are:
new() does not have a return type specified, but it is defined to be void*. new() must return a void*.
• If new() cannot allocate memory, it must not return null, but must throw an exception.
• The pointer returned from new() must be to memory aligned to the default alignment. This is 8 on win32 systems.
• The size parameter is needed in case the allocator is called from a class derived from Foo and is a larger size than Foo.
• A null is not returned if storage cannot be allocated. Instead, an exception is thrown. Which exception gets thrown is up to the programmer, in this case, OutOfMemory() is.
• When scanning memory for root pointers into the garbage collected heap, the static data segment and the stack are scanned automatically. The C heap is not. Therefore, if Foo or any class derived from Foo using the allocator contains any references to data allocated by the garbage collector, the gc needs to be notified. This is done with the gc.addRange() method.
• No initialization of the memory is necessary, as code is automatically inserted after the call to new() to set the class instance members to their defaults and then the constructor (if any) is run.

The critical features of delete() are: • The destructor (if any) has already been called on the argument p, so the data it points to should be assumed to be garbage.
• The pointer p may be null.
• If the gc was notified with gc.addRange(), a corresponding call to gc.removeRange() must happen in the deallocator.
• If there is a delete(), there should be a corresponding new().

If memory is allocated using class specific allocators and deallocators, careful coding practices must be followed to avoid memory leaks and dangling references. In the presence of exceptions, it is particularly important to practice RAII to prevent memory leaks.
Also, I just learned by experience that the following code works as well:

extern (C) void* kmalloc(uint sz); //alternatively malloc()
extern (C) void kfree(void *p); //and free() defined in an external C file

class foo
{
     new(uint sz)
     {
         void* p;
         p = kmalloc(sz);
         return p;
     }
     delete(void* p)
     {
         kfree(p);
     }
}

No comments: