Memory management

Met.3D uses a lease-based memory management system to keep frequently used data in memory while evicting stale items when memory pressure rises.

Overview

Data sources do not manage the lifetime of their produced items directly. Instead, every data item is handed to a memory manager via store(), which immediately returns a lease to the data item, see MDataLease. As long as at least one lease referencing an item is alive, the memory manager guarantees that the item will not be evicted. This means there is no window between item creation and first use during which the item could disappear. When all leases for an item are destroyed, the item becomes eligible for eviction according to the policy implemented by the concrete memory manager.

The Memory Manager

MAbstractMemoryManager is the abstract base class for all memory managers. It defines the two primary operations:

  • store(key, item): Hands a freshly produced data item to the memory manager and returns a MDataLease that pins the item immediately. The caller owns the returned lease; the item is guaranteed alive for its lifetime.

  • acquire(key): Looks up an already-cached item by its key and returns a new lease pinning it. Returns an invalid lease if the key is not found.

The base class also exposes isCached(key) to check for existence without acquiring a pin. Note that a race condition might occur if you call isCached(key) before acquire(key), as the item could be evicted between the two calls. Call acquire(key) directly and check whether the returned lease isValid().

Concrete subclasses implement the eviction policy by overriding the pin(), unpin(), and evictToMakeSpace() methods.

The MLRUMemoryManager

MLRUMemoryManager is the standard implementation, applying a Least Recently Used eviction policy. It maintains two data structures:

  • A QMap<MMemoryKey, CacheEntry> that stores all items, both pinned and evictable. Each CacheEntry holds the MMemoryManagerItem control block, the last-access timestamp, and an iterator into the LRU list below.

  • A std::list<MMemoryKey> (evictableKeys) that orders evictable items from most recently used (front) to least recently used (back). The iterator stored in each CacheEntry allows O(1) removal from any position in this list. Pinned items use evictableKeys.end() as a sentinel to indicate they are not in the eviction list.

The memory limit is set at construction time. Attempting to store an item that exceeds the total capacity when no further items can be evicted (all have active leases) throws MMemoryError.

MDataLease and MDataLeaseBase

MDataLeaseBase is an RAII handle representing a pin on a cached item. Constructing or copying a lease increments the item’s pin count; destroying it decrements the count. When the count reaches zero, the item becomes evictable. The templated subclass MDataLease<T> adds typed access via operator->, providing access to the underlying data item. The parameter T must be a MAbstractDataItem. The typeless base lease provides type elision.

Typically, you will get the data lease from the data source producing the data item, using dataSource->getData(key). This will already return a typed lease. If you need to obtain a typed lease from an untyped MDataLeaseBase, use dynamic_lease_cast<T>, similar to std::dynamic_pointer_cast<T>:

1 MDataLeaseBase base = memoryManager->acquire(key);
2 MDataLease<MStructuredGrid> lease = dynamic_lease_cast<MStructuredGrid>(base);
3 if (lease) { /* safe to use lease->... */ }

MMemoryManagerItem

MMemoryManagerItem is the internal control block that the memory manager creates for each stored item. It holds the raw pointer to the data item, the current pin count (lease count), and the key. The control block is reference-counted via shared_ptr and outlives the data item itself: this is required because an item may be force-deleted when its producing data source is destroyed. After such a force deletion the lease remains safe to access, but isValid() returns false, allowing callers to detect the situation without a dangling pointer.

participant A as "Actor"
participant DS as "DataSource"
participant MM as "MemoryManager"

DS->>DS: create data item
DS->>MM: store(key, item)
MM-->>DS: DataLease [refcount=1]
Note over MM: Item pinned while any lease alive

A->>MM: acquire(key)
MM-->>A: DataLease [refcount=2]

DS->>MM: ~DataLease() [refcount=1]
A->>MM: ~DataLease() [refcount=0]
Note over MM: Item now evictable
MM->>MM: remove item\non memory pressure

Fig. 41 Sequence diagram depicting exemplary lifetime of a memory managed data item.