TEngine Memory Pool Internal Implementation: A Deep Dive into the Generic Object Pool Architecture
TEngine implements a thread-safe, generic object pool through a static MemoryPool facade that manages type-specific MemoryCollection queues to minimize GC pressure in Unity applications.
The alex-rachel/tengine repository provides a high-performance TEngine memory pool designed for Unity game development. This system eliminates runtime allocations for short-lived objects by recycling instances through a centralized, type-safe pooling mechanism.
Core Architecture Components
The TEngine memory pool architecture consists of four primary components working in concert to provide object lifecycle management.
MemoryPool Static Facade
The entry point for all pooling operations resides in UnityProject/Assets/TEngine/Runtime/Core/MemoryPool/MemoryPool.cs. This static partial class exposes the public API including Acquire<T>(), Release(IMemory), and bulk operations like Add(), Remove(), and ClearAll().
At its core, the facade maintains a process-wide dictionary:
private static readonly Dictionary<Type, MemoryCollection> _memoryCollections
= new Dictionary<Type, MemoryCollection>();
MemoryCollection Per-Type Queues
Each pooled type receives a dedicated MemoryCollection instance, implemented in UnityProject/Assets/TEngine/Runtime/Core/MemoryPool/MemoryPool.MemoryCollection.cs. This class encapsulates a Queue<IMemory> that stores idle objects and tracks real-time usage statistics.
The collection handles the actual allocation and recycling logic. When Acquire<T>() is called, the collection either dequeues an existing instance or constructs a new one via new T().
MemoryPoolInfo Statistics DTO
Diagnostic visibility comes from UnityProject/Assets/TEngine/Runtime/Core/MemoryPool/MemoryPoolInfo.cs. This struct captures per-type metrics including:
UnusedMemoryCount: Items waiting in the queueUsingMemoryCount: Items currently checked outAcquireMemoryCount: Total acquisitions performedReleaseMemoryCount: Total releases performedAddMemoryCount: Total objects createdRemoveMemoryCount: Total objects removed
Thread Safety and Synchronization
The TEngine memory pool implements comprehensive thread safety for concurrent environments. Access to the global _memoryCollections dictionary is guarded by a lock statement, ensuring safe lazy initialization of MemoryCollection instances.
Within each MemoryCollection, the internal _memories queue receives independent synchronization via lock (_memories). This dual-locking strategy allows safe concurrent Acquire and Release operations across Unity's main thread and background tasks without cross-type contention.
Strict-Check Validation Mode
The system provides an optional EnableStrictCheck mode configurable through UnityProject/Assets/TEngine/Runtime/Core/MemoryPool/MemoryPoolSetting.cs. When activated, the pool performs runtime validation through InternalCheckMemoryType and the Release method.
Strict-check mode validates that:
- The requested type is a non-abstract class implementing
IMemory - Released instances are not already present in the queue (preventing double-release errors)
Core API Operations
Acquiring Objects
The Acquire<T>() method in MemoryPool.cs delegates to the type-specific collection:
public static T Acquire<T>() where T : class, IMemory, new()
{
return GetMemoryCollection(typeof(T)).Acquire<T>();
}
The collection implementation either recycles an existing instance or allocates a new one, updating UsingMemoryCount and AcquireMemoryCount accordingly.
Releasing Objects
The Release(IMemory memory) method validates the argument, determines the concrete type, and forwards to the appropriate collection:
public static void Release(IMemory memory)
{
// Validation and type resolution...
GetMemoryCollection(type).Release(memory);
}
The collection clears the object via memory.Clear(), optionally checks for duplicates in strict mode, enqueues the instance, and updates release statistics.
Bulk Operations
The pool supports batch management through Add<T>(int count) and Remove<T>(int count) methods. Add pre-populates the pool with new instances, useful for warm-up phases. Remove dequeues and discards specified quantities of idle objects, reducing memory footprint during low-usage periods.
ClearAll() iterates every MemoryCollection, calls RemoveAll(), and clears the global dictionary, effectively resetting the entire pool state.
Performance Monitoring and Debugging
The GetAllMemoryPoolInfos() method aggregates statistics from all collections into MemoryPoolInfo arrays. This data feeds the Memory Pool tab in TEngine's built-in debugger window (DebuggerModule.MemoryPoolInformationWindow), enabling runtime visualization of:
- Memory leaks (indicated by perpetually high
UsingMemoryCount) - Over-allocation (high
AddMemoryCountrelative to actual usage) - Pool efficiency (ratio of
AcquireMemoryCounttoAddMemoryCount)
Implementation Example
Any class implementing IMemory (requiring a Clear() method) can utilize the pool:
public class AssetObject : IMemory
{
public string AssetName { get; set; }
public int ReferenceCount { get; set; }
public void Clear()
{
AssetName = null;
ReferenceCount = 0;
}
}
// Warm-up
MemoryPool.Add<AssetObject>(100);
// Runtime usage
AssetObject obj = MemoryPool.Acquire<AssetObject>();
obj.AssetName = "HeroTexture";
// ... use object ...
MemoryPool.Release(obj);
// Cleanup
MemoryPool.ClearAll();
Summary
- TEngine memory pool uses a static
MemoryPoolfacade with type-specificMemoryCollectionqueues to recycle objects implementingIMemory. - Thread safety is achieved through dual-level locking on the global dictionary and individual collection queues.
- Strict-check mode provides runtime validation against double-releases and invalid type requests.
- Comprehensive statistics via
MemoryPoolInfoenable leak detection and performance tuning through the built-in debugger. - The implementation resides primarily in
MemoryPool.csandMemoryPool.MemoryCollection.cswithin thealex-rachel/tenginerepository.
Frequently Asked Questions
What types of objects can be pooled in TEngine?
Any non-abstract class implementing the IMemory interface can be pooled. This interface requires a single Clear() method that resets the object's state upon release. Common use cases include data transfer objects like AssetObject, state machines such as Fsm<T>, and temporary containers for loading operations.
How does TEngine prevent memory leaks in the memory pool?
The pool tracks UsingMemoryCount for each type, representing objects currently acquired but not released. If this count grows indefinitely while Release calls remain low, the debugger's Memory Pool tab will highlight the leak. Additionally, strict-check mode validates that released instances aren't already in the idle queue, preventing logic errors that could corrupt pool state.
Is the TEngine memory pool thread-safe for concurrent access?
Yes. The implementation uses two levels of synchronization: a global lock protects the Dictionary<Type, MemoryCollection> in MemoryPool.cs, while each MemoryCollection maintains its own lock on the internal Queue<IMemory>. This design allows different types to be acquired and released concurrently without contention, while ensuring thread safety for operations on the same type.
What is the performance overhead of using strict-check mode?
Strict-check mode adds runtime validation overhead by checking type constraints in InternalCheckMemoryType and scanning the queue for duplicate instances during Release. While negligible for development and debugging builds, these operations involve reflection and linear queue searches that could impact performance in high-frequency allocation scenarios. Production builds typically disable strict-check mode via MemoryPoolSetting to maximize throughput.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →