c++ - это - сборщик мусора php




RAII против сборщика мусора (8)

Если я строго следую правилам RAII, что, по-видимому, хорошо, почему это отличается от наличия сборщика мусора в C ++?

Хотя оба имеют дело с распределением, они делают это совершенно по-разному. Если вы ссылаетесь на GC, подобный тому, что есть в Java, который добавляет свои собственные издержки, удаляет часть детерминизма из процесса освобождения ресурса и обрабатывает циклические ссылки.

Вы можете реализовать GC, хотя для особых случаев, с очень разными характеристиками производительности. Я реализовал один раз для закрытия соединений с сокетами на высокопроизводительном / высокопроизводительном сервере (простой вызов API закрытия сокетов занимал слишком много времени и снижал производительность). Это включало не память, а сетевые соединения и обработку циклических зависимостей.

Я знаю, что с RAII программист полностью контролирует, когда ресурсы будут освобождены снова, но выгодно ли в любом случае иметь сборщик мусора?

Этот детерминизм - особенность, которую GC просто не позволяет. Иногда вы хотите знать, что через некоторое время была выполнена операция очистки (удаление временного файла, закрытие сетевого подключения и т. Д.).

В таких случаях GC не обрезает это, что является причиной того, что в C # (например) у вас есть интерфейс IDisposable .

Я даже слышал, что сборщик мусора может быть более эффективным, поскольку он может освобождать большие куски памяти за раз вместо освобождения небольших фрагментов памяти по всему коду.

Может быть ... зависит от реализации.

Недавно я наблюдал отличный доклад Херба Саттера о «Leak Free C ++ ...» на CppCon 2016, где он говорил об использовании интеллектуальных указателей для реализации RAII (получение ресурсов - это инициализация) - концепции и как они решают большинство проблем утечки памяти.

Теперь мне было интересно. Если я строго следую правилам RAII, что, по-видимому, хорошо, почему это отличается от наличия сборщика мусора в C ++? Я знаю, что с RAII программист полностью контролирует, когда ресурсы будут освобождены снова, но выгодно ли в любом случае иметь сборщик мусора? Неужели это будет менее эффективно? Я даже слышал, что сборщик мусора может быть более эффективным, поскольку он может освобождать большие куски памяти за раз вместо освобождения небольших фрагментов памяти по всему коду.


Я даже слышал, что сборщик мусора может быть более эффективным, поскольку он может освобождать большие куски памяти за раз вместо освобождения небольших фрагментов памяти по всему коду.

Это вполне выполнимо - и, фактически, фактически сделано - с RAII (или с простым malloc / free). Видите ли, вы не обязательно всегда используете распределитель по умолчанию, который освобождает только по частям. В определенных контекстах вы используете пользовательские распределители с различными видами функциональности. Некоторые распределители имеют встроенную возможность освобождения всего в некоторой области распределителя, все сразу, без необходимости повторять отдельные выделенные элементы.

Конечно, вы затем встаете в вопросе, когда освобождать все - будь то использование RAII или эти блоки памяти, с которыми они связаны, и как.


RAII и GC решают проблемы в совершенно разных направлениях. Они совершенно разные, несмотря на то, что некоторые скажут.

Оба решают проблему трудностей управления ресурсами. Сборка мусора решает эту проблему, делая так, чтобы разработчику не приходилось уделять столько внимания управлению этими ресурсами. RAII решает эту проблему, упрощая разработчикам возможность уделять внимание управлению своими ресурсами. Любой, кто говорит, что они делают то же самое, может продать вас.

Если вы посмотрите на последние тенденции в языках, вы увидите, что оба подхода используются на одном языке, потому что, честно говоря, вам действительно нужны обе стороны головоломки. Вы видите много языков, которые используют своего рода сборку мусора, так что вам не нужно обращать внимание на большинство объектов, и эти языки также предлагают решения RAII (например, python with оператором) в те времена, когда вы действительно хотите обратить внимание им.

  • C ++ предлагает RAII через конструкторы / деструкторы и GC через shared_ptr (если я могу привести аргумент, что пересчет и GC относятся к одному и тому же классу решений, потому что оба они разработаны, чтобы помочь вам не обращать внимания на продолжительность жизни)
  • Python предлагает RAII через with и GC через систему пересчета плюс сборщик мусора
  • C # предлагает RAII через IDisposable и using и GC через сборщик мусора поколений

Шаблоны появляются на каждом языке.


RAII и сборщик мусора предназначены для решения разных задач.

Когда вы используете RAII, вы оставляете объект в стеке, единственная цель которого - очистить все, что вы хотите (сокеты, память, файлы и т. Д.), Выходя из области действия метода. Это для безопасности исключений , а не просто для сборки мусора, поэтому вы получаете ответы о закрытии сокетов и освобождении мьютексов и тому подобное. (Ладно, никто кроме меня не упомянул мьютексы.) Если выдается исключение, разматывание стека естественным образом очищает ресурсы, используемые методом.

