LINUX.ORG.RU

Java в докере жрёт много памяти

 ,


0

2

По крайней мере по словам докера. docker stats выдаёт, что контейнер жрёт 322 MiB. При этом Java запущена с -Xmx128M, а средства Java говорят, что и из этого используется лишь половина. Т.е. реально Java потребляет 60 MiB. Откуда всё остальное?

Java 17. Флажки, относящиеся к памяти: -XX:G1PeriodicGCInterval=900000 -Xms16m -Xmx128m.

★★★★★

Ответ на: комментарий от hummer
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                      
    1 root      20   0 5992400 330448  20776 S   2.0   2.0  87:53.46 java
Legioner ★★★★★ ()

JVM наверное не покажет если в приложении используется прямая аллокция вне хип jvm, через какой-нибудь DirectByteBuffers. Что-то вроде netty наверняка может использовать.

Быстро глянул тут, пишут:

By default, it’s equal to  -Xmx. Yes, the JVM heap and off-heap memory are two different memory areas, but by default, they have the same maximum size.

Статью я не читал, но по фразе выходит что Xmx влияет не только на аллокацию кучи но и на максимальный размер DirectByteBuffers, поэтому может получится захвачено 2х размера указанного в Xmx. Проверь, поставь -Xmx=96m, если это так контейнер займет 200 метров памяти.

anonymous ()

У ява-машины ведь не только куча есть, с чего ты взял что она будет потреблять только Xmx

slowpony ★★★ ()
Ответ на: комментарий от slowpony

«Все остальное» должно занимать немного, не куча это stack и permgen для загружаемых классов.

anonymous ()
Ответ на: комментарий от Legioner

Ключи -Xm* указывают java heap size (то есть то что аллокейтится жабовским кодом через new например), а кроме кучи есть еще всякие кэши классов, JIT и прочие внутренние механизмы.

Nastishka ★★★★★ ()
Последнее исправление: Nastishka (всего исправлений: 1)
Ответ на: комментарий от Nastishka

Это я в курсе, но по моему опыту это не должно занимать так много. Там приложение не так много библиотек тянет и само по себе несложное. Пока подозрение на Netty, как выше писали, но руки не дошли поковыряться подробней. Хотя зачем Netty 100 мегабайтов буферов, не представляю.

Legioner ★★★★★ ()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от Nastishka

В Java грузятся только используемые классы. Из этих десятков мегабайтов в память загрузятся далеко не все.

Legioner ★★★★★ ()
Ответ на: комментарий от Legioner

Чтобы не быть голословным, сделал простой юз-кейс. Приложение:

package test.java17;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;

import static java.lang.Runtime.getRuntime;
import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {
    public static void main(String[] args) throws IOException {
        var httpServer = HttpServer.create();
        httpServer.createContext("/").setHandler(exchange -> {
            String memoryInfo = "max=" + getRuntime().maxMemory() + " " +
                                "free=" + getRuntime().freeMemory() + " " +
                                "total=" + getRuntime().totalMemory();
            exchange.getResponseHeaders().add("content-type", "text/plain");
            exchange.sendResponseHeaders(200, 0);
            exchange.getResponseBody().write(memoryInfo.getBytes(UTF_8));
            exchange.close();
        });
        httpServer.bind(new InetSocketAddress("0.0.0.0", 8080), 0);
        httpServer.start();
    }
}

Dockerfile:

FROM openjdk:17-jdk-oraclelinux8
WORKDIR /opt/java17-idea-test
COPY out/artifacts/java17_idea_test_jar/java17-idea-test.jar java17-idea-test.jar
CMD ["java", \
     "-XX:G1PeriodicGCInterval=5000", "-Xms16m", "-Xmx128m", \
     "-jar", "java17-idea-test.jar"]
EXPOSE 8080

Запускаем: docker run --rm --name java17-idea-test --publish 127.0.0.1:8080:8080 java17-idea-test

Делаем пару запросов. В хипе 2,1 МБ, в докере показывает 21 МиБ. Это примерно то, что я ожидаю, накладные расходы 20 МиБ.

Запускаем while true; do curl http://127.0.0.1:8080 -o /dev/null -sfS; done, насилуем минутку, мониторим docker stats. Нагрузка растёт до 50 МиБ и остаётся на этом уровне. При этом по хипу особо потребления нет, то бишь 30 МиБ сожрали какие-то накладные native расходы. Ну ладно. Вот примерно на этом уровне я рассчитывал получить расход. 60 МБ хип, пусть 50 МБ накладных расходов и 20 МБ на библиотечные классы, т.е. 130 МБ, а не 322 МБ.

