Почему в C++ нет сборщика мусора?

Я не задаю этот вопрос в первую очередь из-за достоинств сборки мусора. Моя основная причина спрашивать об этом заключается в том, что я знаю, что Бьярн Страуструп сказал, что в C++ в какой-то момент будет сборщик мусора.

С учетом сказанного, почему его не добавили? Уже есть сборщики мусора для C++. Является ли это одним из тех, что «легче сказать, чем сделать»? Или есть другие причины, по которым он не был добавлен (и не будет добавлен в C++ 11)?

Перекрестные ссылки:

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

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

davr 29.09.2008 07:25

Но в этом вся суть в том, что он не является встроенным, это нужно делать самому. Реальность по размеру: встроенная, библиотечная, самодельная. Я сам использую C++ и определенно не ненавижу его, потому что это лучший язык в мире. Но управление динамической памятью - это боль.

QBziZ 29.09.2008 12:55

@Davr - Я не ненавистник C++ и даже не пытаюсь утверждать, что C++ нужен сборщик мусора. Я спрашиваю, потому что знаю, что Бьярн Страуструп сказал, что он БУДЕТ добавлен, и мне было просто любопытно, каковы были причины его невыполнения.

Jason Baker 30.09.2008 03:21

См. Также stackoverflow.com/questions/819425/….

Daniel Daranas 19.05.2009 11:38

я думаю, что лучший вопрос; почему стратегия сборки мусора не является обязательной в C++? и почему исходное предложение C++ 0x не допускало частичного gc в программе?

lurscher 10.12.2010 19:42

В этой статье Сборщик Boehm для C и C++ от доктора Доббса описывается сборщик мусора с открытым исходным кодом, который можно использовать как с C, так и с C++. В нем обсуждаются некоторые проблемы, возникающие при использовании сборщика мусора с деструкторами C++, а также со стандартной библиотекой C.

Richard Chambers 17.01.2016 17:56

С ++ 11, по-видимому, допускает это, «если реализация выберет это»: stackoverflow.com/questions/15157591/…

rogerdpack 08.11.2017 21:03

@rogerdpack: Но на данный момент это не так полезно (см. мой ответ ...), поэтому маловероятно, что реализации будут инвестировать в его наличие.

einpoklum 01.01.2018 02:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
279
8
170 586
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

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

C / C++ используется слишком во многих различных обстоятельствах. Я подозреваю, что для большинства пользователей будет достаточно чего-то вроде ускоренных интеллектуальных указателей.

Изменить - автоматические сборщики мусора - это не столько проблема производительности (вы всегда можете купить больше серверов), это вопрос предсказуемой производительности. Не знать, когда сработает ГК, - все равно что нанять пилота нарколептической авиакомпании, в большинстве случаев они хороши, но когда вам действительно нужна отзывчивость!

Я определенно понимаю вашу точку зрения, но чувствую себя обязанным спросить: разве Java не используется примерно в таком же количестве приложений?

Jason Baker 29.09.2008 05:05

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

Zathrus 29.09.2008 05:22

Вы всегда можете купить больше сервера, но не всегда можете купить больше процессора для сотового телефона, уже находящегося в кармане клиента!

Crashworks 10.01.2010 08:06

Java значительно повысила производительность ЦП. По-настоящему неразрешимая проблема - это использование памяти, Java по своей природе менее эффективна с точки зрения памяти, чем C++. И эта неэффективность связана с тем, что это сборщик мусора. Сборка мусора не может быть одновременно быстрой и эффективной с точки зрения памяти - факт, который становится очевидным, если вы посмотрите, насколько быстро работают алгоритмы сборки мусора.

Nate C-K 03.11.2010 18:20

@Zathrus java может выиграть по пропускной способности b / c оптимизирующего jit, но не по задержке (в реальном времени) и, конечно, не по объему памяти.

gtrak 23.10.2011 01:54

@Zathrus - Java на мэйнфрейме: www-03.ibm.com/systems/z/os/zos/tools/java. Контроллер коммутатора Java - floodlight.openflowhub.org. Гарантии производительности в реальном времени важны только для относительно небольшого подмножества высокопроизводительных приложений, в большинстве областей HPC Java была сильна в течение долгого времени.

mikera 08.08.2012 10:10

@Zathrus Разве 864 ядра не подходит для суперкомпьютера? Разве 6 миллионов заказов в секунду в одном потоке в торговле в реальном времени не считается высокопроизводительным? У Java по-прежнему есть недостатки, но она хороша почти для всех задач.

maaartinus 30.12.2017 16:58
Ответ принят как подходящий

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

Цитата самого Бьярна Страуструпа:

I had hoped that a garbage collector which could be optionally enabled would be part of C++0x, but there were enough technical problems that I have to make do with just a detailed specification of how such a collector integrates with the rest of the language, if provided. As is the case with essentially all C++0x features, an experimental implementation exists.

Есть хорошее обсуждение темы здесь.

Общий обзор:

C++ очень мощный и позволяет делать практически все. По этой причине он автоматически не навязывает вам многие вещи, которые могут повлиять на производительность. Сборку мусора можно легко реализовать с помощью интеллектуальных указателей (объектов, которые обертывают указатели с помощью счетчика ссылок, которые автоматически удаляют себя, когда счетчик ссылок достигает 0).

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

Есть 2 типа сборки мусора ...

Явная сборка мусора:

C++ 0x будет иметь сборку мусора с помощью указателей, созданных с помощью shared_ptr

Если вы хотите, вы можете использовать это, если вы этого не хотите, вас не заставляют его использовать.

В настоящее время вы также можете использовать boost: shared_ptr, если не хотите ждать C++ 0x.

Неявная сборка мусора:

Однако у него нет прозрачной сборки мусора. Тем не менее, это будет фокус для будущих спецификаций C++.

Почему в Tr1 нет неявной сборки мусора?

Есть много вещей, которые должны были быть у tr1 C++ 0x, Бьярн Страуструп в предыдущих интервью заявил, что tr1 не имеет того, что ему хотелось бы.

Я бы становиться ненавидел, если бы C++ принудил меня к сборке мусора! Почему люди не могут использовать smart_ptr's? Как бы вы сделали разветвление в стиле Unix на низком уровне, с мешающим сборщиком мусора? Это повлияет на другие вещи, такие как потоки. Python имеет свой глобальная блокировка интерпретатора в основном из-за его сборки мусора (см. Cython). Держите его подальше от C / C++, спасибо.

unixman83 19.04.2012 11:47

Как сделать прозрачную сборку мусора непрозрачных структур API операционной системы void *?

unixman83 19.04.2012 11:54

@ unixman83: Основная проблема со сборкой мусора с подсчетом ссылок (например, std::shared_ptr) - это циклические ссылки, которые вызывают утечку памяти. Поэтому вы должны осторожно использовать std::weak_ptr, чтобы разорвать циклы, а это беспорядочно. У GC стиля метки и развертки этой проблемы нет. Между потоками / разветвлением и сборкой мусора нет внутренней несовместимости. И Java, и C# обладают высокопроизводительной вытесняющей многопоточностью и сборщиком мусора. Есть проблемы, связанные с приложениями реального времени и сборщиком мусора, поскольку большинство сборщиков мусора должны останавливать запуск мира.

Andrew Tomazos 09.01.2013 00:14

«Основная проблема со сборкой мусора с подсчетом ссылок (например, std::shared_ptr) - это циклические ссылки» и ужасная производительность, что иронично, потому что более высокая производительность обычно является оправданием для использования C++ ... flyingfrogblog.blogspot.co.uk/2011/01/…

J D 17.06.2013 15:57

"Как бы вы сделали разветвление в стиле Unix на низком уровне". Точно так же языки с GC, такие как OCaml, делают это в течение ~ 20 лет или больше.

