Dana uses a hybrid approach to memory management. Dana does not have an active garbage collector which is always running; memory is only ever checked for cleanup as part of normal program control flow, at points where cleanup might be necessary. This generally makes memory management both automated and fast, and avoids stop-the-world effects of active mark-sweep garbage collectors.
In most cases, memory management in Dana will appear to be fully automated: allocated memory is automatically cleaned up for you by the language, and so there's nothing that you need to do as a programmer.
However, understanding the detail of Dana's memory management approach will help you to know when you don't get full automation and so will need to explicitly assist with your own code.
Dana defines two kinds of memory usage: managed and unmanaged.
Managed memory is that which has been created by an object by instantiating data items, arrays, and objects. As long as these items are still referenced by their creating object, we call them managed. Dana uses a simple reference-counting garbage collector for managed memory, which means that when the number of references to an element reaches zero the system will automatically clean up the memory of that element. However, Dana will not check for circular references in managed memory; the programmer is therefore responsible for explicitly breaking any such circular references in managed memory scopes in order to allow memory to be cleaned up. Circular memory references often occur in structures like doubly-linked lists, where each element of a list references the next item and the previous item. It is up to the programmer to determine whether or not managed memory instances may contain reference cycles, but Dana has a built-in memory leak checker which can tell you of any problems at runtime if you're not sure.
Unmanaged memory is any data instance or array which has been passed to an object that did not create it, where the creating object no longer has a reference to it. In this case, the owner of the instance is no longer managing the memory and so could not be responsible for ensuring it is cycle-free for cleanup. In this case Dana's full cycle-detecting garbage collector operates on these instances to automatically detect circular references and free the memory when it is no longer referenced. Each time the reference count of an unmanaged data or array instance decreases, the Dana runtime triggers its cycle-detection process on that instance.
Cycle detection is relatively expensive, and can significantly impact the performance of a program if you are not aware of it. The Dana compiler automatically detects which data types will never need cycle detection, and annotates those types as such in order to optimise execution time of a program. In some cases. however, it is not possible for the compiler to statically determine this, which is where performance problems may arise as the compiler necessarily takes a pessimistic approach. Consider this example:
data JSONElement {
char content[]
JSONElement children[]
}
Here we have a field called children of type JSONElement, which could contain references to the parent instance. However, JSON documents are trees, so as a programmer we know that the children field will never actually be used in a way that could cause a reference graph cycle. In this case we can annotate the type with the nocycle keyword, which tells the garbage collector not to bother with cycle-detection on data instances of this type:
data JSONElement nocycle {
char content[]
JSONElement children[]
}
Note that using nocycle is not strictly necessary, and if you're not sure what to do then it's always better not to use it. If you are certain that you have a data type which looks like it could entail circular references, but you know that instances of that type never will do in practice, then using nocycle appropriately will improve the general performance of your program.
Dana has a memory leak checker built in to the runtime. To use it, launch your program with the command dana -mc MyProgram.o. This will report all instances in which managed memory was not properly cleaned up because it was left with circular references. Note that programs will run much more slowly with the leak checker turned on, so it should only be used during development.