Как узнать, где в памяти находится точный молодой / старый ген?
Недавно мне удалось получить адрес объекта с помощью класса sun.misc.Unsafe.
И теперь я пытаюсь программно найти фактическое поколение, где находится мой объект. Для этого я хочу знать начальную и конечную точки каждого поколения. Если Java (Oracle JVM) предоставляет какие-либо инструменты для решения этой проблемы? Я полагаю, что нет, поскольку даже разные GC требуют разных структур памяти (например, G1), и это делает задачу еще более интересной:)
То, что я хочу знать здесь, это просто пара чисел, представляющих границы поколений в памяти, например:
young gen: start point - 8501702198
end point - 9601256348
Хотелось бы услышать даже ваши самые сумасшедшие идеи о черной магии, которая позволяет определить, где различные области поколения помещены в память.
2 ответа
Это возможно с HotSpot JVM, хотя как-то сложно.
Основная идея заключается в использовании VMStructs
- информация о внутренних константах и типах HotSpot, встроенных прямо в общую библиотеку JVM.
Например, ParallelScavengeHeap::_young_gen
Глобальная переменная ВМ содержит указатель на PSYoungGen
структура, которая имеет _virtual_space
член с границами параллельного коллекционера молодого поколения. Так же, GenCollectedHeap::_gch
глобальный указывает на структуру, описывающую поколения коллекторов CMS.
Я сделал пробный проект для демонстрации использования VMStructs. Это чистая Java, никаких дополнительных библиотек не требуется, но она глубоко опирается на недокументированные внутренние компоненты JDK и может работать не на всех версиях Java. Я проверил это на JDK 8u40 и JDK 7u80 в Windows и Linux.
- JVM.java - код для чтения VMStructs;
- HeapInfo.java - пример программы для получения адресов поколений кучи.
Я не знаю, как точно определить границы молодого и старого поколений (даже не уверен, что это возможно). В случае G1 это стало еще сложнее, потому что разрешено иметь более одного региона старого поколения из-за необычной структуры кучи G1.
Но вы можете использовать сложную эвристику, чтобы определить, принадлежит ли объект старому поколению или нет, не зная границ поколений.
Давайте использовать некоторые секретные знания о магии горячих точек: каждый объект содержит заголовок со всей необходимой информацией о блокировке, хэш-коде личности и, что наиболее важно, возрасте. Извлечение возраста будет выглядеть так:
return unsafe.getByte(targetObject, 0L) & 0x78;
где 0x78 - соответствующая маска в заголовке объекта для его возраста (биты с 4-го по 7-й включительно).
получать MaxTenuringThreshold
параметр через API управления:
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(
server,
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
int threshold = Integer.valueOf(bean.getVMOption("MaxTenuringThreshold").getValue());
Теперь вы знаете возраст объекта и порог владения вашим приложением, поэтому вы можете предположить, что если возраст больше, чем порог, то это в старом поколении.
ВНИМАНИЕ: это эвристика, основанная на магии и секретных знаниях.
- Это не сработает, если кто-то синхронизирует целевой объект, пока вы читаете его возраст, потому что виртуальная машина переместит заголовок в стек и заменит заголовок указателем на стек
- Это не будет работать с
XX:+UseAdaptiveSizePolicy
, так что вы должны явно отключить его. - Некоторый объект может быть выделен прямо в старом поколении (например, из-за его размера)
- Тип I подвержен ошибкам
- Этот подход является незаконным, небезопасным и может быть неправильным и зависит от jvm