Как узнать, где в памяти находится точный молодой / старый ген?

Недавно мне удалось получить адрес объекта с помощью класса 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());

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

ВНИМАНИЕ: это эвристика, основанная на магии и секретных знаниях.

  1. Это не сработает, если кто-то синхронизирует целевой объект, пока вы читаете его возраст, потому что виртуальная машина переместит заголовок в стек и заменит заголовок указателем на стек
  2. Это не будет работать с XX:+UseAdaptiveSizePolicy, так что вы должны явно отключить его.
  3. Некоторый объект может быть выделен прямо в старом поколении (например, из-за его размера)
  4. Тип I подвержен ошибкам
  5. Этот подход является незаконным, небезопасным и может быть неправильным и зависит от jvm
Другие вопросы по тегам