памяти - как увеличить память java




Java использует намного больше памяти, чем размер кучи(или размер корректно ограничивает объем памяти Docker) (4)

TL; DR

Подробное использование памяти обеспечивается деталями Native Memory Tracking (NMT) (в основном, метаданными кода и сборщиком мусора). В дополнение к этому компилятор Java и оптимизатор C1 / C2 потребляют память, не указанную в сводке.

Объем памяти может быть уменьшен с помощью флагов JVM (но есть последствия).

Определение размеров контейнера Docker должно выполняться путем тестирования с ожидаемой загрузкой приложения.

Деталь для каждого компонента

Общее пространство классов может быть отключено внутри контейнера, так как классы не будут совместно использоваться другим процессом JVM. Можно использовать следующий флаг. Это удалит общее пространство классов (17 МБ).

-Xshare:off

Серийный сборщик мусора имеет минимальный объем памяти за счет более длительного времени паузы во время обработки сборки мусора (см. Сравнение Алексея Шипилева между GC на одном изображении ). Это может быть включено с помощью следующего флага. Это может сэкономить до используемого пространства GC (48 МБ).

-XX:+UseSerialGC

Компилятор C2 может быть отключен с помощью следующего флага, чтобы уменьшить данные профилирования, используемые для принятия решения, оптимизировать или нет метод.

-XX:+TieredCompilation -XX:TieredStopAtLevel=1

Кодовое пространство уменьшается на 20 МБ. Кроме того, память вне JVM уменьшается на 80 МБ (разница между пространством NMT и пространством RSS). Оптимизирующему компилятору C2 требуется 100 МБ.

Компиляторы C1 и C2 могут быть отключены с помощью следующего флага.

-Xint

Объем памяти вне JVM теперь меньше общего выделенного пространства. Кодовое пространство уменьшается на 43 МБ. Осторожно, это сильно влияет на производительность приложения. Отключение компилятора C1 и C2 уменьшает используемую память на 170 МБ.

Использование компилятора Graal VM (замена C2) приводит к немного меньшему объему памяти. Это увеличивает объем памяти кода на 20 МБ и уменьшает объем внешней памяти JVM на 60 МБ.

Статья Java Memory Management для JVM предоставляет некоторую соответствующую информацию о различных пространствах памяти. Oracle предоставляет некоторые подробности в документации по отслеживанию собственной памяти . Дополнительные сведения об уровне компиляции в расширенной политике компиляции и при отключении C2 уменьшают размер кэша кода в 5 раз . Некоторые сведения о том, почему JVM сообщает о большей выделенной памяти, чем размер резидентного набора процесса Linux? когда оба компилятора отключены.

Для моего приложения память, используемая процессом Java, намного больше, чем размер кучи.

Система, в которой работают контейнеры, начинает испытывать проблемы с памятью, поскольку контейнер занимает гораздо больше памяти, чем размер кучи.

Размер кучи установлен на 128 МБ ( -Xmx128m -Xms128m ), в то время как контейнер занимает до 1 ГБ памяти. При нормальных условиях требуется 500 МБ. Если в контейнере докера есть ограничение ниже (например, mem_limit=mem_limit=400MB ), процесс уничтожается из-за mem_limit=mem_limit=400MB памяти в ОС.

Не могли бы вы объяснить, почему процесс Java использует гораздо больше памяти, чем куча? Как правильно определить лимит памяти Docker? Есть ли способ уменьшить объем памяти, занимаемой вне кучи процесса Java?

Я собираю некоторые подробности о проблеме, используя команду из Native memory tracking в JVM .

Из хост-системы я получаю память, используемую контейнером.

$ docker stats --no-stream 9afcb62a26c8
CONTAINER ID        NAME                                                                                        CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
9afcb62a26c8        xx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.0acbb46bb6fe3ae1b1c99aff3a6073bb7b7ecf85   0.93%               461MiB / 9.744GiB   4.62%               286MB / 7.92MB      157MB / 2.66GB      57

Внутри контейнера я получаю память, используемую процессом.

$ ps -p 71 -o pcpu,rss,size,vsize
%CPU   RSS  SIZE    VSZ
11.2 486040 580860 3814600
$ jcmd 71 VM.native_memory
71:

Native Memory Tracking:

Total: reserved=1631932KB, committed=367400KB
-                 Java Heap (reserved=131072KB, committed=131072KB)
                            (mmap: reserved=131072KB, committed=131072KB) 