Сборка мусора - это программное управление памятью, хотя вы можете «собрать мусор» и другие дефицитные ресурсы, если хотите. Явное их освобождение имеет смысл в 99% случаев. Единственная причина использовать RAII для чего-то вроде файла или сокета - вы ожидаете, что использование ресурса будет завершено, когда метод вернется.

Сборка мусора также имеет дело с объектами, которые выделены в куче , когда, например, фабрика создает экземпляр объекта и возвращает его. Наличие постоянных объектов в ситуациях, когда контроль должен выходить из области видимости, делает сборку мусора привлекательной. Но вы можете использовать RAII на фабрике, поэтому, если перед возвращением выдается исключение, вы не теряете ресурсы.


Грубо говоря. Идиома RAII может быть лучше для задержки и дрожания . Сборщик мусора может быть лучше для пропускной способности системы.


На основную часть вопроса о том, является ли один или другой «выгодным» или более «эффективным», нельзя ответить, не предоставив много контекста и рассуждений об определениях этих терминов.

Помимо этого, вы можете почувствовать напряжение древнего "Java или C ++ лучший язык?" флирт хрустит в комментариях. Интересно, как может выглядеть «приемлемый» ответ на этот вопрос, и мне любопытно увидеть его в конце концов.

Но один момент, касающийся возможного важного концептуального различия, еще не отмечен: с RAII вы связаны с потоком, который вызывает деструктор. Если ваше приложение является однопоточным (и хотя Херб Саттер заявил, что бесплатный обед окончен : большинство программного обеспечения сегодня по-прежнему однопоточное), то одно ядро ​​может быть занято очисткой объектов, которые не больше актуально для актуальной программы ...

В отличие от этого сборщик мусора обычно работает в своем собственном потоке или даже в нескольких потоках и, таким образом (в некоторой степени) не связан с выполнением других частей.

(Примечание: в некоторых ответах уже пытались указать шаблоны приложений с различными характеристиками, упомянутыми эффективностью, производительностью, задержкой и пропускной способностью - но этот конкретный момент еще не упоминался)


Одна из проблем сборщиков мусора заключается в том, что трудно прогнозировать производительность программы.

С RAII вы знаете, что в точное время ресурс выйдет из области видимости, вы очистите немного памяти, и это займет некоторое время. Но если вы не являетесь мастером настройки сборщика мусора, вы не можете предсказать, когда произойдет очистка.

Например: очистка множества мелких объектов может быть выполнена более эффективно с помощью GC, поскольку она может освободить большой кусок, но это будет не быстрая операция, и трудно предсказать, когда произойдет вход, и из-за «очистки большого фрагмента» это будет занять некоторое время процессора и может повлиять на производительность вашей программы.


Сборка мусора и RAII поддерживают одну общую конструкцию, для которой другая не очень подходит.

В системе сбора мусора код может эффективно обрабатывать ссылки на неизменяемые объекты (такие как строки) как прокси для данных, содержащихся в них; Передача таких ссылок почти такая же дешевая, как и обход «тупых» указателей, и это быстрее, чем создание отдельной копии данных для каждого владельца или попытка отследить владение общей копией данных. Кроме того, системы сбора мусора позволяют легко создавать неизменяемые типы объектов, написав класс, который создает изменяемый объект, заполняя его по желанию, и предоставляя методы доступа, при этом воздерживаясь от утечек ссылок на все, что может изменить его после конструктора. отделки. В тех случаях, когда ссылки на неизменяемые объекты необходимо широко копировать, а сами объекты этого не делают, GC опережает RAII.

С другой стороны, RAII отлично справляется с ситуациями, когда объект должен приобретать эксклюзивные услуги у внешних объектов. В то время как многие системы GC позволяют объектам определять методы «Завершить» и запрашивать уведомление, когда они оказываются заброшенными, и таким методам иногда удается выпустить внешние службы, которые больше не нужны, они редко бывают достаточно надежными, чтобы обеспечить удовлетворительный способ обеспечение своевременного предоставления сторонних услуг. Для управления внешними нефункциональными ресурсами RAII опережает GC.

Основное различие между случаями, когда GC выигрывает, по сравнению с теми, где выигрывает RAII, состоит в том, что GC хорош в управлении заменимой памятью, которую можно освобождать по мере необходимости, но плохо обрабатывает не заменимые ресурсы. RAII хорош в обработке объектов с явным владением, но плохо в обращении с неизменными владельцами неизменных данных, которые не имеют реальной идентичности, кроме данных, которые они содержат.

Поскольку ни GC, ни RAII хорошо не справляются со всеми сценариями, для языков было бы полезно обеспечить хорошую поддержку для них обоих. К сожалению, языки, которые фокусируются на одном, имеют тенденцию относиться к другому как к запоздалой мысли.





smart-pointers