По сути, я до сих пор узнал, что сборка мусора навсегда стирает любую структуру данных, на которую в данный момент не указывается. Но это только проверяет кучу для таких условий.
Почему он также не проверяет раздел данных (глобальные переменные, константы и т. Д.) Или стек? Что такого в куче, что это единственное, что мы хотим собирать мусором?
data
garbage-collection
Темный тамплиер
источник
источник
Ответы:
Сборщик мусора делает сканирование стек - чтобы увидеть , что все в куче в настоящее время используются (заостренный к) вещами в стеке.
Для сборщика мусора нет смысла рассматривать сбор стековой памяти, потому что стек не управляется таким образом: все в стеке считается «используемым». И память, используемая стеком, автоматически восстанавливается при возврате из вызовов методов. Управление памятью стекового пространства настолько просто, дешево и легко, что вы не захотите, чтобы сборщик мусора был вовлечен.
(Существуют системы, такие как smalltalk, где стековые фреймы являются первоклассными объектами, хранящимися в куче и мусоре, собираемом, как и все другие объекты. Но в наши дни это не популярный подход. JVM Java и CLR Microsoft используют аппаратный стек и непрерывную память .)
источник
Переверните ваш вопрос. Настоящий мотивирующий вопрос заключается в том, при каких обстоятельствах мы можем избежать затрат на сборку мусора?
Ну, во- первых, то , что есть затраты на сбор мусора? Есть две основные затраты. Во-первых, вы должны определить, что является живым ; это требует потенциально много работы. Во-вторых, вы должны сжать дыры , которые образуются, когда вы освобождаете что-то, что было распределено между двумя вещами, которые еще живы. Эти дыры расточительны. Но их сжатие тоже дорого.
Как мы можем избежать этих затрат?
Ясно, что если вы можете найти схему использования хранилища, в которой вы никогда не выделяете что-то долгоживущее, затем выделяете что-то недолговечное, а затем выделяете что-то долгоживущее, вы можете исключить стоимость дыр. Если вы можете гарантировать, что для некоторого подмножества вашего хранилища каждое последующее выделение будет короче, чем предыдущее в этом хранилище, то в этом хранилище никогда не будет дыр.
Но если мы решили проблему с дырами, мы решили и проблему сбора мусора . У вас есть что-то в этом хранилище, которое еще живо? Да. Было ли все выделено до того, как оно дольше будет жить? Да - это предположение, как мы устранили возможность дыр. Поэтому все, что вам нужно сделать, это сказать «живое ли последнее распределение?» и вы знаете, что в этом хранилище все живо.
Есть ли у нас набор распределений памяти, где мы знаем, что каждое последующее выделение является более коротким, чем предыдущее выделение? Да! Фреймы активации методов всегда уничтожаются в том порядке, в котором они были созданы, потому что они всегда короче, чем активация, которая их создала.
Поэтому мы можем хранить кадры активации в стеке и знать, что их никогда не нужно собирать. Если в стеке есть какой-либо кадр, весь набор кадров под ним является более долгоживущим, поэтому их не нужно собирать. И они будут уничтожены в обратном порядке, в котором они были созданы. Таким образом, стоимость сборки мусора исключается для фреймов активации.
Вот почему у нас есть временный пул в стеке, потому что это простой способ реализовать активацию метода без ущерба для управления памятью.
(Конечно, стоимость сбора мусора в памяти, на которую ссылаются ссылки на фреймах активации, все еще остается.)
Теперь рассмотрим систему потоков управления, в которой кадры активации не уничтожаются в предсказуемом порядке. Что произойдет, если кратковременная активация может привести к долгоживущей активации? Как вы можете себе представить, в этом мире вы больше не можете использовать стек для оптимизации необходимости сбора активаций. Набор активаций может снова содержать дыры.
C # 2.0 имеет эту функцию в виде
yield return
. Метод, возвращающий доход, будет активирован позднее - в следующий раз, когда вызывается MoveNext - и когда это произойдет, это не предсказуемо. Поэтому информация, которая обычно находится в стеке для кадра активации блока итератора, вместо этого сохраняется в куче, где она собирается при сборке перечислителя.Аналогично, функция «async / await», появившаяся в следующих версиях C # и VB, позволит вам создавать методы, чьи активации «приносят» и «возобновляют» в четко определенных точках во время действия метода. Поскольку кадры активации больше не создаются и не уничтожаются предсказуемым образом, вся информация, которая раньше хранилась в стеке, должна храниться в куче.
Это просто случайность истории, когда мы решили на протяжении нескольких десятилетий, что языки с фреймами активации, которые создаются и уничтожаются строго упорядоченным образом, были модными. Поскольку в современных языках все больше отсутствует это свойство, ожидайте увидеть все больше и больше языков, которые преобразуют продолжения в кучу, собираемую мусором, а не в стек.
источник
Наиболее очевидный ответ, и, возможно, не самый полный, заключается в том, что куча - это местоположение данных экземпляра. Под данными экземпляров мы подразумеваем данные, представляющие экземпляры классов, или объектов, которые создаются во время выполнения. Эти данные по своей природе являются динамическими, и количество этих объектов и, следовательно, объем занимаемой ими памяти известен только во время выполнения. Должна быть некоторая рана восстановления этой памяти, или долго работающие программы потребляли бы всю эту память со временем.
Память, используемая определениями классов, константами и другими статическими структурами данных, вряд ли будет увеличиваться без контроля. Так как в памяти имеется только одно определение класса для неизвестного числа экземпляров времени выполнения этого класса, имеет смысл, что этот тип структуры не представляет угрозы для использования памяти.
источник
Стоит помнить причину, по которой мы собираем мусор: потому что иногда трудно понять, когда освободить память. У вас действительно есть только эта проблема с кучей. Данные, размещенные в стеке, будут в конечном итоге освобождены, поэтому в этом нет никакой необходимости выполнять сборку мусора. Вещи в разделе данных, как правило, предполагаются для времени жизни программы
источник
Их размер является предсказуемым (постоянным, за исключением стека, а размер стека обычно ограничен несколькими МБ) и обычно очень мал (по крайней мере по сравнению с сотнями МБ, которые могут выделять большие приложения).
Динамически размещенные объекты обычно имеют небольшой интервал времени, в течение которого они достижимы. После этого на них нельзя будет ссылаться снова. Сравните это с записями в разделе данных, глобальными переменными и т. Д. Часто есть фрагмент кода, который ссылается на них напрямую (подумайте
const char *foo() { return "foo"; }
). Как правило, код не изменяется, поэтому ссылка остается, и каждый раз при вызове функции создается другая ссылка (которая может быть в любое время, насколько известно компьютеру - если вы не решите проблему остановки, то есть ). Таким образом, вы все равно не сможете освободить большую часть этой памяти, так как она всегда будет доступна.Во многих языках, предназначенных для сбора мусора, все, что принадлежит выполняемой программе, выделяется кучей. В Python просто нет никакого раздела данных и нет значений, выделенных стеком (есть ссылки на локальные переменные, и есть стек вызовов, но ни одно из них не является значением в том же смысле, что и
int
в C). Каждый объект находится в куче.источник
Как сказал ряд других респондентов, стек является частью корневого набора, поэтому он сканируется на наличие ссылок, но не «собирается», как таковой.
Я просто хочу ответить на некоторые комментарии, которые подразумевают, что мусор в стеке не имеет значения; это так, потому что это может привести к тому, что больше мусора в куче будет считаться достижимым. Добросовестные писатели виртуальных машин и компиляторов либо обнуляют, либо иным образом исключают мертвые части стека из сканирования IIRC, на некоторых виртуальных машинах есть таблицы, отображающие диапазоны ПК с битовой картой живучести слотов, а другие просто обнуляют слоты. Я не знаю, какая техника сейчас предпочтительнее.
Один термин, используемый для описания этого конкретного соображения, является безопасным для космоса .
источник
Позвольте мне указать на несколько фундаментальных заблуждений, которые вы и многие другие ошиблись:
"Почему сборщик мусора только подметает кучу?" Это наоборот. Только самые простые, самые консервативные и самые медленные сборщики мусора охватывают кучу. Вот почему они такие медленные.
Быстрые сборщики мусора только очищают стек (и, возможно, некоторые другие корни, такие как некоторые глобальные переменные для указателей FFI и регистры для живых указателей), и только копируют указатели, достижимые объектами стека. Остальное выбрасывается (то есть игнорируется), вообще не сканируя кучу.
Поскольку куча примерно в 1000 раз больше стека (ов), такой GC для сканирования стека обычно намного быстрее. ~ 15 мс против 250 мс на кучах нормального размера. Так как он копирует (перемещает) объекты из одного пространства в другое, его обычно называют копирующим сборщиком полупространства, ему требуется 2-кратная память, и, следовательно, он в основном не используется на очень маленьких устройствах, таких как телефоны с небольшим объемом памяти. Он компактен, поэтому в дальнейшем он очень удобен для кэширования, в отличие от простых сканеров меток и развертки.
Так как это движущиеся указатели, FFI, идентичность и ссылки являются хитрыми. Идентичность обычно решается с помощью случайных идентификаторов, ссылки с помощью указателей пересылки. FFI сложно, поскольку посторонние объекты не могут удерживать указатели на старое пространство. Указатели FFI обычно хранятся в отдельной области кучи, например, с медленным разметкой и разверткой, статическим коллектором. Или тривиальный malloc с пересчетом. Обратите внимание, что malloc имеет огромные накладные расходы, а пересчет еще больше.
Использование Mark & Sweep тривиально, но его не следует использовать в реальных программах, и особенно его нельзя преподавать как стандартный сборщик. Самым известным из таких копирующих коллекторов с быстрым сканированием стека называется чейни для двух пальцев .
источник
Что выделяется в стеке? Локальные переменные и обратные адреса (в C). Когда функция возвращается, ее локальные переменные отбрасываются. Это не обязательно, даже вредно, чтобы подмести стек.
Многие динамические языки, а также Java или C # реализованы на языке системного программирования, часто на C. Можно сказать, что Java реализован с функциями C и использует локальные переменные C, и, следовательно, сборщик мусора Java не нуждается в очистке стека.
Есть интересное исключение: сборщик мусора в Chicken Scheme очищает стек (в некотором роде), потому что его реализация использует этот стек в качестве пространства для сборки мусора первого поколения: см. Википедию Chicken Scheme Design .
источник