-                     Class (reserved=1120142KB, committed=79830KB)
                            (classes #15267)
                            (  instance classes #14230, array classes #1037)
                            (malloc=1934KB #32977) 
                            (mmap: reserved=1118208KB, committed=77896KB) 
                            (  Metadata:   )
                            (    reserved=69632KB, committed=68272KB)
                            (    used=66725KB)
                            (    free=1547KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=1048576KB, committed=9624KB)
                            (    used=8939KB)
                            (    free=685KB)
                            (    waste=0KB =0.00%)

-                    Thread (reserved=24786KB, committed=5294KB)
                            (thread #56)
                            (stack: reserved=24500KB, committed=5008KB)
                            (malloc=198KB #293) 
                            (arena=88KB #110)

-                      Code (reserved=250635KB, committed=45907KB)
                            (malloc=2947KB #13459) 
                            (mmap: reserved=247688KB, committed=42960KB) 

-                        GC (reserved=48091KB, committed=48091KB)
                            (malloc=10439KB #18634) 
                            (mmap: reserved=37652KB, committed=37652KB) 

-                  Compiler (reserved=358KB, committed=358KB)
                            (malloc=249KB #1450) 
                            (arena=109KB #5)

-                  Internal (reserved=1165KB, committed=1165KB)
                            (malloc=1125KB #3363) 
                            (mmap: reserved=40KB, committed=40KB) 

-                     Other (reserved=16696KB, committed=16696KB)
                            (malloc=16696KB #35) 

-                    Symbol (reserved=15277KB, committed=15277KB)
                            (malloc=13543KB #180850) 
                            (arena=1734KB #1)

-    Native Memory Tracking (reserved=4436KB, committed=4436KB)
                            (malloc=378KB #5359) 
                            (tracking overhead=4058KB)

-        Shared class space (reserved=17144KB, committed=17144KB)
                            (mmap: reserved=17144KB, committed=17144KB) 

-               Arena Chunk (reserved=1850KB, committed=1850KB)
                            (malloc=1850KB) 

-                   Logging (reserved=4KB, committed=4KB)
                            (malloc=4KB #179) 

-                 Arguments (reserved=19KB, committed=19KB)
                            (malloc=19KB #512) 

-                    Module (reserved=258KB, committed=258KB)
                            (malloc=258KB #2356) 

$ cat /proc/71/smaps | grep Rss | cut -d: -f2 | tr -d " " | cut -f1 -dk | sort -n | awk '{ sum += $1 } END { print sum }'
491080

Приложение представляет собой веб-сервер, использующий Jetty / Jersey / CDI, встроенный в большой далекий 36 МБ.

Используются следующие версии ОС и Java (внутри контейнера). Образ Docker основан на openjdk:11-jre-slim .

$ java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment (build 11+28-Debian-1)
OpenJDK 64-Bit Server VM (build 11+28-Debian-1, mixed mode, sharing)
$ uname -a
Linux service1 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 GNU/Linux

https://gist.github.com/prasanthj/48e7063cac88eb396bc9961fb3149b58


Java требует много памяти. Сама JVM нуждается в большом количестве памяти для запуска. Куча - это память, доступная внутри виртуальной машины, доступная вашему приложению. Поскольку JVM - это большой пакет, в котором есть все возможные вкусности, для загрузки требуется много памяти.

Начиная с java 9 у вас есть что-то, называемое Jigsaw проекта , которое может уменьшить объем памяти, используемой при запуске приложения java (вместе со временем запуска). Головоломка проекта и новая система модулей не обязательно создавались для уменьшения необходимой памяти, но если это важно, вы можете попробовать.

Вы можете взглянуть на этот пример: https://steveperkins.com/using-java-9-modularization-to-ship-zero-dependency-native-apps/ . При использовании модульной системы это привело к применению CLI 21 МБ (с включенным JRE). JRE занимает более 200мб. Это должно переводиться в менее выделенную память, когда приложение запущено (много неиспользуемых классов JRE больше не будут загружаться).

Вот еще один хороший учебник: https://www.baeldung.com/project-jigsaw-java-modularity

Если вы не хотите тратить время на это, вы можете просто выделить больше памяти. Иногда это лучшее.



https://developers.redhat.com/blog/2017/04/04/openjdk-and-containers/ :

Почему при указании -Xmx = 1g моя JVM использует больше памяти, чем 1 ГБ памяти?

Указание -Xmx = 1g говорит JVM выделить кучу в 1 Гб. Он не говорит JVM ограничить использование всей памяти до 1 ГБ. Существуют таблицы карточек, кеши кода и всякие другие структуры данных без кучи. Параметр, который вы используете для указания общего использования памяти: -XX: MaxRAM. Имейте в виду, что с -XX: MaxRam = 500 м ваша куча будет примерно 250 МБ.

Java видит размер памяти хоста и не знает об ограничениях памяти контейнера. Это не создает нагрузку на память, поэтому GC также не нужно освобождать использованную память. Я надеюсь, что XX:MaxRAM поможет вам уменьшить объем памяти. В конце концов, вы можете настроить конфигурацию GC ( -XX:MinHeapFreeRatio , -XX:MaxHeapFreeRatio , ...)

Существует много типов метрик памяти. Docker, похоже, сообщает об объеме памяти RSS, который может отличаться от «выделенной» памяти, сообщаемой jcmd (более ранние версии Docker сообщают об использовании кэша RSS + как использование памяти). Хорошее обсуждение и ссылки: Разница между размером резидентного набора (RSS) и общей выделенной памятью Java (NMT) для JVM, работающей в контейнере Docker

(RSS) память может быть съедена также некоторыми другими утилитами в контейнере - оболочкой, менеджером процессов, ... Мы не знаем, что еще выполняется в контейнере и как вы запускаете процессы в контейнере.







jvm