J D 17.06.2013 15:57

«Python имеет глобальную блокировку интерпретатора в основном из-за сборки мусора». Аргумент Строумана. У Java и .NET есть GC, но ни у одной из них нет глобальных блокировок.

J D 17.06.2013 15:59

Проблема, с которой я сталкиваюсь с принудительным и широко распространенным использованием GC, заключается в том, что трудно избежать утечек. Если мы полностью перейдем к наиболее ручным формам управления памятью, таким как C, единственный способ утечки памяти - это не иметь free, соответствующего malloc/calloc. В GC автор чего-то вроде объекта текстуры мог бы сделать все идеально, чтобы выделить текстуру и удалить все существующие ссылки, которые он создал на нее во время ее удаления - абсолютно идеально, проверено модулем. И все же могут быть сотни мест, где утечка текстуры - все, что требуется в GC, чтобы ресурс текстуры ...

user4842163 14.11.2015 08:06

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

user4842163 14.11.2015 08:07

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

user4842163 14.11.2015 08:16

Ссылка "источник" мертва. Пожалуйста, обновите ответ. Я пробовал, но кнопка редактирования не работает.

th3an0maly 28.09.2016 09:40

@DrunkCoder После любой вовлеченный случайный разработчик просто сохранил ссылку на текстуру в некотором агрегате и не смог удалить ссылку в надлежащее время вы старательно вызываете free, а затем другой случайный разработчик использует эту ссылку, и вам всем нравится отлаживать неопределенное поведение, убивая ваших кошек и сжигая ваши дома. Хотя valgrind находит ссылку и код доступа, я не понимаю, насколько это проще, чем анализировать утечку памяти, которую можно получить с помощью GC.

maaartinus 30.12.2017 16:40

С практической точки зрения, если бы такое неопределенное поведение могло легко пролететь незамеченным, и программа могла бы работать нормально или в некоторой степени, я бы согласился. Но в большинстве случаев такой UB имеет тенденцию проявляться в виде сбоя, повреждения кучи, segfault и т. д., И все это может быть немедленно обнаружено во время отладки. Если вы работаете над критически важным программным обеспечением, утечка которого - не конец света, я согласен с тем, что сборщик мусора - очень разумный выбор. Но в остальном я больше всего стремлюсь к тому, чтобы ошибки были как можно проще воспроизводить последовательно и как можно проще выявлять.

user4842163 30.12.2017 16:53

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

user4842163 30.12.2017 16:54

По моему личному опыту, ошибки, которые легко воспроизводятся последовательно, как, например, ошибки сегментации, как правило, сообщаются и исправляются, если они не были обнаружены в нашем модульном / интеграционном тестировании до того, как продукт официально отправится. Между тем ошибки, которые чрезвычайно сложно воспроизвести / обнаружить, могут оставаться в продукте годами - среди тех, с которыми я столкнулся, были условия гонки, неинициализированные переменные и утечки сборщика мусора. Только подумайте, как написать тест, который может обнаружить такую ​​утечку сборщика мусора с корневым ресурсом? Не думаю, что это возможно, но можно сразу обнаружить доступ к зависшим указателям.

user4842163 30.12.2017 17:01

@maaartinus Наконец, valgrind не может обнаружить логическую утечку AFAIK. Корневой ресурс не обязательно продолжает использовать память после завершения работы. Он просто продолжает занимать память во время работы приложения. Как только приложение начинает закрываться, объект (скажем, шейдер), который хранит текстуру, удаляется из системы, и текстура, на которую он ссылается, также освобождается. Поскольку сборщик мусора полагается на неявное уничтожение, а не явное, невозможно сказать, какой ресурс должен быть освобожден, когда именно. Текстура просто не была освобождена достаточно рано, когда пользователь запросил ее.

user4842163 30.12.2017 17:16

Тип утечки, о котором я говорю, - это тот, где, скажем, эти шейдеры хранят ссылки на текстуры, которые они используют. То же самое и с библиотекой изображений. Однако пользователь может явно запросить удаление текстуры из библиотеки изображений, после чего шейдеры должны освободить свои ссылки на текстуру, чтобы сборщик мусора мог собрать свою память. Однако они не удаляют / не обнуляют эти ссылки, поэтому текстуры молча продолжают занимать память, пока не будут удалены сами шейдеры. Это тот вид утечки GC, который, по моему опыту, незаметен и никогда не обнаруживается тестированием / valgrind.

user4842163 30.12.2017 17:24

А пока возьмем аналог с явным уничтожением. В том случае, когда пользователь запрашивает удаление текстуры из библиотеки изображений, разработчик явно просит ее уничтожить. Теперь, если у нас есть та же ошибка, описанная выше, когда шейдеры не выпускают свои указатели / ссылки на текстуру, они могут затем попытаться получить к ней доступ, что приведет к висящему указателю UB (обычно это приводит к ужасному сбою). Но это, как правило, немедленно обнаруживается и воспроизводится QA, если не нашими автоматическими тестами - так что я на самом деле считаю это гораздо более предпочтительным.

user4842163 30.12.2017 17:26

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

user4842163 30.12.2017 17:38

И фух, извиняюсь за длинные комментарии, но я думаю, что для многих приложений небольшая утечка во время выполнения, вероятно, не представляет большого труда. Но есть некоторые области, такие как игры или, в моем случае, VFX, где нетривиальные ресурсы выделяются довольно часто - с VFX художник может загрузить HDR-текстуру высокого разрешения, которая занимает гигабайт. Так что неспособность освободить его в надлежащее время - это действительно неприятная ошибка в нашем случае, а не что-то относительно безобидное, и мы не хотим, чтобы такие ошибки легко оставались незамеченными, поскольку GC может позволить.

user4842163 30.12.2017 17:45

И последнее, (последнее, я обещаю!) - давайте рассмотрим сценарий, который не разрешен сборщиком мусора - сценарий, в котором исходный разработчик вообще не освобождает текстуру - никакая часть системы не освобождает текстуры. Это тот вид физической утечки, который valgrind легко обнаруживает, поэтому его также очень легко обнаружить ... Это только логическая утечка в стиле GC в этом сценарии, которую я нашел невероятно трудной для обнаружения в больших базах кода (миллионы LOC), потому что вы видите тот факт, что приложение загружает все больше и больше памяти, чем дольше оно работает, но кто это сделал? Кому не удалось обнулить / удалить реф?

user4842163 30.12.2017 17:54

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

user4842163 30.12.2017 17:59

@DrunkCoder Valgrind действительно не может найти логические утечки, и нет возможности их протестировать, но есть другие инструменты для языков, собранных с помощью GC. Вы говорите с точки зрения C++, которая полностью отличается от Java. Действительно, сборщик мусора не помогает напрямую с логическими утечками, но делает их маловероятными. Какой у вас шейдер для ссылки? В Java это, вероятно, будет легкий объект, выделяемый при необходимости, и он сам получит сборку мусора. Это может быть синглтон, вместо того, чтобы содержать ссылку на текстуру ток, а затем он будет пропускать ее только до тех пор, пока не будет заменен новой.

maaartinus 30.12.2017 18:10

@maaartinus Это проблема управления ресурсами, не решаемая ни одной языковой функцией AFAIK (хотя в Java могут быть хорошие инструменты отладки, о которых я не знаю, чтобы помочь обнаружить эти утечки). Скажем, у вас есть центральный граф сцены в программном обеспечении. Графики сцены хранят такие вещи, как шейдеры, сетки, текстуры, камеры и т. д., И некоторые из этих вещей реализованы сторонними организациями (сторонние шейдеры, загружаемые плагинами). Типы утечек, с которыми я столкнулся, были теми, где у вас может быть что-то вроде сетки или текстуры, на которую ссылается (и, следовательно, принадлежит) ...

