Object Pooling

Read time 6 minutes Package: Nalix.Framework
On this page

Namespace: Nalix.Framework.Memory.Objects (and Nalix.Framework.Options)
Assembly: Nalix.Framework

ObjectPoolManager ([Injectable(typeof(IObjectPoolManager))] public sealed partial class ObjectPoolManager : IObjectPoolManager, IDisposable) manages reusable class instances implementing IPoolable across the Nalix stack (packets, PacketContext<T>, PacketSender, ObjectMap<K,V>, BufferLease shells, and socket event args).

Object Pool Architecture

Architecture Diagram
flowchart TD
    Req(["ObjectPoolManager.Get<T>()"]) --> TypeId["Lookup PoolType<T>.Id (Flat Array _pools[id])"]
    TypeId --> TLC{"ThreadCacheDepth > 0 & Slot Has Item?"}
    TLC -->|Yes| HitTLC["Pop from ThreadLocalCache<T>"]
    TLC -->|No| Bucket{"TypePool Stack Has Item?"}
    Bucket -->|Yes| HitBucket["Pop from TypePool Stack"]
    Bucket -->|No| Create["Create via new T() (Cache Miss)"]

    HitTLC --> RentHook{"T is IPoolRentable?"}
    HitBucket --> RentHook
    Create --> RentHook
    RentHook -->|Yes| CallOnRent["Call rentable.OnRent()"] --> Ret(["Return T"])
    RentHook -->|No| Ret

    RetCall(["ObjectPoolManager.Return<T>(obj)"]) --> Reset["obj.ResetForPool() inside TypePool/ThreadLocalCache"]
    Reset --> Store["Store in ThreadLocalCache (if enabled) or TypePool up to MaxCapacity"]

Source Mapping

  • src/Nalix.Abstractions/IPoolable.cs
  • src/Nalix.Abstractions/IObjectPoolManager.cs
  • src/Nalix.Framework/Memory/Objects/ObjectPoolManager.cs
  • src/Nalix.Framework/Memory/Objects/ObjectPoolManager.Cleanup.cs
  • src/Nalix.Framework/Memory/Objects/ObjectPoolManager.Report.cs
  • src/Nalix.Framework/Memory/Objects/TypedObjectPool.cs
  • src/Nalix.Framework/Options/ObjectPoolOptions.cs

Core Contracts (IPoolable & IPoolRentable)

namespace Nalix.Abstractions;

public interface IPoolable
{
    void ResetForPool();
}

public interface IPoolRentable
{
    void OnRent();
}
  • IPoolable.ResetForPool(): Invoked automatically when an object is returned to the pool (Return<T>(obj)). Implementations must clear references and reset mutable fields.
  • IPoolRentable.OnRent(): Optional hook invoked by ObjectPoolManager.Get<T>() right before returning the rented instance to the caller.
  • Double-Return Protection (IPoolStateTracked): Objects implementing IPoolStateTracked (MarkRented() / TryMarkReturned()) are checked on Return<T>(obj) so duplicate returns are safely rejected (TotalRejectedReturnCount).

ObjectPoolManager API Reference

Static Configuration

MemberSignatureDescription
Sharedpublic static ObjectPoolManager Shared { get; }Returns the globally configured ObjectPoolManager (throws InvalidOperationException if accessed before Configure is called).
Configurepublic static void Configure(ObjectPoolManager manager)Sets the process-wide ObjectPoolManager.Shared instance (wired automatically by NetworkApplicationBuilder).

Constructors, Properties & Methods

