-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce memory consumption by avoiding circular reference from stream reader #18
Conversation
Thanks for filing this PR! Once the failing test is addressed, I'm not opposed to getting this 👍
Can you give some background on where this is an issue and how this problem can be reproduced? I'd like to ensure this change addresses the core of this issue and make sure we do not introduce any regressions in the future 👍 |
Here is a test case: $loop = React\EventLoop\Factory::create();
$proxy = new \Clue\React\HttpProxy\ProxyConnector('127.0.0.1:3128', new \React\Socket\Connector($loop));
$connector = new \React\Socket\Connector($loop, array(
'tcp' => $proxy,
'timeout' => 3.0,
'dns' => false,
'tls' => false,
));
$loop->addPeriodicTimer(1, function () use ($connector) {
$connector->connect('google.com:80')
->then(function (\React\Socket\ConnectionInterface $stream) {
$stream->close();
printf("[+] %.3fMB\n", memory_get_usage() / 1024 / 1024);
})->otherwise(function (\Exception $e) {
printf("[-] %.3fMB (%s)\n", memory_get_usage() / 1024 / 1024, $e->getMessage());
});
});
$loop->addTimer(60, function () use ($loop) {
printf(
"%.3fMB => %d => %.3fMB\n",
memory_get_usage() / 1024 / 1024,
gc_collect_cycles(),
memory_get_usage() / 1024 / 1024);
$loop->stop();
});
$loop->run(); Before fix:
After fix:
So basically that circular reference eats about 3.5KB of memory for each successful connection. When you run a lot of clients with unstable proxy, it becomes the issue. |
I fixed the failing test. By the way, if a proxy is not running, the test case leaks 20x faster:
But it seems it is a problem with the Socket component. |
Ok, I have done some research and found that leaks with timed out connections are produced by promise cancellation. Related PR: reactphp/socket#113. The failing test is addressed and this PR is ready for final review. |
Thanks for looking into this and providing the elaborate description! 👍 IMO it makes sense to get this in as-is, as reducing the memory consumption by reducing the number of circular dependencies is certainly a good thing 👍 |
Stream reader has a circular reference to itself here: https://github.com/clue/php-http-proxy-react/blob/7258a76f492357874d0af1fe1f50a08209f61174/src/ProxyConnector.php#L137 so it can be cleaned only with GC. When a proxy is doing a lot of disconnects, it is turning into a nightmare.