user4842163 30.12.2017 18:15

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

user4842163 30.12.2017 18:17

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

user4842163 30.12.2017 18:18

В нашем случае шейдеры были легкими объектами - очень легкими - в основном это был код. Но они могут ссылаться на что-то не легкое, например, сетку с миллионом полигонов или HDR-текстуру 8000x8000 пикселей. И когда пользователь запрашивает удалить такую ​​текстуру из библиотеки изображений, шейдер (который может быть шейдером плагина стороннего производителя) может не обработать это событие и должным образом освободить текстуру ... и тогда начинаются мои кошмары.

user4842163 30.12.2017 18:22

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

user4842163 30.12.2017 18:27

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

user4842163 30.12.2017 18:28

@DrunkCoder Похоже, вы работаете с Java. На Java существует масса критически важных приложений и очень мало жалоб на утечку памяти. Существуют утечки загрузчика классов, это отдельная проблема, так что давайте о них забудем. Многие проблемы действительно решаются с помощью слабых ссылок, особенно таких как «список мешей, которые нужно исключить из рендеринга». Если ваши шейдеры настолько легкие, вы можете выделить их и забыть после того, как они выполнили свою работу, чтобы они получили GD. Если это невозможно, и плагин не может быть изменен, то вы можете не давать ему реальную вещь ...

maaartinus 30.12.2017 18:28

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

user4842163 30.12.2017 18:30

Позвольте нам продолжить обсуждение в чате.

maaartinus 30.12.2017 18:31

Что мне не нравится в STL ATM, так это неудобный способ использования итераторов для работы с отсутствующим сборщиком мусора. В Linq я мог бы просто выполнить .Select (), в C++ отфильтрованные элементы перемещаются в начало, а итератор перемещается к первому выбранному элементу. Думаю, чтобы вы могли освободить предыдущие предметы. Иначе утечка памяти. Все это могло бы быть намного лучше с GC. На данный момент они просто неудобные. ИМХО

HankTheTank 26.12.2018 12:34

Идея C++ заключалась в том, что вы не будете платить за влияние на производительность функций, которые вы не используете. Таким образом, добавление сборки мусора означало бы, что некоторые программы будут работать прямо на оборудовании, как это делает C, а некоторые - на какой-то виртуальной машине времени выполнения.

Ничто не мешает вам использовать интеллектуальные указатели в той или иной форме, привязанные к стороннему механизму сборки мусора. Кажется, я припоминаю, что Microsoft делала что-то подобное с COM, и это пошло не так.

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

user83255 07.05.2009 15:39

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

Uri 07.05.2009 20:25

If you want automatic garbage collection, there are good commercial and public-domain garbage collectors for C++. For applications where garbage collection is suitable, C++ is an excellent garbage collected language with a performance that compares favorably with other garbage collected languages. See The C++ Programming Language (4rd Edition) for a discussion of automatic garbage collection in C++. See also, Hans-J. Boehm's site for C and C++ garbage collection (archive).

Also, C++ supports programming techniques that allow memory management to be safe and implicit without a garbage collector. I consider garbage collection a last choice and an imperfect way of handling for resource management. That does not mean that it is never useful, just that there are better approaches in many situations.

Источник: http://www.stroustrup.com/bs_faq.html#garbage-collection

Что касается того, почему он не встроен, если я правильно помню, он был изобретен до того, как GC был предмет, и я не верю, что язык мог иметь GC по нескольким причинам (I.E. Обратная совместимость с C)

Надеюсь это поможет.

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

J D 17.06.2013 16:13

Моя ссылка не работает. Я написал этот ответ 5 лет назад.

Rayne 18.06.2013 04:59

Хорошо, я надеялся на независимую проверку этих утверждений, то есть не со стороны Страуструпа или Бема. :-)

J D 18.06.2013 16:07

Чтобы ответить на большинство вопросов «почему» о C++, прочтите Дизайн и развитие C++

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

  • детерминированное время жизни объектов (подсчет ссылок дает это, а сборщик мусора - нет. Хотя это может быть не так уж важно).
  • что произойдет, если деструктор сработает, когда объект собирает мусор? Большинство языков игнорируют это исключение, поскольку на самом деле нет блока catch, в который его можно было бы перенести, но это, вероятно, неприемлемое решение для C++.
  • Как включить / отключить? Естественно, это, вероятно, будет решением времени компиляции, но код, написанный для GC, по сравнению с кодом, написанным для НЕ GC, будет очень другим и, вероятно, несовместимым. Как вы это примирите?

Это лишь некоторые из проблем, с которыми пришлось столкнуться.

Сборщик мусора и деструкторы - это решенная проблема, хороший шаг в сторону от Бьярна. Деструкторы не запускаются во время сборки мусора, потому что сборщик мусора не в этом. GC в C++ существует, чтобы создать понятие бесконечного объем памяти, а не бесконечного количества других ресурсов.

MSalters 29.09.2008 15:54

Если деструкторы не запускаются, это полностью меняет семантику языка. Я предполагаю, что, по крайней мере, вам понадобится новое ключевое слово "gcnew" или что-то в этом роде, чтобы вы явно разрешили сборку мусора для этого объекта (и, следовательно, вы не должны использовать его для обертывания ресурсов, помимо памяти).

Greg Rogers 29.09.2008 17:43

Это подделка. Поскольку C++ имеет явное управление памятью, вам нужно выяснить, когда нужно освободить каждый объект. С GC все не хуже; скорее, проблема сводится к выяснению того, когда освобождены определенные объекты, а именно те объекты, которые требуют особого внимания при удалении. Опыт программирования на Java и C# показывает, что подавляющее большинство объектов не требует особого внимания и может быть безопасно оставлено на усмотрение сборщика мусора. Оказывается, одна из основных функций деструкторов в C++ - освобождать дочерние объекты, которые GC обрабатывает за вас автоматически.

Nate C-K 03.11.2010 18:27

@ NateC-K: Одна вещь, которая улучшена в GC по сравнению с не-GC (возможно, самая большая вещь) - это способность надежной системы GC гарантировать, что каждая ссылка будет продолжать указывать на один и тот же объект, пока существует ссылка. Вызов Dispose для объекта может сделать его непригодным, но ссылки, которые указывали на объект, когда он был жив, продолжат действовать после его смерти. Напротив, в системах без GC объекты могут быть удалены, пока существуют ссылки, и редко существует какой-либо предел хаосу, который может быть нанесен, если одна из этих ссылок используется.

supercat 02.10.2013 01:49

Чтобы добавить к дискуссии здесь.

Существуют известные проблемы со сборкой мусора, и их понимание помогает понять, почему их нет в C++.

1. Производительность?

Первая жалоба часто касается производительности, но большинство людей не понимают, о чем они говорят. Как показано на примере Martin Beckett, проблема может заключаться не в производительности как таковой, а в ее предсказуемости.

В настоящее время широко распространены 2 семейства сборщиков мусора:

  • Mark-And-Sweep вид
  • Ссылочно-счетный вид

Mark And Sweep быстрее (меньше влияет на общую производительность), но страдает синдромом «замораживания мира»: то есть, когда срабатывает сборщик мусора, все остальное останавливается до тех пор, пока сборщик мусора не произведет очистку. Если вы хотите создать сервер, который отвечает за несколько миллисекунд ... некоторые транзакции не оправдают ваших ожиданий :)

Проблема Reference Counting в другом: подсчет ссылок увеличивает накладные расходы, особенно в многопоточных средах, потому что вам нужно иметь атомарный счетчик. Кроме того, существует проблема эталонных циклов, поэтому вам нужен умный алгоритм для обнаружения этих циклов и их устранения (обычно реализуется также путем «замораживания мира», хотя и реже). В целом, на сегодняшний день этот тип (хотя обычно более отзывчивый или, скорее, реже зависает) медленнее, чем Mark And Sweep.

