Roblox-TS object pooling system with configurable allocation strategies for efficient object management.
npm install @rbxts/allocatorRoblox-TS object pooling system with configurable allocation strategies for efficient object management.
- Four Allocation Strategies:
- Unbounded: Infinite growth for high-intensity scenarios
- Elastic: Temporary expansion with automatic shrinkage
- Fixed: Strict reuse of initial pool
- FailSilent: Silent failure when exhausted
- Thread-Safe Disposal: Uses Roblox events for cross-thread cleanup
- Lifecycle Hooks: Full control over creation, activation, and destruction
- Diagnostic Tracking: Creation IDs and usage counters for debugging
``bash
#npm
npm install @rbxts/allocator
#bun
bun add @rbxts/allocator
`
`ts
import { Workspace } from "@rbxts/services";
import { EObjectPoolType, ObjectPool } from "@rbxts/allocator";
interface IPartData {
Part: BasePart;
Thread?: thread;
}
class PartObjectPool extends ObjectPool
protected CreateObj(): IPartData {
return identity
}
protected StartObj(value: IPartData, start_data: Color3, dispose: () => void): void {
value.Part.Position = new Vector3(0, 100, 0);
value.Part.Parent = Workspace;
value.Part.Color = start_data;
value.Thread = task.delay(3, dispose);
}
protected DisposeObj(value: IPartData, safe_cancel_thread: (t?: thread) => void): void {
value.Part.Parent = undefined;
safe_cancel_thread(value.Thread);
}
protected DestroyObj(value: IPartData): void {
print("Destroyed");
value.Part.Destroy();
}
}
const part_object_pool = new PartObjectPool(15, EObjectPoolType.Elastic);
for (const i of $range(0, 200)) {
task.wait(0.2);
part_object_pool.UseObject(new Color3(math.random(), math.random(), math.random()));
}
`
The pool uses lazy initialization - objects are only created when first needed:
`ts
// No objects are created at this point
const pool = new PartObjectPool(15, EObjectPoolType.Elastic);
// First call to UseObject() triggers initialization
pool.UseObject(new Color3(1, 0, 0));
`
If you need to pre-initialize the pool or need parameters before initialization, you can call the protected Init() method in your constructor:
`ts
class CustomPool extends ObjectPool
constructor(size: number, type: EObjectPoolType, customParam: string) {
super(size, type);
this.customParam = customParam;
// Initialize pool immediately instead of on first UseObject()
this.Init();
}
// ...implementation of abstract methods
}
`
Benefits:
- Lazy initialization delays resource allocation until needed
- Explicit initialization gives control when needed
- Supports constructor parameters needed for object creation
`ts`
new ObjectPool(initialSize: number, strategy: EObjectPoolType);
#### Abstract Methods
| Method | Responsibility | Timing |
| --------------------------------------- | --------------------- | ------------------------ |
| CreateObj() | Instance construction | Pool initialization |StartObj(value, data, dispose)
| | Activate instance | On UseObject() call |DisposeObj(value, safe_cancel_thread)
| | Deactivate instance | Before reuse/destruction |DestroyObj(value)
| | Cleanup resources | When pool shrinks |
#### Public Methods
| Method | Description |
| ----------------- | ------------------------------------------- |
| UseObject(data) | Activates an object from the pool with data |Destroy()
| | Destroys all objects and cleans up the pool |
#### Fixed Pool
`ts`
// For memory-critical systems where exceeding the initial size is not allowed
const fixedPool = new PartObjectPool(10, EObjectPoolType.Fixed);
// When all 10 items are in use, the oldest active item will be recycled
#### Unbounded Pool
`ts`
// For high-demand scenarios where performance is critical
const unboundedPool = new PartObjectPool(5, EObjectPoolType.Unbounded);
// Will create new instances indefinitely as needed
#### FailSilent Pool
`ts`
// For optional visual effects that aren't critical
const failSilentPool = new PartObjectPool(20, EObjectPoolType.FailSilent);
// Returns undefined without allocation when pool is exhausted
The object pool provides debugging information through:
- Creation IDs: Unique identifier for each created object
- Usage counters: Track how many times an object has been used
- FailSilent: Returns undefined when the pool is exhaustedFixed
- : Recycles the oldest active object when pool is exhaustedDestroy()
- : Safely cleans up all resources when you're done
For simpler use cases where you don't need the full lifecycle management, you can use ManualObjectPool:
`ts
import { EObjectPoolType, ManualObjectPool } from "@rbxts/allocator";
const partPool = new ManualObjectPool(
10, // initial size
EObjectPoolType.Elastic,
() => new Instance("Part"), // create function
(part) => part.Destroy(), // destroy function
);
// Get an object from the pool
const part = partPool.UseObj();
if (part) {
//usually check can be avoided if the pool type is not FailSilent
part.Parent = Workspace;
// ... use the part
// Return it to the pool when done
partPool.FreeObj(part);
}
// Clean up when done
partPool.Destroy();
`
#### ManualObjectPool Methods
| Method | Description |
| -------------- | ------------------------------------------- |
| UseObj() | Gets an object from the pool |FreeObj(obj)
| | Returns an object to the pool |Destroy()` | Destroys all objects and cleans up the pool |
|
| Strategy | Allocation Speed | Memory Usage |
| ---------- | ----------------- | ---------------- |
| Unbounded | ⚡ Instant | 📈 Linear growth |
| Elastic | ⚡ Instant (temp) | ↔️ Controlled |
| Fixed | ⚡ Fast (reuse) | ✅ Fixed |
| FailSilent | ⚡ Instant | ✅ Fixed |