Skip to main content

Memory Cleanup for Long-Running Apps

As of PR #1558, Session.close() automatically calls memory.close_connections(), which in turn calls .close() on the active memory adapter. Most users no longer need manual cleanup hooks for basic memory management.
Long-running multi-agent applications can accumulate memory over time, leading to performance degradation and potential crashes. This guide covers best practices for effective memory management.

Understanding Memory Issues

Common Memory Problems

  1. Memory Leaks: Unreleased references to objects
  2. Conversation History Accumulation: Growing chat histories
  3. Cache Overflow: Unbounded caching
  4. Circular References: Objects referencing each other
  5. Resource Handles: Unclosed files, connections, etc.

Bounding In-Memory Growth

For long-running agents, prevent unbounded memory growth using the InMemoryAdapter with size limits:
The existing helper class ResourcePool in this page with max_size=10 is unrelated to the new InMemoryAdapter(max_size=...) parameter. They serve different purposes: ResourcePool manages connection pools, while InMemoryAdapter manages memory entry limits.

Memory Management Strategies

1. Conversation History Management

Implement sliding window or summary-based history management:

2. Agent Memory Management

Memory construction is now thread-safe and async-safe. Concurrent Tasks sharing a memory_config will coordinate through locks rather than each creating duplicate stores.
Implement memory limits and cleanup for agents:

3. Resource Pool Management

Implement resource pooling to prevent resource exhaustion:

4. Cache Management

Implement LRU cache with memory limits:

Memory Monitoring

1. Memory Usage Tracking

2. Automatic Garbage Collection

3. Agent Garbage Collection Safety Net

Since PR #1514, Agent.__del__ runs a best-effort close_connections() during garbage collection as a safety net. However, this may be skipped by the Python interpreter and must not be relied upon. Always use explicit cleanup:

Profiler buffers are bounded

Enabling Profiler no longer leaks memory in long-running processes. All record lists are now deque(maxlen=PRAISONAI_PROFILE_MAX) (default 10,000) which fixes the prior class-level List[...] storage that grew unbounded. Key improvements:
  • Memory-safe: Fixed maximum memory usage per buffer
  • Ring buffer: Only keeps the most recent N records
  • Configurable: Set PRAISONAI_PROFILE_MAX environment variable
  • Production-ready: Safe for long-running agents
For more details on profiling configuration and best practices, see Profiling.

Best Practices

1. Use Context Managers

Always use context managers for resource management:

2. Implement Memory Budgets

Set memory budgets for different components:

3. Profile Memory Usage

Regular profiling helps identify memory issues:

Common Pitfalls

  1. Unbounded Collections: Always set limits on collections
  2. Circular References: Use weak references where appropriate
  3. Global State: Minimize global state that accumulates data
  4. Event Listeners: Always unregister event listeners
  5. Thread Local Storage: Clean up thread-local data

Testing Memory Management

Conclusion

Effective memory management is crucial for long-running multi-agent applications. By implementing proper cleanup strategies, monitoring, and resource management, you can ensure your applications remain stable and performant over extended periods.