Я видел статью разработчиков Eiffel, которые пытались реализовать сборщик мусора Reference Counting, который имел бы такую ​​же глобальную производительность, что и Mark And Sweep, без аспекта «заморозить мир». Требовался отдельный поток для GC (обычно). Алгоритм был немного пугающим (в конце), но документ хорошо поработал, вводя концепции по одному и показывая эволюцию алгоритма от «простой» версии к полноценной. Рекомендую к прочтению, если бы я только мог снова положить руки на файл PDF ...

2. Приобретение ресурсов - это инициализация (RAII)

В C++ распространена идиома, заключающаяся в том, что вы оборачиваете владение ресурсами внутри объекта, чтобы гарантировать, что они правильно высвобождены. Он в основном используется для памяти, поскольку у нас нет сборки мусора, но, тем не менее, он также полезен во многих других ситуациях:

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

Идея состоит в том, чтобы правильно контролировать время жизни объекта:

  • он должен быть жив, пока он вам нужен
  • он должен быть убит, когда вы закончите с ним

Проблема GC в том, что если он помогает с первым и в конечном итоге гарантирует, что позже ... этого «конечного» может быть недостаточно. Если вы снимете блокировку, вам бы очень хотелось, чтобы она была снята сейчас, чтобы не блокировать дальнейшие вызовы!

У языков с GC есть два решения:

  • не используйте сборщик мусора, когда выделения стека достаточно: обычно это связано с проблемами производительности, но в нашем случае это действительно помогает, поскольку область действия определяет время жизни
  • Конструкция using ... но это явный (слабый) RAII, в то время как в C++ RAII неявный, поэтому пользователь НЕ МОЖЕТ невольно совершить ошибку (опуская ключевое слово using)

3. Умные указатели

Умные указатели часто появляются как серебряная пуля для работы с памятью в C++. Часто я слышал: нам ведь не нужен сборщик мусора, поскольку у нас есть умные указатели.

Трудно ошибиться.

Умные указатели действительно помогают: auto_ptr и unique_ptr используют концепцию RAII, что действительно очень полезно. Они настолько просты, что вы легко можете написать их самостоятельно.

Однако, когда нужно разделить владение, это становится сложнее: вы можете делиться между несколькими потоками, и есть несколько тонких проблем с обработкой счетчика. Поэтому естественно идти в сторону shared_ptr.

Это здорово, в конце концов, именно для этого и нужен Boost, но это не серебряная пуля. Фактически, основная проблема с shared_ptr заключается в том, что он эмулирует сборщик мусора, реализованный Reference Counting, но вам нужно реализовать обнаружение цикла самостоятельно ...

Конечно, есть эта штука с weak_ptr, но я, к сожалению, уже видел утечки памяти, несмотря на использование shared_ptr из-за этих циклов ... а когда вы находитесь в многопоточной среде, это чрезвычайно сложно обнаружить!

4. Какое решение?

Серебряной пули нет, но, как всегда, она вполне осуществима. В отсутствие GC необходимо четко указать владельца:

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

Так что, действительно, было бы здорово иметь сборщик мусора ... однако это нетривиальная проблема. А пока нам остается только засучить рукава.

Хотел бы я принять два ответа! Это просто здорово. Следует отметить, что в отношении производительности сборщик мусора, работающий в отдельном потоке, на самом деле довольно распространен (он используется в Java и .Net). Конечно, это может быть неприемлемо для встроенных систем.

Jason Baker 25.02.2010 16:24

Всего два типа? Как насчет копирования коллекционеров? Коллекционеры поколений? Различные параллельные сборщики (включая беговую дорожку Бейкера в режиме жесткого реального времени)? Различные коллекционеры гибридов? Черт, полное невежество в индустрии этой области иногда меня удивляет.

JUST MY correct OPINION 01.06.2010 18:51

Я сказал, что их всего 2 типа? Я сказал, что было 2, которые были широко развернуты. Насколько мне известно, Python, Java и C# теперь используют алгоритмы Mark и Sweep (раньше в Java был алгоритм подсчета ссылок). Чтобы быть еще более точным, мне кажется, что C# использует GC Generational для второстепенных циклов, Mark And Sweep для больших циклов и Copying для борьбы с фрагментацией памяти; хотя я бы сказал, что сердце алгоритма - это Mark And Sweep. Знаете ли вы какой-нибудь основной язык, использующий другую технологию? Я всегда рада учиться.

Matthieu M. 01.06.2010 21:59

Вы только что назвали один основной язык, который использует три.

JUST MY correct OPINION 02.06.2010 06:15

@ ТОЛЬКО МОЕ ПРАВИЛЬНОЕ МНЕНИЕ, Твое высокомерие регулярно меня удивляет. Но высокомерные люди иногда бывают крутыми ...;)

d-_-b 12.04.2011 09:00

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

Keldon Alleyne 17.04.2012 23:12

@ JSPerfUnkn0wn: об этом уже говорилось ТОЛЬКО МОЕ правильное МНЕНИЕ. Я бы сказал, что сборщик мусора поколений основан на Mark & ​​Sweep (хотя они ограничивают алгоритм меньшим объемом). Что касается ненужности, проблема в том, что либо вы используете сборщик мусора и рискуете получить утечку пространства, либо используете ручную обработку и рискуете получить утечки памяти / устаревшие ссылки. Мне очень интересна система регионов Rust, и мне интересно, представит ли она альтернативу вышеуказанной проблеме.

Matthieu M. 18.04.2012 10:48

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

Keldon Alleyne 18.04.2012 12:32

@ JSPerfUnkn0wn: честно говоря, что касается Mark & ​​Sweep, вы также можете избежать эффекта остановки мира, введя барьеры для чтения или записи и т. д., Так что на самом деле я рассматриваю генерацию как усовершенствование Mark & ​​Sweep. Очевидно, я лишь смутно затронул эту тему, во-первых, потому что это ТАКОЕ ответ, а во-вторых, потому что я определенно не эксперт в этой теме. Не могли бы вы добавить свой ответ?

Matthieu M. 18.04.2012 12:56

@MatthieuM. вы сказали «Mark And Sweep быстрее (меньше влияет на общую производительность), но страдает синдромом« заморозить мир »», это все, что я тоже отвечал. Меня не волнует, назовете ли вы их может вариациями Mark & ​​Sweep или хотите называть их "Egg and Bacon", все, что меня волнует, - это реализация и четкое общение между программистами, а не то, что аргументирует это должен называться: ) ... теперь вернемся к теме, GC (или назовем его Automatic GC, как его называет Python) не должен останавливать мир, это все, на что я намекал (и другие вещи, которые я упомянул) :)

Keldon Alleyne 18.04.2012 15:39

@MatthieuM. Что касается «Возможно, вы могли бы добавить свой собственный ответ», в этом нет необходимости, эти ответы хороши, как и ваш :) И, как вы сказали, эти алгоритмы являются усовершенствованием Mark & ​​Sweep, хотя я бы пошел дальше, чтобы указать, что они только когда-либо будут проходить через узлы (что выделяет их как варианты M&S). Что определяет их больше всего, так это то, как различается их поведение, например, я не просто отмечаю, у меня есть 5 состояний разделов / узлов, у Mark & ​​Sweep - 2, и я видел один в Википедии с 3. Я никогда не пытался создать Хотя и поколениями, но у меня есть несколько идей.

Keldon Alleyne 18.04.2012 15:40

"страдает синдромом" замораживания мира ". Дейкстра решил эту проблему в 1975 году с помощью своей схемы трехцветной маркировки для инкрементальной зачистки меток. Такие языки, как OCaml (1996), используют это и, следовательно, не страдают от этой проблемы. cs.utexas.edu/~EWD/ewd05xx/EWD595.PDF

