Память Java превышает 512 МБ, несмотря на установку -Xmx300m -Xss512k -XX:MetaspaceSize=100M?

Я развернул Java-приложение в Heroku, используя следующие параметры vm:

-Xmx300m -Xss512k -XX:MetaspaceSize=100M

Несмотря на это, мое приложение постоянно получает ошибки Error R14 (превышение квоты памяти) от Heroku. Это связано с тем, что занимаемый объем памяти достигает 540 МБ (и квота составляет 512 МБ).

Как приложение Java может занимать более 500 МБ, когда я выделяю ему только 400 МБ в аргументах виртуальной машины? Я пропустил аргумент виртуальной машины?

Я ожидаю, что JVM перейдет в режим OOM, прежде чем достигнет 512 МБ памяти (но она просто увеличивается за пределы установленных мною ограничений).

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

Любая помощь в этом будет очень признательна.

Заметки:

  • я использую джаву 8
  • мой procfile выглядит следующим образом:

    web: java -Xmx300m -Xss512k -XX:MetaspaceSize=100M -javaagent:./lib/heroku-javaagent-2.0.jar=stdout=true,lxmem=true -Dserver.port=$PORT -Dspring.profiles.active=heroku -cp ./app.jar          org.springframework.boot.loader.JarLauncher
    

Приведенный выше Java-агент выдает в логах следующие данные:

Dec 13 11:45:54 app/web.1:  measure.mem.jvm.heap.used=177M measure.mem.jvm.heap.committed=212M measure.mem.jvm.heap.max=273M 
Dec 13 11:45:54 app/web.1:  measure.mem.jvm.nonheap.used=129M measure.mem.jvm.nonheap.committed=132M measure.mem.jvm.nonheap.max=0M 
Dec 13 11:45:54 app/web.1:  measure.mem.jvm.direct.used=0M measure.mem.jvm.direct.count=0 measure.mem.jvm.direct.capacity=74M 
Dec 13 11:45:54 app/web.1:  measure.mem.jvm.mapped.used=0M measure.mem.jvm.mapped.count=0 measure.mem.jvm.mapped.capacity=0M 
Dec 13 11:45:54 app/web.1:  measure.mem.linux.vsz=5817M measure.mem.linux.rss=543M 

person Tom G    schedule 13.12.2017    source источник
comment
В самом приложении могут быть утечки памяти, вы можете развернуть приложение локально и посмотреть поведение распределения памяти в VisualVM или Jconsole.   -  person kakurala    schedule 13.12.2017
comment
привет @kakurala . Если бы произошла утечка памяти, разве OOM JVM не увеличился бы за его пределы?   -  person Tom G    schedule 13.12.2017
comment
Да, я предполагаю, что эта ошибка появляется, когда вы запускаете приложение, а не через какое-то время. Если это так, то при запуске ваше приложение занимает больше, чем выделенное пространство, и в конечном итоге выходит из строя. Чтобы подтвердить, разверните его на локальном ПК, а затем посмотрите, возникает ли такая же ошибка или нет. Это скажет вам, связана ли проблема с Heroku или с самим приложением.   -  person kakurala    schedule 13.12.2017
comment
Ключ здесь measure.mem.jvm.nonheap.used=129M, что означает, что используется 129 МБ памяти вне кучи, для которой не важны ограничения кучи. Память вне кучи может использоваться прямыми ByteBuffers, файлами с отображением памяти и т.п. Считает ли heroku, что квота включает память, которую JVM использует в дополнение к приложению, или только приложение?   -  person Kayaman    schedule 13.12.2017
comment
Помимо Java Heap и Metaspace есть много других вещей, которые могут использовать память: кэш кода, Direct ByteBuffers, стеки потоков, нативные библиотеки...   -  person apangin    schedule 13.12.2017
comment
Я думал, что кеш кода теперь хранится в Metaspace, что я ограничиваю? И я также ограничиваю размер стека потоков с помощью -Xss512k. Однако я не рассматривал ByteBuffers и нативные библиотеки. Спасибо за это. Я думаю, нет никакого способа узнать, что это будет?   -  person Tom G    schedule 13.12.2017
comment
@Kayaman - похоже, что heroku измеряет RSS (en.m.wikipedia.org/wiki/Resident_set_size) — это общая память, используемая процессом.   -  person Tom G    schedule 13.12.2017
comment
@kakurala - ошибка возникает после того, как приложение работает несколько часов. Ошибка определенно исходит от героку, а не от приложения. Ошибка означает, что мой процесс использует слишком много места (за пределами квоты 512 МБ). Извините, что не объяснил это лучше   -  person Tom G    schedule 13.12.2017
comment
Кэш кода и метапространство — совершенно разные области. -Xss — это ограничение на поток, т. е. больше потоков => больше памяти. Чтобы получить подробные отчет об использовании памяти JVM (но все еще за исключением файлов с отображением памяти и собственных библиотек).   -  person apangin    schedule 14.12.2017


Ответы (2)


Как приложение Java может занимать более 500 МБ, когда я выделяю ему только 400 МБ в аргументах виртуальной машины? Я пропустил аргумент виртуальной машины?

JVM имеет много категорий памяти, включая кучу, метапространство, стеки потоков, кеш кода и многое другое. Через JNI может быть даже выделена память, которую JVM не может учесть. Взгляните на в этом блоге. опубликовать в памяти JVM.

Я ожидаю, что JVM перейдет в режим OOM, прежде чем достигнет 512 МБ памяти (но она просто увеличивается за пределы установленных мною ограничений).

Большинство ошибок OOM происходят только тогда, когда у вас заканчивается место в куче. В вашем случае у вас достаточно места в куче, но все эти другие категории складываются и превышают ограничение в 512 МБ.

Вы можете попробовать уменьшить Xmx до -Xmx256m. Любое значение ниже, и вы, вероятно, начнете получать ошибки OOM.

Вы также можете проверить свой код на предмет утечки буферов ввода-вывода. Если они не закрыты должным образом, они могут иногда вызывать утечки собственной памяти (вне кучи). Наконец, проверьте, не использует ли какая-либо из ваших зависимостей (сторонних библиотек) JNI или не выделяет собственную память.

person codefinger    schedule 13.12.2017

-Xmx256m -XX:+UseContainerSupport

сделал это для меня. Использование настроек Heroku по умолчанию (например, -Xmx300m) превысило порог Dyno в 512 МБ.

person Beppe C    schedule 31.12.2019