Memory Management
Automatic Memory Management | Extracting from a Managed Object |
Manual Memory Management | Unmanaging a Managed Object |
Managed | Value Objects |
Certain elements of programs compiled by the Wolfram Compiler are reference objects and are held as a memory address. They are typically created by dynamic memory allocation. There are also other ways to allocate and free raw memory. This section discusses the functionality that the compiler provides for these purposes.
Automatic Memory Management
A product type declaration is a reference object with automatic memory management unless otherwise specified. An example will use a declaration of a product type called "MemoryDemo1":
This compiles and runs a simple function that creates an instance. It extracts an element that is returned. When the function exits, the instance is no longer used and will be automatically freed:
This loads a utility package that has a function to check memory usage:
This runs the function and returns True, which confirms that memory is being cleaned up:
Note that when a memory-managed product type is freed, any memory-managed types that it contains as arguments are also freed.
CompilerCallback
When an object is freed, it is possible to set up a callback called to free up resources held by an object. This can be useful if the object holds something that needs specific action on freeing, for example, a reference to something allocated in an external library.
The following example shows how this can track when an object is freed. It uses the following declaration for a product type called "MemoryDemo1":
A declaration of the callback function is needed. This is declared for the "MemoryDemo1" type, and this function will be used instead of a generic declaration:
Compile a function that creates and discards an instance of the type:
When the function is called, an instance of "MemoryDemo1" is created. When the function finishes, the instance is freed. This calls the callback, and the Print statement is executed:
A more typical usage of memory callbacks would be to free a resource. In this example, the object contains a block of memory. This is allocated when the object is created, and it is freed when the object is freed. This example uses the C memory allocation functions "malloc" and "free" just for demonstration. It could instead use the Wolfram Compiler functions CreateTypeInstance and DeleteObject:
This declares the creator and the freeing callback:
Here a call to the creator is compiled:
When the function is called, the instance of "MemoryDemo2" is created and then freed. When it is created, the "CArray" data is also created, and when it is freed, the data is also freed:
Manual Memory Management
Certain types, for example, "CArray" and "RawPointer", require manual memory management. Instances can be created by the memory allocator with CreateTypeInstance, but they need to be freed with DeleteObject.
The following example compiles functions for creating a raw pointer with dynamic memory, reading a value and deleting it:
Create the raw pointer. It has an expression serialization, so can be returned to top level:
If you read an element from it, it will probably be some random value:
There is a top-level function to write data into the raw pointer:
Finally, the value can be deleted:
Typically, this type of manual work is not very satisfactory. It would be much better to wrap the pointer in "Managed", as shown in a later section.
Unmanaged Product Types
You can declare product types that do not have automatic memory management, as in the following declaration:
This compiles a function that creates an instance of the type. It needs to call DeleteObject explicitly:
This loads a utility package that has a function to check memory usage:
This runs the function and returns True, which confirms that memory is being cleaned up:
Note that instances of "UnmanagedDemo" are references, like all product types that use reference semantics. They can be passed around efficiently; they can also be shared in different places (if care is taken). Freeing them by calling DeleteObject explicitly is not very convenient. The object could be used with the "Managed" type (as shown below). It could also be used inside some other type that has automatic memory management and then freed by using a callback.
Stack Allocation
A raw pointer is a memory address. An instance can be created with or without dynamic memory allocation. Creation on the program stack that does not involve allocation is done by ToRawPointer.
An example is shown below. First, there is a declaration of a function that uses a raw pointer; this uses ToRawPointer to store the value 10 in the pointer:
This is a function that calls the user of the pointer. It creates the raw pointer on the stack, passes it to the user of the pointer and then extracts the value with FromRawPointer:
When the function is called, the value that was stored is returned:
The advantage of this approach is that the pointer is created with stack allocation, which is faster than dynamic allocation. It also means that there is no need to free the memory. The disadvantage is that if the pointer was returned from the function where it was created, this will lead to an error.
Stack Allocation and Inlining
If you want to write a utility function that creates a pointer and you want to use ToRawPointer, you can do this by ensuring that the function you create is always inlined. This can be done as follows, where the declaration uses the "Inline" setting of "Always":
This is a function that uses the pointer creation function:
You can see the underlying code that is created. The creation function is always inlined, so the stack allocation is safe:
Function inlining was discussed in the section on Function Declarations.
Managed
If you want to work with objects that are not memory managed, this can be done with "Managed". This is particularly useful for raw types such as "CArray" or "RawPointer".
The following function creates a "CArray" of "UnsignedInteger8", filling in the data to make a zero-terminated C-style string. It calls a function in the C runtime library and returns its result:
This gives the expected result:
This loads a utility package that has a function to check memory usage:
Unfortunately, memory is being lost. This is because memory was dynamically allocated but was not freed:
It would be possible to make a call to DeleteObject to reclaim the memory, but this is not very convenient. An alternative is to use the "Managed" type to wrap the raw type. Note how the managed wrapper is passed around; it works with ToRawPointer. When passed to the library function, it unwraps to get out the raw array. However, the managed object frees the array when it passes out of scope:
This gives the expected result:
Now the result is True because memory is collected:
The default usage of managed objects is to call DeleteObject, but it is possible to create them with a custom function for when the managed object is freed. An example of this follows:
This gives the expected result:
Extracting from a Managed Object
A few functions such as LibraryFunction have special code to automatically accept a managed object as the object it holds. However, this is not generally the case. In the following, the extractFirst function expects a "CString" but gets a managed argument and does not compile:

However, the object that is managed can be borrowed using the "BorrowValue" operation as in the following:
Note that when something is borrowed, the code that accepts it cannot keep it for longer than the original managed object.
Unmanaging a Managed Object
It is possible to extract the value from a managed object so it can be used for longer than the object. This is done with the "UnmanageObject" operation.
The following declaration takes a "String" and casts it into a managed "CString". The underlying C string is then extracted by using "UnmanageObject". If "BorrowValue" was used, the C string would be collected when the function finished, but because it is unmanaged this does not happen:
This calls the declaration and casts the C string back to a "String" object:
When the function is called, it returns a string equal to its input:
This loads a utility package that has a function to check memory usage:
As expected, this leaks memory:
In general, if "UnmanageObject" is used to extract a raw object, then it is necessary to delete the raw object at some future time.
Value Objects
The examples in this section have all involved objects that are held as a reference using a memory address. Other objects are held as value objects. This includes numbers and Booleans. It also includes product types that are created with value semantics by using "ReferenceSemantics" of False. These were discussed in the section on type declarations.