Справжня відповідь полягає в тому, що єдиний спосіб зробити безпечний ефективний механізм збору сміття - це підтримка на рівні мови непрозорих посилань. (Або, навпаки, відсутність підтримки на рівні мови для прямого маніпулювання пам'яттю.)
Java та C # можуть це робити, оскільки вони мають спеціальні типи довідок, якими не можна керувати. Це дає свободі виконання часу робити такі речі, як переміщення виділених об'єктів у пам'ять , що є вирішальним значенням для високоефективної реалізації GC.
Для запису жодна сучасна реалізація GC не використовує підрахунок посилань , так що це абсолютно червона оселедець. Сучасні ГК використовують колекцію поколінь, де нові виділення трактуються по суті так само, як виділення стека є такою мовою, як C ++, а періодично будь-які новопризначені об'єкти, які ще живі, переміщуються в окремий простір "виживця" та ціле покоління об'єктів розміщується відразу.
Цей підхід має плюси і мінуси: перевага полягає в тому, що купірування виділень на мові, яка підтримує GC, настільки ж швидко, як і розподіл стеків мовою, яка не підтримує GC, і недоліком є те, що об'єкти, які потребують очищення, перш ніж знищити потрібен окремий механізм (наприклад, using
ключове слово C # ), інакше їх код очищення працює недетерміновано.
Зауважте, що одним із ключових моментів у високоефективній GC є те, що для спеціальної категорії посилань повинна бути підтримка мови. C не має такої мовної підтримки і ніколи не буде; оскільки C ++ має перевантаження оператора, він може імітувати тип вказівника GC, хоча це потрібно робити обережно. Насправді, коли Microsoft винайшла свій діалект C ++, який би працював під CLR (.NET час виконання), їм довелося винайти новий синтаксис для "C # -style reference" (наприклад Foo^
), щоб відрізнити їх від "C ++ - посилань на стиль" (наприклад Foo&
).
Що C ++ має, а що регулярно використовується програмістами C ++, це розумні покажчики , які насправді є лише механізмом підрахунку довідок. Я б не вважав підрахунок посилань "справжнім" GC, але він надає багато таких же переваг, ціною більш повільної продуктивності, ніж ручне управління пам'яттю або справжній GC, але з перевагою детермінованого знищення.
Зрештою, відповідь дійсно зводиться до функції дизайну мови. C зробив один вибір, C ++ зробив вибір, який дозволив йому бути сумісним назад із C, в той же час надаючи альтернативи, які є досить хорошими для більшості цілей, а Java та C # зробили інший вибір, несумісний із C, але також досить хороший для більшість цілей. На жаль, срібної кулі немає, але ознайомлення з різними варіантами допоможе вам вибрати правильну програму для будь-якої програми, яку ви зараз намагаєтеся створити.