J D 17.06.2013 16:08

@JonHarrop: Я коснулся возможных улучшений после схемы «подсчета ссылок», однако параллельные сборщики мусора далеки от тривиальности и сами по себе требуют затрат; вам нужны либо барьеры чтения, либо барьеры записи в других потоках, о которых, я думаю, говорил Дейкстра, часть сотрудничество. Может стоит поставить наивный перед Mark and Sweep?

Matthieu M. 17.06.2013 16:17

Обратите внимание, что я говорил об инкрементных сборщиках мусора, а не о параллельных сборщиках мусора. Инкрементальные сборщики мусора тривиальны. Реальные параллельные сборщики мусора, как правило, сложны, но простые параллельные сборщики мусора существуют (например, VCGC). Вы можете устранить неограниченные паузы GC, используя отдельные кучи с инкрементными GC (например, OCaml и Erlang) вместо глобального параллельного GC, но вам нужно глубоко копировать сообщения. Ваше утверждение, безусловно, верно в отношении наивной зачистки меток, но тогда никто не использует наивную метку. Производственные сборщики мусора, как правило, передаются поколению с копирующими коллекторами между молодыми поколениями и разметкой / сжатием для старого поколения.

J D 17.06.2013 16:52

@JonHarrop: Проблема, применительно к C и C++, заключается в том, что копирующие сборщики просто не работают: вы имеете право в C и C++ сохранять значение указателя в целое число и даже просто смещение по сравнению с вашим собственным адресом, поэтому GC не может отследить ссылки, чтобы обновить их при перемещении. Точно так же адреса могут использоваться как идентификаторы объектов, поэтому вы не можете копировать объект за спиной разработчика. И, конечно же, память легко распределяется между потоками. Это серьезно ограничивает возможные реализации GC.

Matthieu M. 17.06.2013 17:25

@MatthieuM .: Чтобы пойти еще дальше: предположим, что программа неоднократно выделяла 1000 объектов с помощью new, заполняет их некоторыми данными, преобразует их адреса в числа, отображает их в течение 1/60 секунды, а затем оставляет объекты без выполнения delete. Позже программа позволяет пользователю ввести число, преобразовать его в адрес и использовать в предположении, что оно представляет собой допустимый объект. Если число, которое вводит пользователь, было отображено как адрес объекта, стандарт C++ требует, чтобы адрес оставался действительным.

supercat 02.10.2013 01:37

@MatthieuM .: Хотя было бы довольно маловероятно, что пользователю удастся скопировать каждое отображаемое число [цифровая камера, безусловно, может захватывать произвольное заполнение экрана], а также любой объект, связанный с адресом, который исчез с экрана до того, как был записанный может быть безопасно удален, очевидно, что для программы было бы абсолютно невозможно узнать, какие адреса все еще существуют где-то вне компьютера, а какие нет.

supercat 02.10.2013 01:43

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

einpoklum 15.05.2020 00:05

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

В частности, стандарт C++ довольно осторожно определяет язык с точки зрения внешнего наблюдаемого поведения, а не того, как реализация достигает этого поведения. Однако в случае сборки мусора у является практически отсутствует наблюдаемое извне поведение.

Главная идея сборки мусора заключается в том, что он должен предпринять разумную попытку гарантировать успешное выделение памяти. К сожалению, практически невозможно гарантировать, что какое-либо выделение памяти будет успешным, даже если у вас работает сборщик мусора. В некоторой степени это верно в любом случае, но особенно в случае C++, потому что (вероятно) невозможно использовать копирующий сборщик (или что-то подобное), который перемещает объекты в памяти во время цикла сбора.

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

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

Тем не менее, он сильнее, чем было предложено для C++. предыдущее предложение [предупреждение: PDF] (который упал) вообще ничего не гарантирует. На 28 страницах предложения вы столкнулись с одним (ненормативным) примечанием, которое помешало внешнему наблюдению поведения:

[ Note: For garbage collected programs, a high quality hosted implementation should attempt to maximize the amount of unreachable memory it reclaims. —end note ]

По крайней мере, для меня это поднимает вопрос серьезный о рентабельности инвестиций. Мы собираемся сломать существующий код (никто точно не знает, сколько, но определенно совсем немного), предъявить новые требования к реализациям и новые ограничения на код, а то, что мы получим взамен, вполне возможно, совсем ничего?

Даже в лучшем случае мы получаем программы, которые на основе тестирование с Java, вероятно, потребуют примерно в шесть раз больше памяти для работы с той же скоростью, что и сейчас. Хуже того, сборка мусора была частью Java с самого начала - C++ налагает на сборщик мусора достаточно больше ограничений, поэтому он почти наверняка будет иметь равное соотношение затрат и выгод хуже (даже если мы выйдем за рамки того, что гарантировано предложением, и предположим, что это будет некоторая выгода).

Я бы резюмировал ситуацию математически: это сложная ситуация. Как известно любому математику, комплексное число состоит из двух частей: действительной и мнимой. Мне кажется, что мы имеем здесь реальные затраты, а выгоды (по крайней мере, в основном) мнимые.

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

supercat 24.07.2013 20:33

Даже в Java GC на самом деле не предназначен для выполнения чего-либо полезного AFAIK. Он мог бы назвать free для вас (я имею в виду free, аналогичный языку C). Но Java никогда не гарантирует вызова финализаторов или чего-то подобного. Фактически, C++ делает гораздо больше, чем Java, для выполнения операций записи в базу данных, очистки дескрипторов файлов и т. д. Java утверждает, что имеет "GC", но разработчики Java должны постоянно вызывать close(), и они должны быть очень осведомлены об управлении ресурсами, стараясь не вызывать close() слишком рано или слишком поздно. C++ избавляет нас от этого. ... (продолжение)

Aaron McDaid 16.10.2014 14:58

.. мой комментарий минуту назад не предназначен для критики Java. Я просто заметил, что термин «сборка мусора» - очень странный термин - он означает гораздо меньше, чем люди думают, и поэтому его трудно обсуждать, не понимая, что он означает.

Aaron McDaid 16.10.2014 15:01

@AaronMcDaid Это правда, что сборщик мусора вообще не помогает с ресурсами, не относящимися к памяти. К счастью, такие ресурсы выделяются довольно редко по сравнению с памятью. Более того, более 90% из них могут быть освобождены с помощью метода, который их назначил, поэтому try (Whatever w=...) {...} решает эту проблему (и вы получите предупреждение, когда забудете). Остальные тоже проблематичны с RAII. Вызов close() «все время» означает, что, возможно, один раз на десятки тысяч строк, так что это не так уж и плохо, в то время как память выделяется почти на каждой строке Java.

maaartinus 30.12.2017 17:19

Все технические разговоры слишком усложняют концепцию.

Если вы автоматически помещаете GC в C++ для всей памяти, подумайте о чем-то вроде веб-браузера. Веб-браузер должен загружать полный веб-документ И запускать веб-скрипты. Вы можете хранить переменные веб-скрипта в дереве документа. В БОЛЬШОМ документе в браузере с множеством открытых вкладок это означает, что каждый раз, когда сборщик мусора должен выполнить полный сбор, он также должен сканировать все элементы документа.

На большинстве компьютеров это означает, что будут возникать ОШИБКИ СТРАНИЦ. Итак, основная причина, по которой нужно ответить на этот вопрос, заключается в том, что будут возникать ОШИБКИ СТРАНИЦЫ. Вы узнаете это, когда ваш компьютер начнет делать много обращений к диску. Это связано с тем, что сборщик мусора должен задействовать много памяти, чтобы доказать недействительные указатели. Когда у вас есть добросовестное приложение, использующее много памяти, необходимость сканировать все объекты, каждая коллекция является разрушительной из-за ОШИБОК СТРАНИЦ. Ошибка страницы - это когда виртуальная память должна быть считана обратно в ОЗУ с диска.