Честно говоря куда утекли 30 МБ в этом юз-кейсе и почему жава не отдала их при отключении нагрузки, мне тоже любопытно. Но это ладно, 30 не 200.

Legioner ★★★★★ ()

Java сама по себе очень жиробастая - писать конечно классно, но продукт написанный на Java очень требовательный к ресурсам. Взять к примеру тот же Minecraft. На старте жрет примерно 1,5 гига по-крайней мере у меня так.

Dumppper001 ()

Запустил с -XX:NativeMemoryTracking=summary. Запустил jcmd pid VM.native_memory. Выплюнуло:

1:

Native Memory Tracking:

(Omitting categories weighting less than 1KB)

Total: reserved=1634308KB, committed=272144KB
-                 Java Heap (reserved=131072KB, committed=83968KB)
                            (mmap: reserved=131072KB, committed=83968KB) 
 
-                     Class (reserved=1049559KB, committed=4951KB)
                            (classes #7398)
                            (  instance classes #6932, array classes #466)
                            (malloc=983KB #24034) 
                            (mmap: reserved=1048576KB, committed=3968KB) 
                            (  Metadata:   )
                            (    reserved=32768KB, committed=32192KB)
                            (    used=31997KB)
                            (    waste=195KB =0.61%)
                            (  Class space:)
                            (    reserved=1048576KB, committed=3968KB)
                            (    used=3853KB)
                            (    waste=115KB =2.89%)
 
-                    Thread (reserved=64850KB, committed=5002KB)
                            (thread #64)
                            (stack: reserved=64680KB, committed=4832KB)
                            (malloc=98KB #382) 
                            (arena=72KB #124)
 
-                      Code (reserved=250529KB, committed=42385KB)
                            (malloc=2845KB #12320) 
                            (mmap: reserved=247684KB, committed=39540KB) 
 
-                        GC (reserved=65467KB, committed=63731KB)
                            (malloc=27783KB #12701) 
                            (mmap: reserved=37684KB, committed=35948KB) 
 
-                  Compiler (reserved=470KB, committed=470KB)
                            (malloc=306KB #809) 
                            (arena=165KB #5)
 
-                  Internal (reserved=551KB, committed=551KB)
                            (malloc=515KB #14633) 
                            (mmap: reserved=36KB, committed=36KB) 
 
-                     Other (reserved=16684KB, committed=16684KB)
                            (malloc=16684KB #78) 
 
-                    Symbol (reserved=6176KB, committed=6176KB)
                            (malloc=4825KB #140764) 
                            (arena=1351KB #1)
 
-    Native Memory Tracking (reserved=3275KB, committed=3275KB)
                            (malloc=13KB #190) 
                            (tracking overhead=3262KB)
 
-        Shared class space (reserved=12288KB, committed=12140KB)
                            (mmap: reserved=12288KB, committed=12140KB) 
 
-               Arena Chunk (reserved=188KB, committed=188KB)
                            (malloc=188KB) 
 
-                   Logging (reserved=5KB, committed=5KB)
                            (malloc=5KB #219) 
 
-                 Arguments (reserved=2KB, committed=2KB)
                            (malloc=2KB #62) 
 
-                    Module (reserved=223KB, committed=223KB)
                            (malloc=223KB #1563) 
 
-                 Safepoint (reserved=8KB, committed=8KB)
                            (mmap: reserved=8KB, committed=8KB) 
 
-           Synchronization (reserved=58KB, committed=58KB)
                            (malloc=58KB #694) 
 
-            Serviceability (reserved=1KB, committed=1KB)
                            (malloc=1KB #14) 
 
-                 Metaspace (reserved=32903KB, committed=32327KB)
                            (malloc=135KB #106) 
                            (mmap: reserved=32768KB, committed=32192KB) 
 
-      String Deduplication (reserved=1KB, committed=1KB)
                            (malloc=1KB #8) 

Итого видно: жрёт 272 МБ. Из них куча 84 МБ, классы 5 МБ, треды 5 МБ, это то, что я понимаю. А всё остальное - нифига не понимаю. Куда дальше копать - не ясно.

Legioner ★★★★★ ()
Ответ на: комментарий от Legioner

В общем из того, что я осилил - G1 жрёт много памяти на свои накладные расходы. Заюзал -XX:+UseSerialGC. Посмотрим.

Legioner ★★★★★ ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей