Skip to main content

Object Pooling API Reference

Hyperscape implements comprehensive object pooling to eliminate GC pressure in high-frequency event loops. The combat system alone fires events every 600ms tick per combatant, which would cause significant memory churn without pooling.

Overview

Location: packages/shared/src/utils/pools/ Core Infrastructure:
  • EventPayloadPool.ts: Factory for creating type-safe event payload pools with automatic growth and leak detection
  • PositionPool.ts: Pool for {x, y, z} position objects with helper methods
  • CombatEventPools.ts: Pre-configured pools for all combat events with optimized sizes
  • TilePool.ts: Pool for tile coordinate objects used in pathfinding
  • QuaternionPool.ts: Pool for quaternion objects used in rotation calculations
  • EntityPool.ts: Pool for entity instances to reduce allocation overhead

Event Payload Pools

Basic Usage

CRITICAL: Event listeners MUST call release() after processing. Failure to release causes pool exhaustion and memory leaks.

Available Combat Event Pools

Pool Configuration

Pools are configured with:
  • Initial size: Pre-allocated objects on pool creation
  • Growth size: Objects added when pool is exhausted
  • Leak detection: Warns when payloads not released at end of tick
  • Statistics tracking: Acquire/release counts, peak usage, leak warnings

Monitoring

Global Registry

All pools are registered with a global registry for centralized monitoring:

Creating Custom Event Pools

When adding new high-frequency events, create a pool to eliminate allocations:

Pool Configuration Options

Best Practices

  1. Set initialSize based on expected concurrent usage
    • Example: Max concurrent combatants = 64 → initialSize: 64
    • Prevents pool exhaustion during normal gameplay
  2. Set growthSize to ~50% of initialSize
    • Balanced growth without excessive allocation
    • Example: initialSize: 64 → growthSize: 32
  3. Always register pools with eventPayloadPoolRegistry
    • Enables centralized monitoring
    • Helps detect leaks across all pools
  4. Use descriptive names
    • Makes debugging easier
    • Shows up in leak warnings and statistics
  5. Call checkLeaks() at the end of each game tick
    • Detects unreleased payloads early
    • Prevents memory leaks from accumulating

Position Pool

Pre-configured pool for 3D position objects.

Usage

API

Features

  • O(1) acquire/release operations
  • Zero allocations after warmup
  • Automatic pool growth when exhausted
  • Helper methods for common operations
  • Statistics tracking for monitoring

Tile Pool

Pool for tile coordinate objects used in pathfinding.

Usage

API

Quaternion Pool

Pool for quaternion objects used in rotation calculations.

Usage

Performance Impact

Before Object Pooling

After Object Pooling

Benchmarks

60-second stress test with 10 agents in combat: Verified in:
  • packages/shared/src/systems/shared/combat/__tests__/CombatSystemPerformance.test.ts
  • Memory stays flat during 60s stress test with agents in combat
  • Zero-allocation event emission in CombatSystem and CombatTickProcessor

Leak Detection

How It Works

Pools track acquired payloads and warn when they’re not released at the end of a game tick:

Warning Output

After 10 warnings, further warnings are suppressed to avoid log spam.

Debugging Leaks

  1. Check event listeners - Ensure all listeners call release()
  2. Check error paths - Release payloads even when errors occur
  3. Use try/finally - Guarantee release even on exceptions

Disabling Leak Detection

For specific pools where leak warnings are expected (e.g., long-lived payloads):

Pool Statistics

Statistics Interface

Getting Statistics

Monitoring Pool Health

Healthy pool:
  • inUse < total (not exhausted)
  • available > 0 (objects available)
  • leakWarnings === 0 (no leaks detected)
  • acquireCount === releaseCount (balanced acquire/release)
Warning signs:
  • inUse === total (pool exhausted, will auto-grow)
  • leakWarnings > 0 (payloads not being released)
  • acquireCount > releaseCount (leak accumulating)
  • Frequent auto-growth warnings in logs

Advanced Usage

Auto-Release Pattern

Use withPayload() for automatic release:

Pool Reset

Reset a pool to initial state (clears all statistics):
Warning: Only reset pools during shutdown or test cleanup. Resetting an active pool can cause crashes if payloads are still in use.

Custom Pool Sizes

Override default sizes for specific use cases:

Integration with Game Systems

Combat System Integration

The CombatSystem uses pools for all event emissions:

Event Listener Integration

All combat event listeners must release payloads:

Tick-End Leak Detection

Call checkAllLeaks() at the end of each game tick:

Troubleshooting

Pool Exhaustion Warnings

Cause: More concurrent events than initial pool size Solutions:
  1. Increase initial size if this happens frequently
  2. Check for leaks - payloads may not be released
  3. Optimize event frequency - reduce unnecessary events

Memory Leaks

Cause: Event listeners not calling release() Solutions:
  1. Find missing release() calls - search for event listeners
  2. Use try/finally - guarantee release even on errors
  3. Use withPayload() - automatic release pattern
  4. Check error paths - ensure release on all code paths

Performance Degradation

Symptoms:
  • Increasing memory usage over time
  • Frequent GC pauses
  • Frame drops during combat
Diagnosis:
Solutions:
  1. Fix leaks (acquire count should equal release count)
  2. Increase pool sizes if exhaustion is frequent
  3. Reduce event frequency if possible

Migration Guide

Converting Existing Code to Use Pools

Before (allocates on every event):
After (uses pool):

Checklist

  • Create pool with createEventPayloadPool()
  • Register pool with eventPayloadPoolRegistry
  • Replace emit() with acquire() + emitTypedEvent()
  • Add release() calls to all listeners
  • Add try/finally blocks for error safety
  • Test for leaks with checkAllLeaks()
  • Monitor statistics with getStats()

References

  • Source Code: packages/shared/src/utils/pools/
  • Tests: packages/shared/src/utils/pools/__tests__/
  • Performance Tests: packages/shared/src/systems/shared/combat/__tests__/CombatSystemPerformance.test.ts
  • Integration: packages/shared/src/systems/shared/combat/CombatSystem.ts