Native Memory May Cause Unknown Memory Leaks

Recently I came across a strange case: the memory usage of my program exceeded the maximum value intended for the heap. Even after running GC, part of the memory was not free. I already knew that a part of  JVM memory would be allocated to native memory and part of native memory allocated to C code, but I did not have even one line of native code in my program. After reviewing and profiling my code several times, I found an interesting issue. Before diving into the problem, let's look at Java memory concepts.

Memory Management in Java

 JVM divides memory into two major spaces, heap and native memory. Heap spaces are used for allocating Java objects whereas native memory is the memory available to the OS. There is a key difference between Java 7 and 8 in the memory management model. Java 7 has PermGen; PermGen is the memory area in the heap that is used by the JVM to store class metadata, static content, primitive variables. Java 8 has eliminated PermGen and added Metaspace; actually, Metaspace and PermGen do the same thing. The main difference is that PermGen is part of the Java heap while Metaspace is NOT part of the heap. Rather Metaspace is part of native memory, which is only limited by the Host Operating System.

Native Memory Allocation in Java

From time to time, we run into a memory problem that is not related to Java Heap but Native Memory. Let's imagine the situation where we have a running container that is restarted once per day. We look at Prometheus/Grafana to check the reason behind this issue. However, we don't spot any problem with Java Heap size (exported via JMX) and start blaming our container scheduler to do any inconvenient operations with our containers :). The real problem can be hidden a bit deeper — in Native Memory.

Do you use any native memory allocator in your code or do you use any library that stores its internal state outside the Java Heap? It could be whatever third-party code that tries to minimize GC, such as I/O libraries or databases/caches keeping data in memory inside your process.