Реальный ответ заключается в том, что единственный способ создать безопасный и эффективный механизм сборки мусора - это поддержка непрозрачных ссылок на уровне языка. (Или, наоборот, отсутствие поддержки уровня языка для прямого манипулирования памятью.)
Java и C # могут сделать это, потому что у них есть специальные ссылочные типы, которыми нельзя манипулировать. Это дает среде выполнения свободу делать такие вещи, как перемещение выделенных объектов в памяти , что имеет решающее значение для высокопроизводительной реализации GC.
К сведению, ни одна современная реализация GC не использует подсчет ссылок , так что это полностью красная сельдь. Современные GC используют коллекцию поколений, где новые выделения обрабатываются, по существу, так же, как выделения стека в языке, подобном C ++, и затем периодически все вновь выделенные объекты, которые еще живы, перемещаются в отдельное пространство «оставшихся в живых», и все поколение объектов освобождается сразу.
У этого подхода есть свои плюсы и минусы: положительным моментом является то, что выделение кучи в языке, поддерживающем GC, происходит так же быстро, как и выделение стека в языке, не поддерживающем GC, и недостатком является то, что объекты, которые должны выполнить очистку перед уничтожением либо требуется отдельный механизм (например, using
ключевое слово C # ), иначе их код очистки выполняется недетерминированно.
Обратите внимание, что одним из ключей к высокопроизводительному GC является то, что должна быть языковая поддержка для специального класса ссылок. C не имеет этой языковой поддержки и никогда не будет; поскольку C ++ имеет перегрузку операторов, он может эмулировать тип указателя GC, хотя это должно быть сделано осторожно. Фактически, когда Microsoft изобрела свой диалект C ++, который будет работать под CLR (среда выполнения .NET), им пришлось изобрести новый синтаксис для «ссылок на стиль C #» (например Foo^
), чтобы отличать их от «ссылок на стиль C ++». (например Foo&
).
Что есть в C ++, и что регулярно используют программисты на C ++, так это умные указатели , которые на самом деле представляют собой просто механизм подсчета ссылок. Я бы не считал подсчет ссылок «истинным» ГХ, но он дает многие из тех же преимуществ за счет более низкой производительности, чем ручное управление памятью или настоящий ГХ, но с преимуществом детерминированного уничтожения.
В конце концов, ответ на самом деле сводится к особенностям дизайна языка. C сделал один выбор, C ++ сделал выбор, который позволил ему быть обратно совместимым с C, в то же время предоставляя альтернативы, которые достаточно хороши для большинства целей, а Java и C # сделали другой выбор, который несовместим с C, но также достаточно хорош для большинство целей. К сожалению, серебряной пули не существует, но знание различных вариантов поможет вам выбрать правильный вариант для любой программы, которую вы сейчас пытаетесь построить.