Переверните ваш вопрос. Настоящий мотивирующий вопрос заключается в том, при каких обстоятельствах мы можем избежать затрат на сборку мусора?
Ну, во- первых, то , что есть затраты на сбор мусора? Есть две основные затраты. Во-первых, вы должны определить, что является живым ; это требует потенциально много работы. Во-вторых, вы должны сжать дыры , которые образуются, когда вы освобождаете что-то, что было распределено между двумя вещами, которые еще живы. Эти дыры расточительны. Но их сжатие тоже дорого.
Как мы можем избежать этих затрат?
Ясно, что если вы можете найти схему использования хранилища, в которой вы никогда не выделяете что-то долгоживущее, затем выделяете что-то недолговечное, а затем выделяете что-то долгоживущее, вы можете исключить стоимость дыр. Если вы можете гарантировать, что для некоторого подмножества вашего хранилища каждое последующее выделение будет короче, чем предыдущее в этом хранилище, то в этом хранилище никогда не будет дыр.
Но если мы решили проблему с дырами, мы решили и проблему сбора мусора . У вас есть что-то в этом хранилище, которое еще живо? Да. Было ли все выделено до того, как оно дольше будет жить? Да - это предположение, как мы устранили возможность дыр. Поэтому все, что вам нужно сделать, это сказать «живое ли последнее распределение?» и вы знаете, что в этом хранилище все живо.
Есть ли у нас набор распределений памяти, где мы знаем, что каждое последующее выделение является более коротким, чем предыдущее выделение? Да! Фреймы активации методов всегда уничтожаются в том порядке, в котором они были созданы, потому что они всегда короче, чем активация, которая их создала.
Поэтому мы можем хранить кадры активации в стеке и знать, что их никогда не нужно собирать. Если в стеке есть какой-либо кадр, весь набор кадров под ним является более долгоживущим, поэтому их не нужно собирать. И они будут уничтожены в обратном порядке, в котором они были созданы. Таким образом, стоимость сборки мусора исключается для фреймов активации.
Вот почему у нас есть временный пул в стеке, потому что это простой способ реализовать активацию метода без ущерба для управления памятью.
(Конечно, стоимость сбора мусора в памяти, на которую ссылаются ссылки на фреймах активации, все еще остается.)
Теперь рассмотрим систему потоков управления, в которой кадры активации не уничтожаются в предсказуемом порядке. Что произойдет, если кратковременная активация может привести к долгоживущей активации? Как вы можете себе представить, в этом мире вы больше не можете использовать стек для оптимизации необходимости сбора активаций. Набор активаций может снова содержать дыры.
C # 2.0 имеет эту функцию в виде yield return
. Метод, возвращающий доход, будет активирован позднее - в следующий раз, когда вызывается MoveNext - и когда это произойдет, это не предсказуемо. Поэтому информация, которая обычно находится в стеке для кадра активации блока итератора, вместо этого сохраняется в куче, где она собирается при сборке перечислителя.
Аналогично, функция «async / await», появившаяся в следующих версиях C # и VB, позволит вам создавать методы, чьи активации «приносят» и «возобновляют» в четко определенных точках во время действия метода. Поскольку кадры активации больше не создаются и не уничтожаются предсказуемым образом, вся информация, которая раньше хранилась в стеке, должна храниться в куче.
Это просто случайность истории, когда мы решили на протяжении нескольких десятилетий, что языки с фреймами активации, которые создаются и уничтожаются строго упорядоченным образом, были модными. Поскольку в современных языках все больше отсутствует это свойство, ожидайте увидеть все больше и больше языков, которые преобразуют продолжения в кучу, собираемую мусором, а не в стек.