По какому принципу объекты перемещаются между секциями в heap в Java и когда происходит их перенос
Что проверяют
Знаете ли вы, что перемещение объектов это не отдельная операция, а побочный эффект копирующей сборки, и не застряли ли вы на схеме из старых учебников. Вопрос заодно показывает, работали ли вы с настройками памяти на живом сервисе.
Как отвечать
Объект рождается в Eden, обычно в TLAB, локальном буфере потока, поэтому аллокация обходится без синхронизации. Когда Eden заполняется, запускается молодая сборка: живые объекты копируются в свободный survivor, второй survivor очищается целиком. Копирующий сборщик платит за живые объекты, мёртвые ему ничего не стоят, отсюда и дешевизна молодой сборки при типичном коде, где почти всё умирает молодым.
У объекта в заголовке есть счётчик возраста, он увеличивается с каждой пережитой сборкой. При достижении порога (MaxTenuringThreshold, по умолчанию пятнадцать) объект продвигается в старое поколение, причём JVM понижает порог динамически, если survivor переполняется. Есть и досрочное продвижение: объект, не влезший в survivor, уезжает в старое поколение сразу, а очень крупные массивы могут выделяться там минуя Eden.
Старое поколение чистится отдельно, обычно конкурентной пометкой и смешанными сборками, а полная остановочная сборка это аварийный режим. Стоит добавить, что G1, сборщик по умолчанию начиная с JDK 9, не хранит поколения непрерывными областями: это набор регионов с ролями. Metaspace к куче не относится, и его переполнение не лечится увеличением Xmx.
Где спотыкаются
Рассказывают схему Eden, survivor и старого поколения как единственно возможную. Уточняющий вопрос про сборщик ставит в тупик, хотя у региональных сборщиков и у сборщиков без деления на поколения картина совсем другая.
Путают сборку старого поколения с полной. Это разные вещи: смешанная сборка захватывает часть старых регионов, а полная останавливает приложение целиком и обычно означает, что настройки не справились.
Считают, что System.gc() управляет переносом объектов или гарантирует очистку. Это только пожелание для JVM, а нередко и способ устроить долгую паузу на ровном месте.