MemberSignatureDescription
ObjectPoolManager()public ObjectPoolManager()Loads ObjectPoolOptions from ConfigurationManager.Instance and schedules the recurring obj.trim job if EnableObjectTrimming is true.
ObjectPoolManager(ObjectPoolOptions)public ObjectPoolManager(ObjectPoolOptions config)Initializes the pool manager with explicit ObjectPoolOptions.
PoolCount / PeakPoolCountintCurrent and peak number of distinct type pools managed.
TotalGetOperations / TotalReturnOperationslongLifetime Get<T>() and Return<T>(obj) operation counters.
TotalCacheHits / TotalCacheMisseslongTotal pool hits versus new allocations (new T()).
CacheHitRatedoubleOverall cache hit rate percentage (0.0..100.0).
UptimeTimeSpanElapsed time since the manager was created or ResetStatistics() was called.
UnhealthyPoolCountintNumber of pools flagged as unhealthy during the most recent PerformHealthCheck().
Get<T>()public T Get<T>() where T : IPoolable, new()Rents or creates an instance of T via PoolType<T>.Id flat-array lookup (preallocates DefaultPreallocate items on first access to a new type).
Return<T>(T)public void Return<T>(T obj) where T : IPoolableResets obj (ResetForPool()) and returns it to the pool (throws ArgumentNullException if obj is null).
GetTypedPool<T>()public TypedObjectPool<T> GetTypedPool<T>() where T : IPoolable, new()Returns a TypedObjectPool<T> adapter bound to this manager.
Prealloc<T>(int)public int Prealloc<T>(int count) where T : IPoolable, new()Preallocates count instances into T’s pool (count > 0) and returns the number of instances added.
SetMaxCapacity<T>(int)public bool SetMaxCapacity<T>(int maxCapacity) where T : IPoolableSets the maximum retained capacity for T’s pool (maxCapacity >= 0).
GetTypeInfo<T>()public Dictionary<string, object> GetTypeInfo<T>() where T : IPoolableReturns a dictionary of metrics (AvailableCount, MaxCapacity, TotalGets, TotalReturns, CacheHitRate, Outstanding, PeakOutstanding, Status, and lifetime percentiles when EnableDiagnostics = true).
ClearPool<T>() / ClearAllPools()public int ClearPool<T>() / public int ClearAllPools()Purges pooled instances and returns the number of removed objects.
PerformHealthCheck()public int PerformHealthCheck()Evaluates window miss rates across active pools and returns the number of unhealthy pools.
ResetMetrics() / ResetStatistics()public void ResetMetrics() / public void ResetStatistics()Resets global and per-pool counters.
GenerateReport() / WriteReportDatapublic string GenerateReport() / public void WriteReportData(Utf8JsonWriter writer)Produces human-readable text or structured JSON reports.

ObjectPoolOptions (Nalix.Framework.Options)

Configured via [ObjectPool] in default.ini or builder.Configure<ObjectPoolOptions>(...):

PropertyTypeDefaultValidationDescription
EnableDiagnosticsboolfalse—Enables PoolSentinel tracking for object lifetimes (Avg/p95/Max) and suspicious long-lived rentals.
EnableMetricsbooltrue—Enables lightweight atomic hit/miss and Outstanding counters (safe for production).
CaptureStackTracesboolfalse—Captures allocation stack traces on Get<T>() when EnableDiagnostics = true (expensive; use only when debugging leaks).
SuspiciousThresholdSecondsint30[ValueRange(0, 3600)]Threshold in seconds after which an unreturned object is flagged as suspicious.
EnableLeakDetectionboolfalse—Uses PoolSentinel finalizers to detect rented objects garbage-collected without Return<T>() (requires EnableDiagnostics = true so PoolSentinel instances are attached on Get<T>()).
LifetimeReservoirSizeint64[ValueRange(16, 1024)]Number of recent rental duration samples kept per pool for p95LifetimeMs calculation.
EnableObjectTrimmingbooltrue—Schedules a recurring TaskManager job (obj.trim) to release idle pooled objects.
TrimIntervalMinutesint5[ValueRange(1, 60)]Interval in minutes between normal pool trim cycles.
DeepTrimIntervalMinutesint30[ValueRange(5, 120)]Interval in minutes between deep/aggressive pool trim cycles.
BaseKeepPercentageint75[ValueRange(50, 90)]Percentage of capacity retained during normal trim cycles.
DeepTrimPercentageint25[ValueRange(10, 45)]Percentage of capacity retained during deep trim cycles.
TrimDecayFactordouble0.5[ValueRange(0.1, 1.0)]Fraction of excess objects removed per trim cycle (exponential decay).
HotHitRateThresholddouble85.0[ValueRange(75.0, 98.0)]Hit-rate percentage above which a pool is treated as “hot” and trimmed lightly.
MinimumKeepObjectsint8[ValueRange(4, 64)]Minimum floor of objects always kept in each pool during trimming.
DefaultMaxPoolSizeint2048[ValueRange(1024, 1_000_000)]Default maximum capacity for newly created type pools.
DefaultPreallocateint8[ValueRange(0, 1_000_000)]Number of instances preallocated when a type pool is first created.
ThreadCacheDepthint0[ValueRange(0, 4)]Per-thread ThreadLocalCache<T> depth (0 = disabled by default so async/await continuations across ThreadPool threads do not strand pooled objects).