Неужели объекты с большим количеством опорных полей (кроме массивов) опустошают производительность Hotspot JVM GC (s)?

Представьте, что я определяю класс с десятками ссылочных полей (вместо использования ссылочных массивов, таких как Object[]), и создавайте экземпляр этого класса довольно сильно в приложении.

Будет ли это влиять на производительность сборщика мусора в JVM Hotspot, когда он пересекает кучу для вычисления достижимых объектов? Или, может быть, это приведет к значительному дополнительному потреблению памяти для некоторых внутренних структур данных JVM или метаданных класса? Или это повлияет на эффективность приложения каким-то другим способом?

Являются ли эти аспекты конкретными для каждого алгоритма сборщика мусора в Hotspot или тех частях механики Hotspot, которые используются и используются всеми сборщиками мусора?

Ответ 1

Позвольте мне перефразировать вопрос. "Лучше ли иметь класс А или класс В ниже?"

class A {
  Target[] array;
}

class B {
  Target a, b, c, ..., z;
}

Обычные проблемы ремонтопригодности, несмотря на... С точки зрения VM, учитывая разрешенную ссылку на класс B, для достижения целевого поля требуется одно разыменование. В то время как в классе A это требует двух заметок, потому что нам также нужно прочитать массив.

Обработка ссылок на объекты в двух случаях несколько отличается: в классе A VM знает, что существует непрерывный массив ссылок, и поэтому ему не нужно ничего знать. В классе B виртуальная машина должна знать, какие поля являются ссылками (потому что, например, могут быть не ссылочные поля), что требует сохранения oop-карт в метаданных класса:

//  InstanceKlass embedded field layout (after declared fields):
...
//    [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
//      The embedded nonstatic oop-map blocks are short pairs (offset, length)
//      indicating where oops are located in instances of this 

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

Oop-карты создаются во время разбора классов, с помощью общего кода времени выполнения. Посетители, которые ходят "oop" -s для конкретного объекта, просматривают эти oop-карты, чтобы найти смещения для ссылок, и этот код также является частью общей среды выполнения. Таким образом, эти накладные расходы не зависят от реализации GC.

Соображения для выполнения:

  1. Oop-карты чередуются: прогоны смежных ссылочных полей образуют непрерывный блок oop-map, который можно было бы посещать так же, как и с непрерывным блоком oop в ссылочном массиве.
  2. Производительность GC (маркировки) зависит от количества ссылок, за которыми она должна следовать, и латентность памяти при разыменованиях будет эффектом первого порядка. Обратите внимание, что в классе A нам нужно пройти больше ссылок.
  3. Проверки с ограничениями на нуль-проверки и массивы, вероятно, будут иметь значение в случае класса А, если запрошенные индексы не являются постоянными, а длины массивов не известны на критических кодовых путях. Для сравнения, поля связаны статически, и их смещения всегда известны.

Поэтому, вероятно, нет смысла спрашивать о различии в обработке GC/runtime отдельных полей и массивов. Уход за местностью справки, вероятно, дает больший удар для доллара. Что подсказывает масштаб для класса B, связанный с накладными расходами на ремонтопригодность - как это делает довольно много трюков.