Таким образом, правильное решение - разделить приложение на части, которым требуется сборщик мусора, и части, которые этого не делают. В случае приведенного выше примера веб-браузера, если дерево документа было выделено с помощью malloc, но javascript работал с GC, то каждый раз, когда GC запускает его, сканируется только небольшая часть памяти и все элементы PAGED OUT памяти для дерево документа не нужно возвращать обратно.

Чтобы лучше понять эту проблему, изучите виртуальную память и то, как она реализована в компьютерах. Все дело в том, что программе доступно 2 ГБ, когда оперативной памяти на самом деле не так много. На современных компьютерах с 2 ГБ ОЗУ для 32-битной системы это не проблема, если запущена только одна программа.

В качестве дополнительного примера рассмотрим полную коллекцию, которая должна отслеживать все объекты. Сначала вы должны просканировать все объекты, доступные через корни. Во-вторых, просканируйте все объекты, видимые на шаге 1. Затем просканируйте ожидающие деструкторы. Затем снова перейдите на все страницы и выключите все невидимые объекты. Это означает, что многие страницы могут заменяться и возвращаться несколько раз.

Итак, мой ответ кратко заключается в том, что количество ОШИБОК СТРАНИЦ, которые возникают в результате касания всей памяти, приводит к невозможности полного GC для всех объектов в программе, и поэтому программист должен рассматривать GC как вспомогательное средство для таких вещей, как скрипты. и база данных работают, но делайте обычные вещи с ручным управлением памятью.

И еще одна очень важная причина - это, конечно, глобальные переменные. Чтобы сборщик знал, что указатель на глобальную переменную находится в GC, ему потребуются определенные ключевые слова, и, таким образом, существующий код C++ не будет работать.

КОРОТКИЙ ОТВЕТ: Мы не знаем, как выполнять сборку мусора эффективно (с небольшими затратами времени и места) и всегда правильно (во всех возможных случаях).

ДЛИННЫЙ ОТВЕТ: Как и C, C++ - системный язык; это означает, что он используется, когда вы пишете системный код, например, операционную систему. Другими словами, C++ разработан, как и C, с наилучшей возможной представление в качестве основной цели. Стандарт языка не будет добавлять никаких функций, которые могут помешать достижению производительности.

Это ставит вопрос: почему сборка мусора снижает производительность? Основная причина в том, что когда дело доходит до реализации, мы [компьютерщики] не знаем, как выполнять сборку мусора с минимальными накладными расходами во всех случаях. Следовательно, компилятор C++ и исполняющая система не могут постоянно эффективно выполнять сборку мусора. С другой стороны, программист на C++ должен знать свой дизайн / реализацию, и он лучший человек, чтобы решить, как лучше всего выполнить сборку мусора.

Наконец, если контроль (оборудование, детали и т. д.) И производительность (время, пространство, мощность и т. д.) Не являются основными ограничениями, то C++ не является инструментом записи. Другой язык может служить лучше и предлагать более [скрытое] управление средой выполнения с необходимыми накладными расходами.

Страуструп сделал несколько хороших комментариев по этому поводу на конференции Going Native в 2013 году.

Просто пропустите примерно 25 минут 50 секунд в это видео. (Я бы порекомендовал посмотреть все видео целиком, но здесь я перехожу к материалам о сборке мусора.)

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

В современном C++ и том, что есть в C++ 11, сборка мусора больше нежелательна, за исключением ограниченных обстоятельств. Фактически, даже если хороший сборщик мусора встроен в один из основных компиляторов C++, я думаю, что он не будет использоваться очень часто. Избежать GC будет не сложнее Полегче.

Он показывает этот пример:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if (x<100) throw SomeException{};
    if (x<200) return;
    delete p;
}

Это небезопасно в C++. Но в Java это тоже небезопасно! В C++, если функция возвращается раньше, delete никогда не будет вызван. Но если у вас была полная сборка мусора, например, в Java, вы просто получаете предположение, что объект будет уничтожен «в какой-то момент в будущем» (Обновлять:, это еще хуже, чем это. Java обещает нет вызвать финализатор когда-либо - это, возможно, никогда не назовут). Этого недостаточно, если Gadget содержит дескриптор открытого файла, или соединение с базой данных, или данные, которые вы буферизовали для записи в базу данных позже. Мы хотим, чтобы гаджет был уничтожен сразу после его завершения, чтобы как можно скорее освободить эти ресурсы. Вы не хотите, чтобы ваш сервер базы данных боролся с тысячами подключений к базе данных, которые больше не нужны - он не знает, что ваша программа завершена.

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

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if (x<100) throw SomeException{};
    if (x<200) return;
}

Для ввода требуется меньше символов. Ему не мешает new. Это не требует, чтобы вы вводили Gadget дважды. В конце функции объект уничтожается. Если это то, что вы хотите, это очень интуитивно понятно. Gadget ведет себя так же, как int или double. Предсказуемый, легкий для чтения, легкий в обучении. Все является «ценностью». Иногда большое значение, но значениям легче научить, потому что у вас нет того «действия на расстоянии», которое вы получаете с указателями (или ссылками).

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

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

Это может показаться проблемой эффективности. Что делать, если я хочу вернуть гаджет от foo()? Семантика перемещения C++ 11 упрощает возврат больших объектов. Просто напишите Gadget foo() { ... }, и он будет работать, причем быстро. Вам не нужно возиться с && самостоятельно, просто возвращайте значения по значению, и язык часто сможет выполнить необходимые оптимизации. (Даже до C++ 03 компиляторы на удивление хорошо избегали ненужного копирования.)

Как сказал Страуструп в другом месте видео (перефразируя): «Только компьютерный ученый будет настаивать на копировании объекта, а затем уничтожении оригинала. (Аудитория смеется). Почему бы просто не переместить объект прямо в новое место? Это то, чего ожидают люди (а не компьютерные ученые)».

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

Если у вас это не сработает, вы можете использовать unique_ptr, а в противном случае - shared_ptr. Хорошо написанный C++ 11 короче, легче для чтения и обучения, чем многие другие языки, когда дело касается управления памятью.

Сборку мусора следует использовать только для объектов, которые не получают ресурсы (т. Е. Просят другие объекты делать что-то от их имени «до дальнейшего уведомления»). Если Gadget не просит ничего делать от его имени, исходный код был бы совершенно безопасным в Java, если бы бессмысленный (для Java) оператор delete был удален.

supercat 02.04.2015 01:13

@supercat, интересны объекты со скучными деструкторами. (Я не определил «скучный», но в основном деструкторы, которые никогда не нужно вызывать, за исключением освобождения памяти). Отдельный компилятор может обрабатывать shared_ptr<T> специально, когда T «скучный». Он может решить фактически не управлять счетчиком ссылок для этого типа, а вместо этого использовать GC. Это позволит использовать GC без необходимости уведомления разработчика. shared_ptr можно рассматривать просто как указатель GC для подходящего T. Но в этом есть ограничения, и это замедлит работу многих программ.

Aaron McDaid 02.04.2015 09:30

В хорошей системе типов должны быть разные типы для объектов кучи, управляемых сборщиком мусора и RAII, поскольку некоторые шаблоны использования очень хорошо работают с одним и очень плохо с другим. В .NET или Java оператор string1=string2; будет выполняться очень быстро независимо от длины строки (это буквально не что иное, как загрузка регистров и хранилище регистров) и не требует какой-либо блокировки, чтобы гарантировать, что если вышеуказанный оператор будет выполняться во время string2 записывается, string1 будет содержать либо старое значение, либо новое значение без неопределенного поведения).

supercat 02.04.2015 18:36

