Search code examples
javajvmcontainers

Java memory off heap inside container


I have been studying and reading some questions about memory usage, even with good answers here I would like to try understading some values that I have collected in my analysis.

I wrote this below code just to show where I collected the data. Note: This codes does not compile, it is just to understand.

       long KB_FACTOR = 1024;
        long MB_FACTOR = 1024 * KB_FACTOR;

        OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        "operationSystem.totalPhysicalMemorySizeMB" = os.getTotalPhysicalMemorySize() / MB_FACTOR);
        "operationSystem.freePhysicalMemorySizeMB"  = os.getFreePhysicalMemorySize() / MB_FACTOR);

        long freeMemoryBytes = Runtime.getRuntime().freeMemory();
        long totalMemoryBytes = Runtime.getRuntime().totalMemory();
        long maxMemoryBytes = Runtime.getRuntime().maxMemory();
        "memory.maxMB" = (maxMemoryBytes / MB_FACTOR);
        "memory.freeMB" = (freeMemoryBytes / MB_FACTOR);
        "memory.totalMB" = (totalMemoryBytes / MB_FACTOR);

        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
        "memoryHeap.commitedMB" = (heapMemoryUsage.getCommitted() / MB_FACTOR);
        "memoryHeap.maxMB" = (heapMemoryUsage.getMax() / MB_FACTOR);

        MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
       "memoryNonHeap.commitedMB" =  (nonHeapMemoryUsage.getCommitted() / MB_FACTOR);
       "memoryNonHeap.maxMB" = (nonHeapMemoryUsage.getMax() / MB_FACTOR);

       "memoryHeap+NonHeap.committedMB" = (heapMemoryUsage.getCommitted() / MB_FACTOR) + (nonHeapMemoryUsage.getCommitted() / MB_FACTOR);

This is some data I have collected: Container 01

 {
        "memory": {
            "freeMB": 611,
            "maxMB": 1622,
            "totalMB": 1336
        },
        "memoryHeap": {
            "committedMB": 1336,
            "maxMB": 1622
        },
        "memoryNonHeap": {
            "initMB": 7,
            "usedMB": 219,
            "committedMB": 227,
            "maxMB": 0
        },
        "memoryHeap+NonHeap": {
            "committedMB": 1563
        },
        "operationSystem": {
            "totalPhysicalMemorySizeMB": 1907,
            "freePhysicalMemorySizeMB": 390
        }
    }

Container 02:

{
    "memory": {
        "freeMB": 303,
        "maxMB": 1622,
        "totalMB": 1336
    },
    "memoryHeap": {
        "committedMB": 1336,
        "maxMB": 1622
    },
    "memoryNonHeap": {
        "committedMB": 244,
        "maxMB": 0
    },
    "memoryHeap+NonHeap": {
        "committedMB": 1580
    },
    "operationSystem": {
        "totalPhysicalMemorySizeMB": 1907,
        "freePhysicalMemorySizeMB": 87
    }
}

I am running with:

  • alpine openjdk-11
  • Container memory limit: 2GB
  • AWS EKS.
  • JVM Env: -Djava.net.preferIPv4Stack=true -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=85.0 -Dfile.encoding=UTF8

I have noticed that "operationSystem.totalPhysicalMemorySizeMB" is the amount of memory JVM understand as available to use.

The parameter "-XX:MaxRAMPercentage=85.0" work as expected. (using 1,907 as reference)

I know that nonHeap memory is off-heap, then should I use nonHeap+heap to calculate the total usage by my application.

Question: My main doubt is about "freePhysicalMemorySizeMB", this value variates a lot between containers even when the committed memories (heap+nonHeap) have the same value .

I read that java can use Native Library (JNI) and this also consume memory.

  • Should I reserve more memory beyond the estimate of heap+NonHeap? If yes, is it possible to know how much? Will Garbage Collector clear these others used memory?

  • Is it the term "NonHeap" the same as "off-heap"?

Note: My pod is just running java application (and alpine itself)


Solution

  • should I use nonHeap+heap to calculate the total usage by my application.

    No. Total physical memory used by a Java process includes many things not counted by the JVM.
    See this answer and this presentation for details.

    My main doubt is about "freePhysicalMemorySizeMB", this value variates a lot between containers even when the committed memories (heap+nonHeap) have the same value .

    Just don't look at getFreePhysicalMemorySize(). This value is rarely useful. It does not take into account page cache and various reclaimable OS structures that can be automatically freed when the application requests more memory.

    The most meaningful measure of the process' memory usage is RSS - the resident size. An easy way to find the application's own RSS on Linux is to read /proc/self/stat.

    Should I reserve more memory beyond the estimate of heap+NonHeap? If yes, is it possible to know how much?

    An application may require more memory than Heap+NonHeap. It's not easy to guess the right number statically, so your best bet is to watch the actual RSS and adjust the limits correspondingly.

    Is it the term "NonHeap" the same as "off-heap"?

    Depends on the context. For example, in jconsole and some other tools, Non-Heap denotes certain JVM structures: Metaspace, Compress Class Space, and Code Cache. Off-heap is usually a broader term, which basically means everything but Java Heap.