7.4 Memory Management

7.4.1 Memory Manager
7.4.2 Relinquishing Memory
7.4.3 Avoiding out-of-memory conditions

Visualize is a retained-mode visualization component, and system memory is allocated in order to store the contents of the scene graph and support the renderer. Since your application makes Visualize API calls to populate the scene graph, your application code is effectively responsible for the amount of memory that Visualize uses, similar to other non-Visualize data structures which are allocated by your application. While Visualize does monitor video memory usage, it does not monitor available system memory or make any attempts to limit system memory usage. It will attempt to allocate any system memory that is required as a result of your application's Visualize API calls. Note that file load operations also consist of Visualize API calls which populate the scene graph.

Your application should ideally contain its own memory management logic, which could include one or more of the following items:

7.4.1 Visualize Memory Manager

The dynamic allocation and releasing of memory in Visualize is controlled by an internal memory manager. The memory manager may allocate memory in numerous situations, such as when objects are inserted into the database, an update is performed, or files are loaded. As items are removed from the database, the memory manager takes care of the freeing of the memory associated with them. Visualize allocates memory using its own memory pool and attempts to reuse memory chunks after entities are deleted.

Tracking and management of Visualize memory usage should be handled in your application logic, and is facilitated by calling HPS::Database::ShowMemoryUsage which lets you query the amount of memory that Visualize currently has allocated, along with how much is currently being used.

7.4.2 Relinquishing memory

If you find that there is a significant gap between the amount of memory that Visualize has allocated and the amount in use, and you wish to reclaim some of that memory, you can consider asking the system to relinquish memory by calling HPS::Database::RelinquishMemory. This forces unused memory to be released back to the system, essentially overriding the normal function of the Visualize memory manager. This function should only be used in very specific situations, and also has major downsides on 32-bit systems. See the Reference Manual entry for more detailed information.

7.4.3 Avoiding out-of-memory conditions

Visualize provides an optional mechanism which allows Visualize to inform you about its memory usage, and help avoid a case where it attempts to utilize more system memory than is currently available.

The mechanism involves the concept of an HPS::EmergencyHandler. Visualize will call your custom handler's HPS::EmergencyHandler::Handle method if any emergency conditions are detected. The values in HPS::Emergency::Code describe the the various types of conditions that can cause an emergency.

NOTE: Emergency memory usage situations must be handled synchronously, and therefore the asynchronous event handler mechanism and its support for errors and warnings via HPS::ErrorEvent cannot be used.

Your emergency handler must derive from HPS::EmergencyHandler. Here's an example of a custom handler:

[snippet 7.4.3.a]
class MyEmergencyHandler : public HPS::EmergencyHandler
{
void Handle(const char *, Emergency::Code code) const
{
abort_file_load(); // this must not make HPS calls directly or indirectly
delete reserve_buffer; // free previously reserved emergency buffer
else if (code == HPS::Emergency::Code::Fatal)
abort(); // this case must not return
}
public class MyEmergencyHandler : HPS.EmergencyHandler
{
public override void Handle(string message, HPS.Emergency.Code code)
{
if (code == HPS.Emergency.Code.SoftMemoryLimit)
abort_file_load(); // this must not make HPS calls directly or indirectly
else if (code == HPS.Emergency.Code.HardMemoryLimit)
delete_reserve_buffer(); // free previously reserved emergency buffer
else if (code == HPS.Emergency.Code.Fatal)
abort(); // this case must not return
}

Soft memory limit

A soft memory limit is set with HPS::Database::SetSoftMemoryLimit. When Visualize utilizes that amount of memory, it will call the HPS::EmergencyHandler. However, the soft memory limit is just a warning - Visualize will continue to allocate memory - it is up to you to cancel whatever operation your applicaton is performing.

In the sample handler above, the code is responding to the HPS::Emergency::Code::SoftMemoryLimit code by aborting a file load, since continuing with a file load might eventually result in an out-of-memory condition as more memory is allocated. If a current file load was aborted, an application would likely be able to continue safely operating on any current Visualize scene graph. The handler cannot make any Visualize API calls directly or indirectly - the appropriate approach would be to post some sort of a message (or set some flag) which the separate file loading logic would check, and then abort its loading logic. Depending on your application memory management strategy, you may wish to:

[snippet 7.4.3.b]
// set memory limit to two gigabytes
size_t memoryLimit = 2147483648;
// set memory limit to eight gigabytes
HPS.Database.SetSoftMemoryLimit(8589934592);

Hard memory limit

The HPS::Emergency::Code::HardMemoryLimit code occurs when Visualize attempts to allocate system memory and that allocation has FAILED. This is severe, and it is possible that system instability could occur. In this case, the sample custom handler in snippet 7.4.3.a elects to free a previously reserved emergency buffer. The sample code which reserved the emergency buffer is not shown, but it would consist of allocating a memory block with new. Freeing a buffer and cancelling the current operation is usually the best method to regain system stability. However, returning to stability cannot be guaranteed. A reasonable amount of memory to reserve in the general case is 128 MB, although this will vary from system to system.

If allowed to continue, Visualize will internally attempt to relinquish the unused portion of its allocated memory (do not try to call HPS::Database::RelinquishMemory yourself) before retrying the allocation. If the memory allocation after the relinquish fails, it is a fatal error.

If a fatal error occurs, the application CANNOT successfully return. The best you could do is have the application gracefully exit (and perhaps display a dialog box), which would require performing a longjmp to a point in the code previously saved by setjmp.

To enable the emergency handler created in snippet 7.4.3.a, you need to call:

[snippet 7.4.3.c]
MyEmergencyHandler myHandler;
MyEmergencyHandler myHandler = new MyEmergencyHandler();
HPS.Database.SetEmergencyHandler(myHandler);