В C++ присвоение shared_ptr<String> требует большой скрытой синхронизации, а назначение String может вести себя странно, если переменная читается и записывается одновременно. Случаи, когда кто-то хотел бы писать и читать String одновременно, не очень распространены, но могут возникнуть, например, если некоторый код хочет сделать текущие отчеты о состоянии доступными для других потоков. В .NET и Java такие вещи просто «работают».

supercat 02.04.2015 18:38

Не могли бы вы прояснить кое-что, чтобы мы сравнивали подобное с подобным? В C++ вы имеете в виду неизменяемый класс String, как и класс String в Java? И поэтому вы никогда не назначаете String. Вы можете назначить String* или shared_ptr<String> (или String Java-справочник в Java). Меня смущает ваше обсуждение «присваивания» и «чтения и записи», потому что я предпочел бы ясность в том, что базовый объект неизменен.

Aaron McDaid 02.04.2015 18:58

В Java и .NET наиболее распространенным типом, используемым для инкапсуляции последовательности символов, является String, неизменяемый ссылочный тип, который используется, потому что это единственное, что может дать практическую семантику значения в Java, и единственное, что может дать семантику значения. в .NET без лишнего бокса [в .NET структура, инкапсулирующая char[], может быть лучше, чем объект кучи, за исключением проблемы бокса]. В целях сравнения я бы сказал, что его следует сравнивать с любым типом в C++, который наиболее эффективно представлял бы ...

supercat 02.04.2015 20:07

... последовательность символов с семантикой значения. Для некоторых шаблонов использования лучше всего подойдет C++ String; для других shared_ptr<string> будет лучше. Однако есть некоторые шаблоны использования, для которых ни один тип C++ не может достичь эффективности и семантики строк GC, поскольку требуется единственная синхронизация с самим GC, и он обладает волшебными способностями для принудительной синхронизации с потоками, у которых нет других механизмов синхронизации. встроен в них.

supercat 02.04.2015 20:11

Извините, что повторяюсь, но является ли ваш C++ String неизменным? Вы говорили ранее о чтении и письме, но писать не имеет смысла с неизменным String. что-то вроде using String = const std::vector<char>;. Я не могу думать о других проблемах, пока не выясню, что это за тип.

Aaron McDaid 02.04.2015 20:43

Намерение состоит в том, чтобы иметь переменную, которая в разное время может быть инкапсулирована различными последовательностями символов, независимо от любой другой переменной строкового типа. В C++ это может быть достигнуто за счет того, что переменная инкапсулирует изменяемую строку напрямую, указатель на совместно используемый экземпляр строки, который никогда не будет изменен, указатель на экземпляр строки, который будет изменен только в том случае, если он никогда не был совместно использован. (и в противном случае будет заменен новым экземпляром, который владелец может затем изменить). Ключевым моментом является то, что Переменная может измениться.

supercat 02.04.2015 21:17

Короче говоря, в целях честного сравнения используйте любой тип типа, который позволил бы C++ достичь наилучшей производительности для каждого шаблона использования. То, что .NET и Java используют изменяемую ссылку на неизменяемый объект, поскольку их строковый тип не означает, что C++ нельзя разрешить использовать что-то лучшее, если это возможно.

supercat 03.04.2015 01:26

"Java не обещает когда-либо вызывать финализатор - возможно, он никогда не будет вызван" Интересный факт: ранние версии Java позволяли запускать финализатор, пока объекты все еще использовались; собственно говоря, еще был доработан ctor !!! Очевидно, это не вызвало много жалоб от толпы «C++ - отстой, потому что порядок f()+g() не детерминирован, а правила Java - потому что он детерминирован».

curiousguy 14.01.2017 14:55

@curiousguy ничего не изменилось, если вы не примете правильных мер предосторожности, Java по-прежнему позволяет вызывать финализатор сразу после завершения конструктора. Вот пример из реальной жизни: «finalize () вызывал строго достижимые объекты в Java 8». Вывод - никогда не использовать эту функцию, что почти все согласны с исторической ошибкой дизайна языка. Когда мы следуем этому совету, язык дает нам любимый детерминизм.

Holger 09.06.2020 11:50

Когда мы сравниваем C++ с Java, мы видим, что C++ не был разработан с учетом неявной сборки мусора, в то время как Java.

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

В дополнение к этому, C++ - это язык, который предназначен для работы как автономный исполняемый файл вместо сложной среды выполнения.

Вобщем: Да, можно было бы добавить сборку мусора в C++, но для непрерывности лучше этого не делать.

Освобождение памяти и запуск деструкторов - слишком разные вопросы. (В Java нет деструкторов, это PITA.) GC освобождает память, но не запускает dtors.

curiousguy 14.01.2017 14:50

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

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

Интересной особенностью многих фреймворков, собирающих мусор, является то, что ссылка на объект определяется не содержащимися в ней битовыми шаблонами, а взаимосвязью между битами, содержащимися в ссылке на объект, и другой информацией, хранящейся в другом месте. В C и C++, если битовый шаблон, хранящийся в указателе, идентифицирует объект, этот битовый шаблон будет идентифицировать этот объект до тех пор, пока объект не будет явно уничтожен. В типичной системе GC объект может быть представлен битовым шаблоном 0x1234ABCD в один момент времени, но следующий цикл GC может заменить все ссылки на 0x1234ABCD ссылками на 0x4321BABE, после чего объект будет представлен последним шаблоном. Даже если бы нужно было отобразить битовый шаблон, связанный со ссылкой на объект, а затем прочитать его обратно с клавиатуры, не было бы никаких ожиданий, что тот же битовый шаблон можно было бы использовать для идентификации того же объекта (или любого объекта).

Это действительно хороший момент, я недавно украл некоторые биты из своих указателей, потому что в противном случае было бы глупое количество промахов кеша.

Passer By 18.06.2017 17:44

@PasserBy: Интересно, сколько приложений, использующих 64-битные указатели, выиграют больше от использования масштабированных 32-битных указателей в качестве ссылок на объекты или хранения почти всего в 4 ГиБ адресного пространства и использования специальных объектов для хранения / извлечения данных с высоких -скорость хранения запредельная? У машин достаточно ОЗУ, поэтому потребление ОЗУ 64-битными указателями может не иметь значения, Кроме - что они потребляют вдвое больше кеша, чем 32-битные указатели.

supercat 19.06.2017 17:32

В основном по двум причинам:

  1. Потому что он не нужен (ИМХО)
  2. Потому что он в значительной степени несовместим с RAII, который является краеугольным камнем C++.

C++ уже предлагает ручное управление памятью, выделение стека, RAII, контейнеры, автоматические указатели, интеллектуальные указатели ... Этого должно быть достаточно. Сборщики мусора предназначены для ленивых программистов, которые не хотят тратить 5 минут на размышления о том, кому какие объекты должны принадлежать и когда следует освобождать ресурсы. В C++ мы поступаем иначе.

Существует множество (более новых) алгоритмов, которые сложно реализовать без сборки мусора. Время шло. Инновации также связаны с новыми идеями, которые хорошо подходят для языков высокого уровня (сборка мусора). Попробуйте перенести любой из них на C++ без сборщика мусора, вы заметите неровности на дороге. (Я знаю, что должен привести примеры, но я как бы тороплюсь прямо сейчас. Извините. Одна, о которой я могу думать прямо сейчас, вращается вокруг постоянных структур данных, где подсчет ссылок не работает.).

BitTickler 07.08.2018 19:27

tl; dr: Потому что современный C++ не требует сборки мусора.

Часто задаваемые вопросы Бьярна Страуструпа ответ по этому поводу говорит:

I don't like garbage. I don't like littering. My ideal is to eliminate the need for a garbage collector by not producing any garbage. That is now possible.


