В наши дни так много языков собирают мусор. Это даже доступно для C ++ третьими лицами. Но в C ++ есть RAII и умные указатели. Так какой смысл использовать сборщик мусора? Это делает что-то дополнительное?
И в других языках, таких как C #, если все ссылки обрабатываются как интеллектуальные указатели (за исключением RAII), по спецификации и по реализации, все еще будет ли необходимость в сборщиках мусора? Если нет, то почему это не так?
garbage-collection
smart-pointer
Gulshan
источник
источник
Ответы:
Я предполагаю, что вы имеете в виду умные указатели с подсчетом ссылок, и я отмечу, что они являются (элементарной) формой сборки мусора, поэтому я отвечу на вопрос «каковы преимущества других форм сбора мусора над умными указателями с подсчетом ссылок» вместо.
Точность . Только подсчет ссылок приводит к утечке циклов, поэтому интеллектуальные указатели с подсчетом ссылок будут пропускать память в целом, если в циклы перехвата не добавлены другие методы. Как только эти методы добавлены, преимущество простоты в подсчете ссылок исчезло. Также обратите внимание, что основанные на области действия GC для подсчета ссылок и трассировки собирают значения в разное время, иногда подсчет ссылок собирается раньше, а иногда для трассировки GC.
Пропускная способность . Умные указатели являются одной из наименее эффективных форм сборки мусора, особенно в контексте многопоточных приложений, когда счетчики ссылок атомарно увеличиваются. Существуют передовые методы подсчета ссылок, предназначенные для облегчения этого, но отслеживание GC все еще остается предпочтительным алгоритмом в производственных средах.
Задержка . Типичные реализации интеллектуальных указателей позволяют лавинам деструкторов приводить к неограниченным временам паузы. Другие формы сбора мусора являются гораздо более инкрементными и даже могут быть в режиме реального времени, например, беговая дорожка Бейкер.
источник
Поскольку никто не смотрел на это с этой точки зрения, я перефразирую ваш вопрос: зачем вводить что-то в язык, если вы можете сделать это в библиотеке? Игнорирование конкретных реализаций и синтаксических подробностей, GC / умные указатели - в основном частный случай этого вопроса. Зачем определять сборщик мусора в самом языке, если вы можете реализовать его в библиотеке?
Есть несколько ответов на этот вопрос. Самое главное первое:
Вы гарантируете, что весь код может использовать его для взаимодействия. Я думаю, что это главная причина, по которой повторное использование кода и совместный доступ к нему действительно не взлетели до Java / C # / Python / Ruby. Библиотеки должны общаться, и единственный надежный общий язык, который у них есть, - это то, что находится в самой спецификации языка (и, в некоторой степени, в его стандартной библиотеке). Если вы когда-либо пытались повторно использовать библиотеки в C ++, вы, вероятно, испытали ужасную боль, которую не вызывает стандартная семантика памяти. Я хочу передать структуру в какую-то библиотеку. Я передаю ссылку? Указатель?
scoped_ptr
?smart_ptr
? Я передаю право собственности или нет? Есть ли способ указать это? Что если lib нужно выделить? Должен ли я дать ему распределитель? Не делая управление памятью частью языка, C ++ заставляет каждую пару библиотек согласовывать здесь свою собственную конкретную стратегию, и очень трудно заставить их всех согласиться. GC делает это полной проблемой.Вы можете разработать синтаксис вокруг него. Поскольку C ++ не инкапсулирует само управление памятью, он должен предоставить ряд синтаксических хуков, чтобы позволить пользовательскому коду выразить все детали. У вас есть указатели, ссылки,
const
операторы разыменования, операторы косвенного обращения, адреса и т. Д. Если вы внедрите управление памятью в сам язык, синтаксис может быть разработан вокруг этого. Все эти операторы исчезают, и язык становится чище и проще.Вы получаете высокую отдачу от инвестиций. Значение, которое генерирует данный фрагмент кода, умножается на количество людей, использующих его. Это означает, что чем больше у вас пользователей, тем больше вы можете позволить себе потратить на программное обеспечение. Когда вы перемещаете функцию на язык, все пользователи языка будут использовать ее. Это означает, что вы можете выделить на него больше усилий, чем на библиотеку, используемую только подмножеством этих пользователей. Вот почему такие языки, как Java и C #, имеют абсолютно первоклассные виртуальные машины и фантастически высококачественные сборщики мусора: стоимость их разработки амортизируется миллионами пользователей.
источник
Dispose
объект, который инкапсулирует растровое изображение, любая ссылка на этот объект будет ссылкой на удаленный растровый объект. Если объект был удален преждевременно, в то время как другой код все еще ожидает его использования, класс точечного рисунка может гарантировать, что другой код завершится с ошибкой предсказуемым образом. Напротив, использование ссылки на освобожденную память является неопределенным поведением.Сборка мусора в основном означает, что ваши выделенные объекты автоматически освобождаются в какой-то момент после того, как они больше не доступны.
Точнее, они освобождаются, когда становятся недоступными для программы, так как иначе объекты с круговой ссылкой никогда не будут освобождены.
Умные указатели просто ссылаются на любую структуру, которая ведет себя как обычный указатель, но имеет некоторые дополнительные функциональные возможности. Они включают, но не ограничиваются освобождением, но также копирование при записи, связанные проверки, ...
Теперь, как вы заявили, умные указатели могут использоваться для реализации формы сборки мусора.
Но ход мыслей идет следующим образом:
Конечно, вы можете создать его с самого начала. C # был разработан для сбора мусора, поэтому просто
new
ваш объект, и он будет освобожден, когда ссылки выпадают из области видимости. Как это сделать, зависит от компилятора.Но в C ++ сборка мусора не предназначалась. Если мы выделяем некоторый указатель,
int* p = new int;
и он выходит из области видимости,p
сам удаляется из стека, но никто не заботится о выделенной памяти.Теперь единственное, что у вас есть с самого начала - это детерминированные деструкторы . Когда объект покидает область, в которой он был создан, вызывается его деструктор. В сочетании с шаблонами и перегрузкой операторов вы можете создать объект-оболочку, который ведет себя как указатель, но использует функциональность деструктора для очистки ресурсов, подключенных к нему (RAII). Вы называете это умным указателем .
Все это в высшей степени специфично для C ++: перегрузка операторов, шаблоны, деструкторы, ... В этой конкретной языковой ситуации вы разработали умные указатели, чтобы предоставить вам необходимый GC.
Но если вы разрабатываете язык с помощью GC с самого начала, это всего лишь деталь реализации. Вы просто говорите, что объект будет очищен, и компилятор сделает это за вас.
Интеллектуальные указатели, такие как в C ++, вероятно, были бы невозможны даже в таких языках, как C #, которые вообще не имеют детерминированного уничтожения (C # обходит это, предоставляя синтаксический сахар для вызова a
.Dispose()
для определенных объектов). Ресурсы, на которые нет ссылок, в конечном итоге будут возвращены GC, но когда именно это произойдет, он не определит.А это, в свою очередь, может позволить GC выполнять свою работу более эффективно. Будучи встроенным в язык глубже, чем интеллектуальные указатели, которые установлены поверх него, .NET GC может, например, задерживать операции с памятью и выполнять их в блоках, чтобы сделать их более дешевыми, или даже перемещать память для повышения эффективности в зависимости от того, как часто объекты Доступ
источник
IDisposable
иusing
. Но это требует немного усилий программиста, поэтому обычно используется только для очень скудных ресурсов, таких как дескрипторы подключения к базе данных.IDisposable
синтаксис, просто заменяя обычныйlet ident = value
наuse ident = value
...using
вообще не имеет ничего общего с сборкой мусора, он просто вызывает функцию, когда переменная выходит из области видимости, как деструкторы в C ++.На мой взгляд, есть две большие разницы между сборкой мусора и умными указателями, которые используются для управления памятью:
Первый означает, что GC будет собирать мусор, а умные указатели - нет; если вы используете умные указатели, вам следует избегать создания такого рода мусора или быть готовым к его обработке вручную.
Последнее означает, что независимо от того, насколько умны умные указатели, их работа будет замедлять рабочие потоки в вашей программе. Сборка мусора может отложить работу и перенести ее в другие потоки; что позволяет ему быть более эффективным в целом (на самом деле, затраты времени выполнения современного GC меньше, чем у обычной системы malloc / free, даже без дополнительных накладных расходов умных указателей), и выполнять ту работу, которую все еще нужно выполнять, не входя в способ применения потоков.
Теперь обратите внимание, что умные указатели, будучи программными конструкциями, могут использоваться для выполнения всевозможных других интересных вещей - см. Ответ Дарио - которые полностью выходят за рамки сбора мусора. Если вы хотите сделать это, вам понадобятся умные указатели.
Однако в целях управления памятью я не вижу перспективы замены интеллектуальных указателей на сборку мусора. Они просто не так хороши в этом.
источник
using
блок в последующих версиях C #. Кроме того, недетерминированное поведение GC может быть запрещено в системах реального времени (именно поэтому GC там не используются). Кроме того, давайте не будем забывать, что GC настолько сложны в настройке, что большинство утечек памяти фактически неэффективны (например, Boehm ...).Термин «сборка мусора» подразумевает, что мусор нужно собирать. В C ++ умные указатели бывают разных типов, и самое главное - unique_ptr. Unique_ptr - это, по сути, единая структура владения и определения области видимости. В хорошо спроектированном фрагменте кода большинство выделенных в куче вещей обычно находятся за умными указателями unique_ptr, и право собственности на эти ресурсы всегда будет четко определено. Вряд ли есть какие-либо накладные расходы на unique_ptr, а unique_ptr устраняет большинство проблем ручного управления памятью, которые традиционно приводили людей к управляемым языкам. Теперь, когда большее количество ядер, работающих одновременно, становятся все более распространенными, принципы проектирования, которые заставляют код использовать уникальное и четко определенное владение в любой момент времени, становятся более важными для производительности.
Даже в хорошо разработанной программе, особенно в многопоточных средах, не все может быть выражено без общих структур данных, и для тех структур данных, которые действительно требуют, потоки должны взаимодействовать. RAII в c ++ работает довольно хорошо для решения проблем жизни в однопоточной установке, в многопоточной установке время жизни объектов может не определяться полностью иерархически в стеке. Для этих ситуаций использование shared_ptr предлагает большую часть решения. Вы создаете совместное владение ресурсом, и это в C ++ - единственное место, где мы видим мусор, но в таких небольших количествах следует подумать, что правильно разработанная программа на c ++ больше подходит для реализации сбора мусора с помощью shared-ptr, чем полноценной сборки мусора, как реализовано на других языках. C ++ просто не имеет столько «мусора»
Как утверждают другие, умные указатели с подсчетом ссылок являются одной из форм сборки мусора, и одна из них имеет одну серьезную проблему. Примером, который используется главным образом как недостаток форм подсчета мусора с подсчетом ссылок, является проблема с созданием потерянных структур данных, связанных интеллектуальными указателями друг с другом, которые создают кластеры объектов, которые не позволяют друг другу собираться. В то время как в программе, разработанной в соответствии с актерской моделью вычислений, структуры данных обычно не допускают возникновения таких несобираемых кластеров в C ++, когда вы используете подход с широкими общими данными для многопоточного программирования, который используется преимущественно в большой части отрасли, эти осиротевшие кластеры могут быстро стать реальностью.
Итак, подведем итог: если под использованием разделяемого указателя вы подразумеваете широкое использование unique_ptr в сочетании с актерской моделью вычислительного подхода для многопоточного программирования и ограниченное использование shared_ptr, то другие формы сборки мусора вам не нужны дополнительные преимущества Однако, если в рамках подхода с общим доступом у вас будет повсеместное использование shared_ptr, вам следует подумать о переключении моделей параллелизма или переключении на управляемый язык, более ориентированный на более широкое разделение владения и одновременный доступ к структурам данных.
источник
Rust
не нужно собирать мусор?Большинство умных указателей реализованы с использованием подсчета ссылок. То есть каждый умный указатель, который ссылается на объект, увеличивает счетчик ссылок на объекты. Когда этот счетчик обнуляется, объект освобождается.
Проблема есть, если у вас есть круговые ссылки. То есть, A имеет ссылку на B, B имеет ссылку на C, а C имеет ссылку на A. Если вы используете умные указатели, то для освобождения памяти, связанной с A, B & C, вам нужно вручную получить там "разорвать" круговую ссылку (например, используя
weak_ptr
в C ++).Сборка мусора (как правило) работает совсем по-другому. В наши дни большинство сборщиков мусора используют тест на достижимость . То есть он просматривает все ссылки в стеке и те, которые доступны глобально, а затем отслеживает каждый объект, на который ссылаются эти ссылки, и объекты, на которые они ссылаются, и т. Д. Все остальное - мусор.
Таким образом, циклические ссылки больше не имеют значения - пока ни A, B, ни C не достижимы , память может быть восстановлена.
Есть и другие преимущества для «настоящей» сборки мусора. Например, выделение памяти чрезвычайно дешево: просто увеличьте указатель на «конец» блока памяти. Распределение также имеет постоянную амортизированную стоимость. Но, конечно, такие языки, как C ++, позволяют реализовывать управление памятью практически любым удобным для вас способом, поэтому вы можете придумать стратегию распределения, которая будет еще быстрее.
Конечно, в C ++ объем памяти, выделенной в куче, обычно меньше, чем у языка с интенсивными ссылками, такого как C # /. NET. Но на самом деле это не проблема сбора мусора против умных указателей.
В любом случае, проблема не в том, что один лучше, чем другой. У каждого из них есть свои преимущества и недостатки.
источник
Это о производительности . Нераспределение памяти требует много администрирования. Если нераспределение выполняется в фоновом режиме, производительность процесса переднего плана увеличивается. К сожалению, распределение памяти не может быть ленивым (выделенные объекты будут использованы в следующий святой момент), но освобождение объектов может.
Попробуйте в C ++ (без всякого GC) выделить большой набор объектов, выведите «hello» и удалите их. Вы будете удивлены, сколько времени потребуется, чтобы освободить объекты.
Кроме того, GNU libc предоставляет более эффективные инструменты для нераспределения памяти, см. Препятствия . Должен заметить, у меня нет опыта с препятствиями, я никогда не использовал их.
источник
Сборка мусора может быть более эффективной - в основном она «загружает» накладные расходы на управление памятью и выполняет все сразу. В целом, это приведет к уменьшению общей загрузки ЦП на перераспределение памяти, но это означает, что в какой-то момент у вас будет большой всплеск активности по выделению ресурсов. Если ГХ не спроектирован должным образом, это может стать видимым для пользователя как «пауза», в то время как ГХ пытается освободить память. Большинство современных ГХ очень хорошо держат это невидимым для пользователя, за исключением самых неблагоприятных условий.
Умные указатели (или любая схема подсчета ссылок) имеют то преимущество, что они происходят именно тогда, когда вы ожидаете увидеть код (умный указатель выходит из области видимости, объект удаляется). Вы получаете небольшие всплески расселения здесь и там В целом вы можете использовать больше процессорного времени для перераспределения, но, поскольку оно распределяется по всем вещам, происходящим в вашей программе, менее вероятно (исключая перераспределение некоторой структуры данных монстров) стать видимым для вашего пользователя.
Если вы делаете что-то, где важна отзывчивость, я бы посоветовал, чтобы умные указатели / подсчеты подсчитывали, чтобы вы точно знали, когда что-то происходит, чтобы вы могли знать при кодировании то, что может стать видимым для ваших пользователей. В настройках GC у вас есть только самый эфемерный контроль над сборщиком мусора, и вам просто нужно попытаться обойти это.
С другой стороны, если вашей целью является общая пропускная способность, система на основе ГХ может быть гораздо лучшим выбором, поскольку она минимизирует ресурсы, необходимые для управления памятью.
Циклы. Я не считаю проблему циклов существенной. В системе, где у вас есть умные указатели, вы стремитесь к структурам данных, у которых нет циклов, или вы просто осторожны в том, как отпустить такие вещи. При необходимости можно использовать объекты-хранители, которые знают, как прерывать циклы в принадлежащих объектах, чтобы автоматически обеспечить надлежащее уничтожение. В некоторых областях программирования это может быть важно, но для большинства повседневной работы это не имеет значения.
источник
Ограничение номер один умных указателей в том, что они не всегда помогают против циклических ссылок. Например, у вас есть объект A, хранящий умный указатель на объект B, а объект B хранит умный указатель на объект A. Если они останутся вместе без сброса любого из указателей, они никогда не будут освобождены.
Это происходит потому, что умный указатель должен выполнить определенное действие, которое не будет триггерировать в приведенном выше сценарии, поскольку оба объекта недоступны для программы. Сборка мусора справится - он правильно определит, что объекты не подлежат повторной проверке программой, и они будут собраны.
источник
Это спектр .
Если вы не хотите ограничивать производительность и готовы приложить все усилия, вы в конечном итоге окажетесь на собрании или c, и у вас будет все необходимое для принятия правильных решений и полная свобода делать это, но с этим Всей свободы испортить
«Я скажу вам, что делать, вы делаете это. Поверьте мне».
Сборка мусора является другим концом спектра. У вас очень мало контроля, но он позаботился о вас:
«Я скажу тебе, что я хочу, ты сделаешь это».
Это имеет много преимуществ, в основном то, что вам не нужно быть настолько заслуживающим доверия, когда речь идет о точном знании того, когда ресурс больше не нужен, но (несмотря на некоторые из ответов, присутствующих здесь) не является хорошим для производительности, и предсказуемость производительности. (Как и все, если вам дают контроль, и вы делаете что-то глупое, у вас могут быть худшие результаты. Однако предположить, что знание во время компиляции, каковы условия для возможности освободить память, не может быть использовано, поскольку выигрыш в производительности за наивный).
RAII, обзор, подсчет ссылок и т. Д. - все это помощники, позволяющие вам двигаться дальше по этому спектру, но это далеко не все. Все эти вещи все еще требуют активного использования. Они по-прежнему позволяют и требуют от вас взаимодействия с управлением памятью так, как это не делает сборщик мусора.
источник
Пожалуйста, помните, что в конце все сводится к выполнению инструкций процессора. Насколько мне известно, все процессоры потребительского уровня имеют наборы команд, которые требуют, чтобы вы хранили данные в определенном месте в памяти, и у вас есть указатели на эти данные. Это все, что у вас есть на базовом уровне.
Все это, помимо сбора мусора, ссылок на данные, которые могли быть перемещены, сжатия кучи и т. Д. И т. Д., Выполняет работу в рамках ограничений, указанных в приведенной выше парадигме «блок памяти с указателем адреса». То же самое с умными указателями - вы все равно должны заставить код работать на реальном оборудовании.
источник