Description
So, in the last 3-4 hours i was searching for a memory leak in my app. After a complete code-review of my application code i decided to make a new simple app built on Ratchet WS server and see, if the problem persists. And it did!
The problem was, that whenever i created a WS connection, the connection class instances built in the background of the Ratchet were not destroyed! So on every new connection was created a new connection class with event listeners and by dropping this connection on the client side, this background instance still exists. Because of this, my memory usage was something like this:
(i was running a PHP Ratchet WS server and testing from a JS client in the browser)
New connection
Memory usage: 2.00 MB | Max. memory: 3.00 MB
Connection dropped by client
New connection
Memory usage: 2.05 MB | Max. memory: 3.00 MB
Connection dropped by client
New connection
Memory usage: 2.10 MB | Max. memory: 3.00 MB
Connection dropped by client
New connection
Memory usage: 2.15 MB | Max. memory: 3.00 MB
Connection dropped by client
...
and so on, you get thw idea.
Then, of course, the PHP script throwed a "Max memory limit exceeded" error.
The fix for this is quite simple:
OLD
Ratchet/src/Ratchet/Server/IoServer.php
public function handleEnd($conn) {
try {
$this->app->onClose($conn->decor);
} catch (\Exception $e) {
$this->handleError($e, $conn);
}
unset($conn->decor);
}
NEW
Ratchet/src/Ratchet/Server/IoServer.php
public function handleEnd($conn) {
try {
$this->app->onClose($conn->decor);
} catch (\Exception $e) {
$this->handleError($e, $conn);
}
unset($conn->decor);
$conn->removeAllListeners();
gc_collect_cycles();
}
I think, you can even skip the $conn->removeAllListeners(); line, because the $conn class has no more instances at this time anyway.
So, now it seems to work pretty nice, no memory leak whatsoever.
But there is one thing i don't get, why isn't the garbage collector collecting things automatically when the memory limit is so near?