[Home]
[Search]
[D]
Last update Feb 8, 2003
Garbage Collection
D is a fully garbage collected language. That means that it is never necessary
to free memory. Just allocate as needed, and the garbage collector will
periodically return all unused memory to the pool of available memory.
C and C++ programmers accustomed to explicitly managing memory
allocation and
deallocation will likely be skeptical of the benefits and efficacy of
garbage collection. Experience both with new projects written with
garbage collection in mind, and converting existing projects to garbage
collection shows that:
- Garbage collected programs are faster. This is counterintuitive,
but the reasons are:
- Reference counting is a common solution to solve explicit
memory allocation problems. The code to implement the increment and
decrement operations whenever assignments are made is one source
of slowdown. Hiding it behind smart pointer classes doesn't help
the speed. (Reference counting methods are not a general solution
anyway, as circular references never get deleted.)
- Destructors are used to deallocate resources acquired by an object.
For most classes, this resource is allocated memory.
With garbage collection, most destructors then become empty and
can be discarded entirely.
- All those destructors freeing memory can become significant when
objects are allocated on the stack. For each one, some mechanism must
be established so that if an exception happens, the destructors all
get called in each frame to release any memory they hold. If the
destructors become irrelevant, then there's no need to set up special
stack frames to handle exceptions, and the code runs faster.
- All the code necessary to manage memory can add up to quite a
bit. The larger a program is, the less in the cache it is,
the more paging it does, and the slower
it runs.
- Garbage collection kicks in only when memory gets tight. When
memory is not tight, the program runs at full speed and does not
spend any time freeing memory.
- Modern garbage collectors are far more advanced now than the
older, slower ones. Generational, copying collectors eliminate much
of the inefficiency of early mark and sweep algorithms.
- Modern garbage collectors do heap compaction. Heap compaction
tends to reduce the number of pages actively referenced by a program,
which means that memory accesses are more likely to be cache hits
and less swapping.
- Garbage collected programs do not suffer from gradual deterioration
due to an accumulation of memory leaks.
- Garbage collectors reclaim unused memory, therefore they do not suffer
from "memory leaks" which can cause long running applications to gradually
consume more and more memory until they bring down the system. GC'd programs
have longer term stability.
- Garbage collected programs have fewer hard-to-find pointer bugs. This
is because there are no dangling references to free'd memory. There is no
code to explicitly manage memory, hence no bugs in such code.
- Garbage collected programs are faster to develop and debug, because
there's no need for developing, debugging, testing, or maintaining the
explicit deallocation code.
- Garbage collected programs can be significantly smaller, because
there is no code to manage deallocation, and there is no need for exception
handlers to deallocate memory.
Garbage collection is not a panacea. There are some downsides:
- It is not predictable when a collection gets run, so the program
can arbitrarily pause.
- The time it takes for a collection to run is not bounded. While in
practice it is very quick, this cannot be guaranteed.
- All threads other than the collector thread must be halted while
the collection is in progress.
- Garbage collectors can keep around some memory that an explicit deallocator
would not. In practice, this is not much of an issue since explicit
deallocators usually have memory leaks causing them to eventually use
far more memory, and because explicit deallocators do not normally
return deallocated memory to the operating system anyway, instead just
returning it to its own internal pool.
- Garbage collection should be implemented as a basic operating system
kernel service. But since they are not, garbage collecting programs must
carry around with them the garbage collection implementation. While this
can be a shared DLL, it is still there.
These constraints are addressed by techniques outlined
in Memory Management.
How Garbage Collection Works
To be written...
Interfacing Garbage Collected Objects With Foreign Code
The garbage collector looks for roots in its static data segment, and the
stacks and register contents of each thread. If the only root of an object
is held outside of this, then the collecter will miss it and free the
memory.
To avoid this from happening,
- Maintain a root to the object in an area the collector does scan
for roots.
- Reallocate the object using the foreign code's storage allocator
or using the C runtime library's malloc/free.
Pointers and the Garbage Collector
The garbage collector's algorithms depend on pointers being pointers and
not pointers being not pointers. To that end, the following practices that
are not unusual in C should be carefully avoided in D:
- Do not hide pointers by xor'ing them with other values, like the
xor'd pointer linked list trick used in C. Do not use the xor trick
to swap two pointer values.
- Do not store pointers into int variables using casts and other tricks.
The garbage collector does not scan non-pointer types for roots.
- Do not take advantage of alignment of pointers to store bit flags
in the low order bits, do not store bit flags in the high order bits.
- Do not store integer values into pointers.
- Do not store magic values into pointers, other than null.
- If you must share the same storage location between pointers and
non-pointer types, use a union to do it so the garbage collector knows about
it.
In fact, avoid using pointers at all as much as possible. D provides features
rendering most explicit pointer uses obsolete, such as reference objects,
dynamic arrays, and garbage collection. Pointers
are provided in order to interface successfully with C API's and for some
wizard level work.
Working with the Garbage Collector
Garbage collection doesn't solve every memory deallocation problem. For
example, if a root to a large data structure is kept, the garbage collector
cannot reclaim it, even if it is never referred to again. To eliminate
this problem, it is good practice to set a reference or pointer to an
object to null when
no longer needed.
This advice applies only to static references or references embedded inside
other objects. There is not much point for such stored on the stack to be
nulled, since the collector doesn't scan for roots past the top of the stack,
and because new stack frames are initialized anyway.
Copyright (c) 1999-2001 by Digital Mars, All Rights Reserved