Подтвердить что ты не робот

Зачем делать короткие и долгоживущие объекты различиями в сборке мусора?

Я часто читал, что в объектах Sun JVM short-living ( "относительно новые объекты" ) можно собрать мусор более эффективно, чем долгоживущие объекты ( "относительно старые объекты" )

  • Почему это так?
  • Является ли это специфическим для Sun JVM или это результат общего принципа сбора мусора?
4b9b3361

Ответ 1

Большинство приложений Java создают объекты Java, а затем быстро отбрасывают их, например. вы создаете некоторые объекты в методе, а затем, когда вы выходите из метода, весь объект умирает. Большинство приложений ведут себя таким образом, и большинство людей склонны кодировать свои приложения таким образом. Куча Java грубо разбита на 3 части, постоянное, старое (долгоживущее) поколение и молодое (короткоживущее) поколение. Молодой ген далее разбивается на S1, S2 и eden. Это просто кучи.

Большинство объектов создаются в молодом поколении. Идея здесь в том, что, поскольку уровень смертности объектов высок, мы быстро создаем их, используем их и затем отбрасываем. Скорость - это сущность. Когда вы создаете объекты, молодой ген заполняется до тех пор, пока не произойдет небольшое GC. В младшем GC все объекты, которые являются живыми, копируются из eden и говорят S2-S1. Затем "указатель" находится на eden и S2.

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

Вы можете настроить параметр использования с помощью

java -XX:MaxTenuringThreshold=16 

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

java -XX:-PrintTenuringDistribution

Ответ 2

(см. выше пояснения для более общих GC.. это ответы, почему новый дешевле GC, чем старый).

Причина, по которой eden может быть очищена быстрее, проста: алгоритм пропорционален количеству объектов, которые выживут GC в пространстве eden, не пропорционально количеству живых объектов во всей куче. т.е.: если у вас средний показатель смертности объекта 99% в eden (то есть: 99% объектов не выживают GC, что не является ненормальным), вам нужно всего лишь посмотреть и скопировать это 1%. Для "старого" GC все живые объекты в полной куче необходимо маркировать/прокатывать. Это значительно дороже.

Ответ 3

Это коллективная сборка мусора. В наши дни он пользовался довольно широко. Подробнее см. Здесь: (wiki).

По сути, GC предполагает, что новые объекты с большей вероятностью станут недоступными, чем более старые.

Ответ 4

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

Вот пример:

public String concatenate(int[] arr) { 
  StringBuilder sb = new StringBuilder();
  for(int i = 0; i < arr.length; ++i)
    sb.append(i > 0 ? "," : "").append(arr[i]);
  return sb.toString();
}

Объект sb станет мусором, как только метод вернется.

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

Ответ 5

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

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

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

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

Ответ 6

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

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

Ответ 7

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

В JVM Hotspot новые объекты распределяются в так называемой области Eden. Когда эта область заполняется, JVM будет охватывать область Eden (что не занимает слишком много времени, потому что оно не так велико). Объекты, которые все еще живы, перемещаются в область Survivor, а остальные отбрасываются, освобождая Eden для следующего поколения. Когда коллекции Eden недостаточно, сборщик мусора переходит к более старым поколениям (что требует больше работы).

Ответ 8

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

Поэтому объекты создаются в небольшом пространстве (называемом "Eden" или "young gen" ). Через некоторое время все объекты, которые могут быть достигнуты, скопированы (= дорогие) из этого пространства, и тогда пространство объявляется пустым (поэтому Java эффективно забывает обо всех недостижимых объектах, поэтому у них нет стоимости, поскольку они не должны быть скопированы). Со временем долговечные объекты перемещаются в "более старые" пространства, а более старые пространства уменьшаются реже, чтобы уменьшить накладные расходы GC (например, каждый N запусков, GC будет запускать старое пространство вместо пространства eden).

Просто для сравнения: если вы выделяете объект в C/С++, вам нужно вызвать free() плюс деструктор для каждого из них. Это одна из причин, по которой GC работает быстрее, чем традиционное ручное управление памятью.

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