Ситуация для кода, написанного в наши дни (C++ 17 и следующий за официальным Основные принципы), выглядит следующим образом:

  • Большая часть кода, связанного с владением памятью, находится в библиотеках (особенно в тех, которые предоставляют контейнеры).
  • Большая часть кода использовать, включающего владение памятью, следует за RAII шаблон, поэтому выделение выполняется при построении и освобождение при уничтожении, что происходит при выходе из области, в которой что-то было выделено.
  • Ты не выделять и не освобождать память напрямую.
  • Необработанные указатели не владею памятью (если вы следовали рекомендациям), поэтому вы не можете утечь, передавая их.
  • Если вам интересно, как вы собираетесь передавать начальные адреса последовательностей значений в памяти - вы сделаете это с помощью охватывать; не нужен необработанный указатель.
  • Если вам действительно нужен собственный «указатель», вы используете C++ 'интеллектуальные указатели в стандартной библиотеке - они не могут протекать и прилично эффективны (хотя ABI может мешать этого). В качестве альтернативы вы можете передать право собственности через границы области с помощью "указатели владельцев". Они встречаются редко и должны использоваться явно; но когда они приняты - они позволяют проводить хорошую статическую проверку на предмет утечек.

«Ах да? А как насчет ...

... если я просто напишу код так, как раньше писали на C++? "

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

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

... если я reintrepret_cast? Или выполнять арифметические операции со сложными указателями? Или другие подобные хаки? "

В самом деле, если вы задумаетесь, вы можете написать код, который все портит, несмотря на то, что хорошо соблюдает правила. Но:

  1. Вы будете делать это редко (с точки зрения мест в коде, не обязательно с точки зрения доли времени выполнения)
  2. Вы бы сделали это намеренно, а не случайно.
  3. Это выделится в кодовой базе, соответствующей руководящим принципам.
  4. Это такой код, в котором вы все равно обойдете сборщик мусора на другом языке.

... развитие библиотеки? "

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


Так что, как сказал Бьярн: на самом деле нет никакой мотивации собирать мусор в целом, так как вы все, но старайтесь не производить мусор. С C++ сборка мусора не проблема.

That is not to say GC isn't an interesting problem for certain specific applications, when you want to employ custom allocation and de-allocations strategies. For those you would want custom allocation and de-allocation, not a language-level GC.

Ну, он нужен (нужен GC), если вы перемалываете строки. Представьте, что у вас есть большие массивы строк (представьте, сотни мегабайт), которые вы строите по частям, затем обрабатываете и перестраиваете в разные длины, удаляя неиспользуемые, объединяя другие и т. д. знаю, потому что мне пришлось переключиться на языки высокого уровня, чтобы справиться. (Конечно, вы также можете создать свой собственный G C).

www-0av-Com 27.01.2018 16:42

@ user1863152: В этом случае будет полезен специальный распределитель. Это по-прежнему не требует встроенного в язык сборщика мусора ...

einpoklum 27.01.2018 16:50

кому: эйнпоклум: правда. Это просто лошадь для курсов. Моим требованием было обрабатывать динамически изменяющиеся галлоны информации о транспортных пассажирах. Увлекательная тема .. Действительно сводится к философии программного обеспечения.

www-0av-Com 27.01.2018 16:58

GC, как обнаружили мир Java и .NET, наконец, имеет огромную проблему - он не масштабируется. Когда у вас есть миллиарды живых объектов в памяти, как в наши дни с любым нетривиальным программным обеспечением, вам придется начать писать код, чтобы скрыть вещи от GC. Наличие GC в Java и .NET - это бремя.

Zach Saw 25.09.2019 15:34

@ZachSaw: Какая часть программ когда-либо будет иметь в памяти хотя бы один миллиард живых объектов? Вы говорите, что все программы, в которых больше всего ничего нет, тривиальны?

supercat 16.11.2020 18:56

@supercat: Может, он имел в виду миллионы объектов? Хотя и в этом случае это не так для «любого нетривиального» программного обеспечения.

einpoklum 16.11.2020 19:22

@einpoklum: GC может нормально работать в масштабе «миллионы объектов». А для задач, которые хорошо подходят для модели, основанной на совместно используемых неизменяемых объектах, она, вероятно, лучше масштабируется для многоядерных систем, чем RAII, поскольку не требует взаимосвязанного счетчика ссылок.

supercat 16.11.2020 19:26

@supercat: Я не утверждал, что сборщик мусора не работает - пожалуйста, не путайте мой ответ с комментарием Закса. Сказав это - он не обязательно масштабируется лучше, чем владение RAII-базой, потому что большая часть владения RAII не использует счетчики ссылок или блокировки. Кроме того, в C++ вы бы избегали использования большого количества std::shared_ptr - это звучит как обещание огромных потерь времени. Тем не менее, если ваше программное обеспечение работает именно так, то да, сборщик мусора может масштабироваться лучше, чем отдельные общие указатели.

einpoklum 16.11.2020 19:41

@supercat под нетривиальным я имел в виду не связанные с памятью тривиальные, то есть приложения с большой памятью, такие как наша запатентованная высокопроизводительная система поиска документов, которая способна обрабатывать миллионы объектов, сортировать на лету и т.д. сам сайт StackOverflow. Разработчики написали множество технических блогов на эту тему.

Zach Saw 17.11.2020 09:17

@einpoklum: У разных задач разные требования. Я не думаю, что какой-то один подход может быть оптимальным для всех. RAII хорош для приложений, в которых все объекты имеют четкое право собственности; Сборщик мусора с трассировкой поколений хорош для приложений, где имеется большое количество объектов, которые оказываются равными, но не имеют других значимых взаимосвязей и имеют полезные сроки жизни, которые хорошо соответствуют ожиданиям поколений.

supercat 17.11.2020 18:44

@supercat: Последний случай, хотя и важен, это не то, что просто произойдет в вашем приложении без вашего ведома; и, таким образом, должен быть достаточно хорошо обслуживаемым механизмом сборки мусора на основе библиотеки (если действительно невозможно избежать мусора).

einpoklum 17.11.2020 22:00

@einpoklum: Системы, которые собираются использовать трассировку сборщиков мусора, должны интегрировать их в дизайн, поскольку такая интеграция позволит им делать то, что не сможет сделать основанный на библиотеке. Например, если foo содержит последнюю ссылку на объект, а поток 1 выполняет foo = bar; примерно в то же время, что другой выполняет boz = foo;, сборщик, интегрированный в Framework, сможет обрабатывать все связанные угловые случаи синхронизации потоков без какой-либо потребности в памяти. барьеры в коде любого потока. GC потребуется принудительно ввести некоторые дорогостоящие барьеры памяти ...

supercat 17.11.2020 22:54

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

supercat 17.11.2020 22:58

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

supercat 17.11.2020 23:06

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

supercat 17.11.2020 23:09

возможно, это лучший подход, который имеет преимущество обоих миров microsoft.com/en-us/research/publication/…

Zach Saw 18.12.2020 04:46

Навязанная сборка мусора - это действительно сдвиг парадигмы с низкого уровня на высокий.

Если вы посмотрите на то, как строки обрабатываются на языке со сборкой мусора, вы обнаружите, что они ТОЛЬКО допускают высокоуровневые функции манипулирования строками и не разрешают двоичный доступ к строкам. Проще говоря, все строковые функции сначала проверяют указатели, чтобы увидеть, где находится строка, даже если вы только рисуете байт. Поэтому, если вы выполняете цикл, который обрабатывает каждый байт в строке на языке со сборкой мусора, он должен вычислять базовое местоположение плюс смещение для каждой итерации, потому что он не может знать, когда строка переместилась. Затем вам нужно подумать о кучах, стеках, потоках и т. д. И т. Д.

Другие вопросы по теме