{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/16798","labels_url":"https://api.github.com/repos/symfony/symfony/issues/16798/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/16798/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/16798/events","html_url":"https://github.com/symfony/symfony/pull/16798","id":119900063,"number":16798,"title":"[Process] Unset callback after stop to free memory","user":{"login":"Slamdunk","id":152236,"avatar_url":"https://avatars.githubusercontent.com/u/152236?v=3","gravatar_id":"","url":"https://api.github.com/users/Slamdunk","html_url":"https://github.com/Slamdunk","followers_url":"https://api.github.com/users/Slamdunk/followers","following_url":"https://api.github.com/users/Slamdunk/following{/other_user}","gists_url":"https://api.github.com/users/Slamdunk/gists{/gist_id}","starred_url":"https://api.github.com/users/Slamdunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Slamdunk/subscriptions","organizations_url":"https://api.github.com/users/Slamdunk/orgs","repos_url":"https://api.github.com/users/Slamdunk/repos","events_url":"https://api.github.com/users/Slamdunk/events{/privacy}","received_events_url":"https://api.github.com/users/Slamdunk/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/symfony/symfony/labels/Status:%20Needs%20Review","name":"Status: Needs Review","color":"ededed"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-12-02T09:24:17Z","updated_at":"2015-12-03T11:43:13Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/16798","html_url":"https://github.com/symfony/symfony/pull/16798","diff_url":"https://github.com/symfony/symfony/pull/16798.diff","patch_url":"https://github.com/symfony/symfony/pull/16798.patch"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Bug fix?      | yes\r\n| New feature?  | no\r\n| BC breaks?    | no\r\n| Deprecations? | no\r\n| Tests pass?   | yes\r\n| Fixed tickets | -\r\n| License       | MIT\r\n| Doc PR        | -\r\n\r\nAs of yet, a `Process` instance can't free memory.\r\nThe built-in callback has a self-reference to the intance not cleared after instance destruction.\r\nI've tried to change both `buildCallback` and https://github.com/symfony/process/blob/v2.7.6/Process.php#L1373 usage to avoid self-referencing, but it can't be done without breaking BC, and the memory issue is still there.\r\n\r\nA simple test script (that obiously can't be embedded in unit-tests)\r\n```php\r\nfor ($inc = 0; $inc < 10000; $inc++) {\r\n    $process = new Symfony\\Component\\Process\\Process('echo 1');\r\n    $process->mustRun();\r\n    $process->stop();\r\n    unset($process);\r\n\r\n    echo (memory_get_usage(true) / 1000000) . ' MB ' . $inc . PHP_EOL;\r\n}\r\n```\r\nBefore:\r\n```\r\n1.572864 MB 0\r\n5.505024 MB 9999\r\n```\r\nAfter:\r\n```\r\n1.572864 MB 0\r\n1.572864 MB 9999\r\n```\r\nOk, in this simple scenario 4 MB of RAM is nothing, but in our business app full of IOC where `unset`-ting is hard, our RAM explodes.\r\n\r\nAfter `stop()`, the callback is no longer necessary.\r\n\r\nTo be clear, the garbage collector in the previous example was already active.\r\nDeactivating it manually, which somewhere is needed to avoid particluar segfaults, obiously leads to worse scenarios:\r\n\r\nBefore:\r\n```\r\n gc_disable();\r\n 1.572864 MB 0\r\n29.360128 MB 9999\r\n```\r\nAfter:\r\n```\r\n gc_disable();\r\n 1.572864 MB 0\r\n 1.572864 MB 9999\r\n```"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/161607336","html_url":"https://github.com/symfony/symfony/pull/16798#issuecomment-161607336","issue_url":"https://api.github.com/repos/symfony/symfony/issues/16798","id":161607336,"user":{"login":"Slamdunk","id":152236,"avatar_url":"https://avatars.githubusercontent.com/u/152236?v=3","gravatar_id":"","url":"https://api.github.com/users/Slamdunk","html_url":"https://github.com/Slamdunk","followers_url":"https://api.github.com/users/Slamdunk/followers","following_url":"https://api.github.com/users/Slamdunk/following{/other_user}","gists_url":"https://api.github.com/users/Slamdunk/gists{/gist_id}","starred_url":"https://api.github.com/users/Slamdunk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Slamdunk/subscriptions","organizations_url":"https://api.github.com/users/Slamdunk/orgs","repos_url":"https://api.github.com/users/Slamdunk/repos","events_url":"https://api.github.com/users/Slamdunk/events{/privacy}","received_events_url":"https://api.github.com/users/Slamdunk/received_events","type":"User","site_admin":false},"created_at":"2015-12-03T11:43:12Z","updated_at":"2015-12-03T11:43:12Z","body":"@nicolas-grekas only after pipes are close you can be sure to unset the callback, and the pipes are closed only in stop: https://github.com/symfony/process/blob/v3.0.0/Process.php#L1356"}}