Совместное использование классов OpenJ9 между разными приложениями

Возможно ли/разумно ли использовать функцию OpenJ9 совместного использования классов, чтобы уменьшить использование памяти отдельные экземпляры разных приложений?

Подробнее:

Похоже, что эта функция предназначена для сокращения использования памяти при запуске нескольких экземпляров одного и того же приложения на одном компьютере (например, узел Kubernetes). Это правильно?

Чего я хотел бы добиться, так это того, чтобы приложения, которые случайно запускаются на одном и том же узле Kubernetes, использовали один и тот же кеш. Поскольку приложения используют почти одни и те же библиотеки, должно быть нетривиальное количество данных, которыми можно поделиться. Однако то, что я испытываю, это кеши с высоким процентом устаревших классов и даже кеши, которые становятся заполненными на 100%, хотя я использую мягкие и жесткие ограничения в 1 ГБ.

В частности, я пытался использовать java -Xshareclasses:cacheDir=/openj9cache,groupAccess,name=somename,nonfatal,persistent,verbose -XX:SharedCacheHardLimit=1g -Xscmx1g <APP> для разных развертываний на одном и том же узле Kubernetes. В зависимости от того, какие приложения я запускаю, я вижу статистику кеша, например:

cache layer                          = 0
cache size                           = 1073741216
softmx bytes                         = 1073741824
free bytes                           = 0
Reserved space for AOT bytes         = -1
Maximum space for AOT bytes          = -1
Reserved space for JIT data bytes    = -1
Maximum space for JIT data bytes     = -1
Metadata bytes                       = 937848376
Metadata % used                      = 87%
Class debug area size                = 85868544
Class debug area used bytes          = 4246022
Class debug area % used              = 4%

ROMClass bytes                       = 36481772
AOT bytes                            = 12671632
JIT data bytes                       = 506596
Zip cache bytes                      = 0
Startup hint bytes                   = 360
Data bytes                           = 363936

# ROMClasses                         = 45685
# AOT Methods                        = 2997
# Classpaths                         = 30076
# URLs                               = 0
# Tokens                             = 0
# Zip caches                         = 0
# Startup hints                      = 3
# Stale classes                      = 40496
% Stale classes                      = 88%


Cache is 100% soft full
cache layer                          = 0
cache size                           = 1073741216
softmx bytes                         = 1073741824
free bytes                           = 0
Reserved space for AOT bytes         = -1
Maximum space for AOT bytes          = -1
Reserved space for JIT data bytes    = -1
Maximum space for JIT data bytes     = -1
Metadata bytes                       = 935010252
Metadata % used                      = 87%
Class debug area size                = 85868544
Class debug area used bytes          = 4745600
Class debug area % used              = 5%

ROMClass bytes                       = 40151980
AOT bytes                            = 11919936
JIT data bytes                       = 426448
Zip cache bytes                      = 0
Startup hint bytes                   = 120
Data bytes                           = 363936

# ROMClasses                         = 38554
# AOT Methods                        = 2950
# Classpaths                         = 22680
# URLs                               = 0
# Tokens                             = 0
# Zip caches                         = 0
# Startup hints                      = 1
# Stale classes                      = 6354
% Stale classes                      = 16%


Cache is 100% soft full
base address                         = 0x00007F3D8C059000
end address                          = 0x00007F3DCC000000
allocation pointer                   = 0x00007F3D8F10BB80

cache layer                          = 0
cache size                           = 1073741216
softmx bytes                         = 1073741824
free bytes                           = 706740256
Reserved space for AOT bytes         = -1
Maximum space for AOT bytes          = -1
Reserved space for JIT data bytes    = -1
Maximum space for JIT data bytes     = -1
Metadata bytes                       = 199367772
Metadata % used                      = 54%
Class debug area size                = 85868544
Class debug area used bytes          = 6499570
Class debug area % used              = 7%

ROMClass bytes                       = 51063680
AOT bytes                            = 29461056
JIT data bytes                       = 875612
Zip cache bytes                      = 0
Startup hint bytes                   = 360
Data bytes                           = 363936

# ROMClasses                         = 30778
# AOT Methods                        = 6624
# Classpaths                         = 8349
# URLs                               = 0
# Tokens                             = 0
# Zip caches                         = 0
# Startup hints                      = 3
# Stale classes                      = 3274
% Stale classes                      = 10%


Cache is 34% soft full

person Martin Kamp Jensen    schedule 07.09.2020    source источник
comment
Я попытался добавить bootClassesOnly к -Xshareclasses, и это имело значение! Теперь я вижу ~ 10% использования кеша и 0% устаревших классов.   -  person Martin Kamp Jensen    schedule 07.09.2020
comment
/openj9cache — это том hostPath Kubernetes, поэтому кеш используется совместно всеми приложениями, работающими на одном узле Kubernetes.   -  person Martin Kamp Jensen    schedule 08.09.2020


Ответы (1)


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

OpenJ9 выполняет проверку временных меток для jar-файлов на пути к классам. Это гарантирует, что класс в пути к классу не был обновлен, так как он сохранен в кеше, чтобы мы могли безопасно вернуть кешированный класс в загрузчик классов. Как только обновление найдено, исходный кешированный класс помечается как устаревший.

Использование подопции bootClassesOnly включает совместное использование классов только для классов начальной загрузки. Никто не обновляет пути к классам начальной загрузки, поэтому вы не видите устаревших классов.

Я вижу, что в статистике кеша тысячи путей к классам. Обновлялись ли вещи в этих путях к классам или они генерировались во время выполнения? Вы можете использовать подопцию -Xshareclasses printStats=classpath, чтобы отобразить все пути к классам. Если вы уверены, что классы не обновляются между запусками, вы можете использовать подопцию -Xshareclasses noTimestampChecks, чтобы отключить проверку временных меток.

person hangshao    schedule 08.09.2020
comment
Цифры, которые вы видите, взяты из трех разных кешей на трех разных узлах Kubernetes. В проведенном мной эксперименте было развернуто около 70 приложений (JVM в контейнерах Docker) на более чем 10 узлах Kubernetes. Приложения имеют много общего (Spring, внутренние/внешние библиотеки), но они не идентичны — не все приложения используют одни и те же части Spring и внутренние/внешние библиотеки. Другими словами, пути к классам приложений имеют много одинаковых JAR-файлов, но не совсем одинаковые, и порядок может отличаться, поскольку путь к классам просто исходит из папки lib рядом с app.jar. - person Martin Kamp Jensen; 09.09.2020
comment
Приложения, которые я развертываю, относятся к одной и той же сборке, поэтому, если все приложения используют какой-либо JAR, это должен быть один и тот же JAR. Я предполагаю, что по какой-то причине то, что я считал идентичными JAR, не рассматривается как идентичные JAR. Если бы они считались идентичными, я предполагаю, что устаревшие классы были бы равны 0%. Итак, хм, я думаю, мне нужно исследовать образы Docker двух приложений, которые в итоге были запущены на одном и том же узле Kubernetes (и, следовательно, использовали один и тот же кеш), чтобы попытаться выяснить, есть ли по какой-то причине различия в JAR-файлах. Я знаю, что пути к классам не идентичны, но некоторые их подмножества идентичны. - person Martin Kamp Jensen; 09.09.2020