D programming Language



Download 1.66 Mb.
Page28/47
Date08.01.2017
Size1.66 Mb.
#7507
1   ...   24   25   26   27   28   29   30   31   ...   47

Reference Counting


The idea behind reference counting is to include a count field in the object. Increment it for each additional reference to it, and decrement it whenever a reference to it ceases. When the count hits 0, the object can be deleted.

D doesn't provide any automated support for reference counting, it will have to be done explicitly.

Win32 COM programming uses the members AddRef() and Release() to maintain the reference counts.

Explicit Class Instance Allocation


D provides a means of creating custom allocators and deallocators for class instances. Normally, these would be allocated on the garbage collected heap, and deallocated when the collector decides to run. For specialized purposes, this can be handled by creating NewDeclarations and DeleteDeclarations. For example, to allocate using the C runtime library's malloc and free:

import c.stdlib;

import outofmemory;

import gc;


class Foo

{

new(uint sz)



{

void* p;


p = c.stdlib.malloc(sz);

if (!p)


throw new OutOfMemory();

gc.addRange(p, p + sz);

return p;

}
delete(void* p)

{

if (p)


{ gc.removeRange(p);

c.stdlib.free(p);

}

}

}



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.

Mark/Release


Mark/Release is equivalent to a stack method of allocating and freeing memory. A 'stack' is created in memory. Objects are allocated by simply moving a pointer down the stack. Various points are 'marked', and then whole sections of memory are released simply by resetting the stack pointer back to a marked point.

import c.stdlib;

import outofmemory;
class Foo

{

static void[] buffer;



static int bufindex;

static const int bufsize = 100;


static this()

{ void *p;


p = malloc(bufsize);

if (!p)


throw new OutOfMemory;

gc.addRange(p, p + bufsize);

buffer = p[0 .. bufsize];

}
static ~this()

{

if (buffer.length)



{

gc.removeRange(buffer);

free(buffer);

buffer = null;

}

}
new(uint sz)



{ void *p;
p = &buffer[bufindex];

bufindex += sz;

if (bufindex > buffer.length)

throw new OutOfMemory;

return p;

}
delete(void* p)

{

assert(0);



}
static int mark()

{

return bufindex;



}
static void release(int i)

{

bufindex = i;



}

}
void test()

{

int m = Foo.mark();



Foo f1 = new Foo; // allocate

Foo f2 = new Foo; // allocate

...

Foo.release(m); // deallocate f1 and f2



}

 The allocation of buffer[] itself is added as a region to the gc, so there is no need for a separate call inside Foo.new() to do it.


RAII (Resource Acquisition Is Initialization)


RAII techniques can be useful in avoiding memory leaks when using explicit allocators and deallocators. Adding the auto attribute to such classes can help.

Allocating Class Instances On The Stack


Allocating class instances on the stack is useful for temporary objects that are to be automatically deallocated when the function is exited. No special handling is needed to account for function termination via stack unwinding from an exception. To work, they must not have destructors.

import c.stdlib;


class Foo

{

new(uint sz, void *p)



{

return p;

}
delete(void* p)

{

assert(0);



}

}
void test()

{

Foo f = new(c.stdlib.alloca(Foo.classinfo.init.length)) Foo;



...

}


  • There is no need to check for a failure of alloca() and throw an exception, since by definition alloca() will generate a stack overflow exception if it overflows.

  • There is no need for a call to gc.addRange() or gc.removeRange() since the gc automatically scans the stack anyway.

  • The dummy delete() function is to ensure that no attempts are made to delete a stack based object.


Download 1.66 Mb.

Share with your friends:
1   ...   24   25   26   27   28   29   30   31   ...   47




The database is protected by copyright ©ininet.org 2024
send message

    Main page