Understanding the Problem
Unbounded Memory Growth and Slowdowns
Memory leaks in Yii-based applications are typically the result of persistent service containers, improperly scoped components, or lack of cleanup in custom handlers. Over time, these problems lead to increased latency, high memory usage, and eventual OOM (Out of Memory) kills.
Symptoms in Production
- Gradual performance degradation of queue workers or API endpoints
- Increased memory footprint over time without a restart
- PHP-FPM workers being killed or restarted due to memory limits
- Cache invalidation not occurring as expected
Architecture and Lifecycle Details
Yii Application Lifecycle
Each Yii application bootstraps components like `Yii::$app`, configuration objects, DI containers, and event handlers. In a web context, these are short-lived. But in long-running processes, stale references can persist across iterations unless explicitly released.
Dependency Injection Container (DI)
Yii's DI container caches definitions and singletons globally. If improperly scoped or reused across iterations, they can hold onto memory or objects beyond their intended lifespan.
Yii::$container->setSingleton(MyService::class, function () { return new MyService(); // Persistent until process exit });
Event Listeners and Closures
Anonymous functions registered to global events without deregistration create hidden memory retention, especially in console or daemon environments.
Diagnostics and Debugging
Enable Verbose GC Logging
Configure PHP to show garbage collection activity and detect when objects are not being collected.
ini_set('zend.enable_gc', '1'); gc_enable(); gc_collect_cycles();
Use Memory Profiling Tools
Leverage tools like Xdebug, PHP-Meminfo, or PHP Profiler to monitor heap usage and identify retained references.
Instrument Long-running Scripts
Add checkpoints inside loops or iterations to log memory usage and component lifetimes.
while (true) { echo memory_get_usage() . "\n"; // run task }
Common Pitfalls
Improper Use of Singletons
Injecting services via singleton bindings may cause them to persist longer than necessary in background processes.
Missing Cleanup in Queue Handlers
Handlers that retain state, file handles, or database connections across executions can slowly accumulate memory.
Global Event Leakage
Event handlers registered globally (`Yii::on`) need to be manually unregistered or re-scoped after use.
Yii::off(Application::EVENT_BEFORE_REQUEST, $handler);
Step-by-Step Resolution
1. Avoid Singleton Misuse
Prefer factory bindings over singleton for services that manage state, especially in looping contexts.
Yii::$container->set(MyService::class, function () { return new MyService(); // new instance each time });
2. Manually Cleanup in Long-running Tasks
Explicitly unset variables, close connections, and deregister events within worker loops.
unset($object); Yii::off(SomeEvent::class);
3. Break Down Tasks
Use subprocesses or forked jobs to run memory-intensive tasks and ensure clean state on each execution.
4. Monitor PHP-FPM Configuration
For web contexts, configure `pm.max_requests` and memory limits to force recycling of PHP workers.
5. Use Dependency Injection Scopes
Adopt scoped service lifetimes if available, or implement custom factories that return fresh instances when needed.
Best Practices
- Prefer short-lived object graphs in background processes
- Avoid global state or cross-request data persistence
- Always clean up event listeners and closures in long-running code
- Use instrumentation to track memory over time
- Test background scripts under load conditions using tools like Siege or Artillery
Conclusion
Performance degradation and memory leaks in Yii applications often arise from architectural oversights—particularly in the handling of singleton services, event listeners, and persistent process environments. Understanding Yii's lifecycle model and memory behavior is critical for building stable, production-ready systems. Through disciplined resource cleanup, smart dependency injection, and continuous profiling, developers can safeguard Yii-based systems against slowdowns and unbounded memory usage.
FAQs
1. Why does my Yii queue worker slow down over time?
It may be leaking memory through singleton services or undeleted references in event listeners or closures. Monitor memory and clean up after each job.
2. Can I use Yii's DI container in long-running scripts?
Yes, but avoid singleton usage unless necessary. Prefer per-iteration instantiation to reduce memory retention.
3. How do I track memory leaks in Yii?
Use tools like PHP-Meminfo or Xdebug along with logging memory usage inside loops. GC logs can also reveal uncollected cycles.
4. Are memory leaks common in console applications?
Yes, especially when services persist beyond their intended scope or event handlers are not cleaned up properly.
5. What Yii config helps prevent PHP-FPM crashes?
Set `pm.max_requests` to a reasonable number (e.g., 500–1000) to force worker recycling and avoid long-term memory buildup.