This has been a long-standing complaint with Java, but it's largely meaningless, and usually based on looking at the wrong information. The usual phrasing is something like "Hello World on Java takes 10 megabytes! Why does it need that?" Well, here's a way to make Hello World on a 64-bit JVM claim to take over 4 gigabytes ... at least by one form of measurement.

java -Xms1024m -Xmx4096m com.example.Hello

Different Ways to Measure Memory

On Linux, the top command gives you several different numbers for memory. Here's what it says about the Hello World example:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2120 kgregory  20   0 4373m  15m 7152 S    0  0.2   0:00.10 java
  • VIRT is the virtual memory space: the sum of everything in the virtual memory map (see below). It is largely meaningless, except when it isn't (see below).
  • RES is the resident set size: the number of pages that are currently resident in RAM. In almost all cases, this is the only number that you should use when saying "too big." But it's still not a very good number, especially when talking about Java.
  • SHR is the amount of resident memory that is shared with other processes. For a Java process, this is typically limited to shared libraries and memory-mapped JARfiles. In this example, I only had one Java process running, so I suspect that the 7k is a result of libraries used by the OS.
  • SWAP isn't turned on by default, and isn't shown here. It indicates the amount of virtual memory that is currently resident on disk, whether or not it's actually in the swap space. The OS is very good about keeping active pages in RAM, and the only cures for swapping are (1) buy more memory, or (2) reduce the number of processes, so it's best to ignore this number.

The situation for Windows Task Manager is a bit more complicated. Under Windows XP, there are "Memory Usage" and "Virtual Memory Size" columns, but the official documentation is silent on what they mean. Windows Vista and Windows 7 add more columns, and they're actually documented. Of these, the "Working Set" measurement is the most useful; it roughly corresponds to the sum of RES and SHR on Linux.

Understanding the Virtual Memory Map

The virtual memory consumed by a process is the total of everything that's in the process memory map. This includes data (eg, the Java heap), but also all of the shared libraries and memory-mapped files used by the program. On Linux, you can use the pmap command to see all of the things mapped into the process space (from here on out I'm only going to refer to Linux, because it's what I use; I'm sure there are equivalent tools for Windows). Here's an excerpt from the memory map of the "Hello World" program; the entire memory map is over 100 lines long, and it's not unusual to have a thousand-line list.

0000000040000000     36K r-x--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000      8K rwx--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000    676K rwx--    [ anon ]
00000006fae00000  21248K rwx--    [ anon ]
00000006fc2c0000  62720K rwx--    [ anon ]
0000000700000000 699072K rwx--    [ anon ]
000000072aab0000 2097152K rwx--    [ anon ]
00000007aaab0000 349504K rwx--    [ anon ]
00000007c0000000 1048576K rwx--    [ anon ]
...
00007fa1ed00d000   1652K r-xs-  /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000   1024K rwx--    [ anon ]
00007fa1ed2d3000      4K -----    [ anon ]
00007fa1ed2d4000   1024K rwx--    [ anon ]
00007fa1ed3d4000      4K -----    [ anon ]
...
00007fa1f20d3000    164K r-x--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000   1020K -----  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000     28K rwx--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000     16K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000      4K rwx--  /lib/x86_64-linux-gnu/libc-2.13.so
...

A quick explanation of the format: each row starts with the virtual memory address of the segment. This is followed by the segment size, permissions, and the source of the segment. This last item is either a file or "anon", which indicates a block of memory allocated via mmap.

Starting from the top, we have

  • The JVM loader (ie, the program that gets run when you type java). This is very small; all it does is load in the shared libraries where the real JVM code is stored.
  • A bunch of anon blocks holding the Java heap and internal data. This is a Sun JVM, so the heap is broken into multiple generations, each of which is its own memory block. Note that the JVM allocates virtual memory space based on the -Xmx value; this allows it to have a contiguous heap. The -Xms value is used internally to say how much of the heap is "in use" when the program starts, and to trigger garbage collection as that limit is approached.
  • A memory-mapped JARfile, in this case the file that holds the "JDK classes." When you memory-map a JAR, you can access the files within it very efficiently (versus reading it from the start each time). The Sun JVM will memory-map all JARs on the classpath; if your application code needs to access a JAR, you can also memory-map it.
  • Per-thread data for two threads. The 1M block is the thread stack. I didn't have a good explanation for the 4k block, but @ericsoe identified it as a "guard block": it does not have read/write permissions, so will cause a segment fault if accessed, and the JVM catches that and translates it to a StackOverFlowError. For a real app, you will see dozens if not hundreds of these entries repeated through the memory map.
  • One of the shared libraries that holds the actual JVM code. There are several of these.
  • The shared library for the C standard library. This is just one of many things that the JVM loads that are not strictly part of Java.

The shared libraries are particularly interesting: each shared library has at least two segments: a read-only segment containing the library code, and a read-write segment that contains global per-process data for the library (I don't know what the segment with no permissions is; I've only seen it on x64 Linux). The read-only portion of the library can be shared between all processes that use the library; for example, libc has 1.5M of virtual memory space that can be shared.

When is Virtual Memory Size Important?

The virtual memory map contains a lot of stuff. Some of it is read-only, some of it is shared, and some of it is allocated but never touched (eg, almost all of the 4Gb of heap in this example). But the operating system is smart enough to only load what it needs, so the virtual memory size is largely irrelevant.

Where virtual memory size is important is if you're running on a 32-bit operating system, where you can only allocate 2Gb (or, in some cases, 3Gb) of process address space. In that case you're dealing with a scarce resource, and might have to make tradeoffs, such as reducing your heap size in order to memory-map a large file or create lots of threads.

But, given that 64-bit machines are ubiquitous, I don't think it will be long before Virtual Memory Size is a completely irrelevant statistic.

When is Resident Set Size Important?

Resident Set size is that portion of the virtual memory space that is actually in RAM. If your RSS grows to be a significant portion of your total physical memory, it might be time to start worrying. If your RSS grows to take up all your physical memory, and your system starts swapping, it's well past time to start worrying.

But RSS is also misleading, especially on a lightly loaded machine. The operating system doesn't expend a lot of effort to reclaiming the pages used by a process. There's little benefit to be gained by doing so, and the potential for an expensive page fault if the process touches the page in the future. As a result, the RSS statistic may include lots of pages that aren't in active use.

Bottom Line

Unless you're swapping, don't get overly concerned about what the various memory statistics are telling you. With the caveat that an ever-growing RSS may indicate some sort of memory leak.

With a Java program, it's far more important to pay attention to what's happening in the heap. The total amount of space consumed is important, and there are some steps that you can take to reduce that. More important is the amount of time that you spend in garbage collection, and which parts of the heap are getting collected.

Accessing the disk (ie, a database) is expensive, and memory is cheap. If you can trade one for the other, do so.

Answer from kdgregory on Stack Overflow
Top answer
1 of 8
726

This has been a long-standing complaint with Java, but it's largely meaningless, and usually based on looking at the wrong information. The usual phrasing is something like "Hello World on Java takes 10 megabytes! Why does it need that?" Well, here's a way to make Hello World on a 64-bit JVM claim to take over 4 gigabytes ... at least by one form of measurement.

java -Xms1024m -Xmx4096m com.example.Hello

Different Ways to Measure Memory

On Linux, the top command gives you several different numbers for memory. Here's what it says about the Hello World example:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2120 kgregory  20   0 4373m  15m 7152 S    0  0.2   0:00.10 java
  • VIRT is the virtual memory space: the sum of everything in the virtual memory map (see below). It is largely meaningless, except when it isn't (see below).
  • RES is the resident set size: the number of pages that are currently resident in RAM. In almost all cases, this is the only number that you should use when saying "too big." But it's still not a very good number, especially when talking about Java.
  • SHR is the amount of resident memory that is shared with other processes. For a Java process, this is typically limited to shared libraries and memory-mapped JARfiles. In this example, I only had one Java process running, so I suspect that the 7k is a result of libraries used by the OS.
  • SWAP isn't turned on by default, and isn't shown here. It indicates the amount of virtual memory that is currently resident on disk, whether or not it's actually in the swap space. The OS is very good about keeping active pages in RAM, and the only cures for swapping are (1) buy more memory, or (2) reduce the number of processes, so it's best to ignore this number.

The situation for Windows Task Manager is a bit more complicated. Under Windows XP, there are "Memory Usage" and "Virtual Memory Size" columns, but the official documentation is silent on what they mean. Windows Vista and Windows 7 add more columns, and they're actually documented. Of these, the "Working Set" measurement is the most useful; it roughly corresponds to the sum of RES and SHR on Linux.

Understanding the Virtual Memory Map

The virtual memory consumed by a process is the total of everything that's in the process memory map. This includes data (eg, the Java heap), but also all of the shared libraries and memory-mapped files used by the program. On Linux, you can use the pmap command to see all of the things mapped into the process space (from here on out I'm only going to refer to Linux, because it's what I use; I'm sure there are equivalent tools for Windows). Here's an excerpt from the memory map of the "Hello World" program; the entire memory map is over 100 lines long, and it's not unusual to have a thousand-line list.

0000000040000000     36K r-x--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000      8K rwx--  /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000    676K rwx--    [ anon ]
00000006fae00000  21248K rwx--    [ anon ]
00000006fc2c0000  62720K rwx--    [ anon ]
0000000700000000 699072K rwx--    [ anon ]
000000072aab0000 2097152K rwx--    [ anon ]
00000007aaab0000 349504K rwx--    [ anon ]
00000007c0000000 1048576K rwx--    [ anon ]
...
00007fa1ed00d000   1652K r-xs-  /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000   1024K rwx--    [ anon ]
00007fa1ed2d3000      4K -----    [ anon ]
00007fa1ed2d4000   1024K rwx--    [ anon ]
00007fa1ed3d4000      4K -----    [ anon ]
...
00007fa1f20d3000    164K r-x--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000   1020K -----  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000     28K rwx--  /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000   1576K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000     16K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000      4K rwx--  /lib/x86_64-linux-gnu/libc-2.13.so
...

A quick explanation of the format: each row starts with the virtual memory address of the segment. This is followed by the segment size, permissions, and the source of the segment. This last item is either a file or "anon", which indicates a block of memory allocated via mmap.

Starting from the top, we have

  • The JVM loader (ie, the program that gets run when you type java). This is very small; all it does is load in the shared libraries where the real JVM code is stored.
  • A bunch of anon blocks holding the Java heap and internal data. This is a Sun JVM, so the heap is broken into multiple generations, each of which is its own memory block. Note that the JVM allocates virtual memory space based on the -Xmx value; this allows it to have a contiguous heap. The -Xms value is used internally to say how much of the heap is "in use" when the program starts, and to trigger garbage collection as that limit is approached.
  • A memory-mapped JARfile, in this case the file that holds the "JDK classes." When you memory-map a JAR, you can access the files within it very efficiently (versus reading it from the start each time). The Sun JVM will memory-map all JARs on the classpath; if your application code needs to access a JAR, you can also memory-map it.
  • Per-thread data for two threads. The 1M block is the thread stack. I didn't have a good explanation for the 4k block, but @ericsoe identified it as a "guard block": it does not have read/write permissions, so will cause a segment fault if accessed, and the JVM catches that and translates it to a StackOverFlowError. For a real app, you will see dozens if not hundreds of these entries repeated through the memory map.
  • One of the shared libraries that holds the actual JVM code. There are several of these.
  • The shared library for the C standard library. This is just one of many things that the JVM loads that are not strictly part of Java.

The shared libraries are particularly interesting: each shared library has at least two segments: a read-only segment containing the library code, and a read-write segment that contains global per-process data for the library (I don't know what the segment with no permissions is; I've only seen it on x64 Linux). The read-only portion of the library can be shared between all processes that use the library; for example, libc has 1.5M of virtual memory space that can be shared.

When is Virtual Memory Size Important?

The virtual memory map contains a lot of stuff. Some of it is read-only, some of it is shared, and some of it is allocated but never touched (eg, almost all of the 4Gb of heap in this example). But the operating system is smart enough to only load what it needs, so the virtual memory size is largely irrelevant.

Where virtual memory size is important is if you're running on a 32-bit operating system, where you can only allocate 2Gb (or, in some cases, 3Gb) of process address space. In that case you're dealing with a scarce resource, and might have to make tradeoffs, such as reducing your heap size in order to memory-map a large file or create lots of threads.

But, given that 64-bit machines are ubiquitous, I don't think it will be long before Virtual Memory Size is a completely irrelevant statistic.

When is Resident Set Size Important?

Resident Set size is that portion of the virtual memory space that is actually in RAM. If your RSS grows to be a significant portion of your total physical memory, it might be time to start worrying. If your RSS grows to take up all your physical memory, and your system starts swapping, it's well past time to start worrying.

But RSS is also misleading, especially on a lightly loaded machine. The operating system doesn't expend a lot of effort to reclaiming the pages used by a process. There's little benefit to be gained by doing so, and the potential for an expensive page fault if the process touches the page in the future. As a result, the RSS statistic may include lots of pages that aren't in active use.

Bottom Line

Unless you're swapping, don't get overly concerned about what the various memory statistics are telling you. With the caveat that an ever-growing RSS may indicate some sort of memory leak.

With a Java program, it's far more important to pay attention to what's happening in the heap. The total amount of space consumed is important, and there are some steps that you can take to reduce that. More important is the amount of time that you spend in garbage collection, and which parts of the heap are getting collected.

Accessing the disk (ie, a database) is expensive, and memory is cheap. If you can trade one for the other, do so.

2 of 8
52

There is a known problem with Java and glibc >= 2.10 (includes Ubuntu >= 10.04, RHEL >= 6).

The cure is to set this env. variable:

export MALLOC_ARENA_MAX=4

If you are running Tomcat, you can add this to TOMCAT_HOME/bin/setenv.sh file.

For Docker, add this to Dockerfile

ENV MALLOC_ARENA_MAX=4

There is an IBM article about setting MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en

This blog post says

resident memory has been known to creep in a manner similar to a memory leak or memory fragmentation.

There is also an open JDK bug JDK-8193521 "glibc wastes memory with default configuration"

search for MALLOC_ARENA_MAX on Google or SO for more references.

You might want to tune also other malloc options to optimize for low fragmentation of allocated memory:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
🌐
GeeksforGeeks
geeksforgeeks.org › java › java-memory-management
Java Memory Management - GeeksforGeeks
Java Memory Management is the process by which the Java Virtual Machine (JVM) allocates and manages memory during program execution.
Published   May 26, 2026
🌐
BETSOL
betsol.com › homepage › java memory management for java virtual machine (jvm)
Java Memory Management for Java Virtual Machine (JVM) | Betsol
March 23, 2026 - The Java Virtual Machine has memory other than the heap, referred to as Non-Heap Memory. It is created at the JVM startup and stores per-class structures such as runtime constant pool, field and method data, and the code for methods and ...
🌐
Micro Focus
microfocus.com › documentation › silk-performer › 205 › en › silkperformer-205-webhelp-en › GUID-D7FDC801-C572-444B-B4E9-B9DB4E24EFFB.html
Virtual Memory Size of a JVM - Micro Focus
The Java runtime allows you to change the default values by defining two special runtime parameters: -Xmsn for defining the minimum heap size (for example: -Xms10m) -Xmxn for defining the maximum heap size (for example: -Xmx128m) If you run into a memory problem during recording, please define the runtime parameter -Xmx with a high enough memory value in the runtime settings of your Oracle Forms applet viewer.
🌐
Elinext
elinext.com › home › navigating the java virtual machine memory model: unraveling memory management in java
How to Navigate the Java Virtual Machine Memory Model Memory
October 17, 2023 - The Java Virtual Machine (JVM) memory is divided into several distinct regions or memory areas, each serving a specific purpose. These memory areas collectively manage the allocation, usage, and deallocation of memory for running Java applications.
🌐
Medium
medium.com › @tabassum_k › memory-allocation-in-java-heap-and-stack-8197aec4accb
Memory Allocation in Java — Heap and Stack | by Tabassum Khan | Medium
February 9, 2023 - This is handled by Java Virtual Machine (JVM). ... Virtual Memory is a computer memory management mechanism whereby the secondary memory is treated as if it was part of the main memory.
Top answer
1 of 3
12

This article gives a good analysis of the problem: Why does my Java process consume more memory than Xmx And its author offers this approximate formula:

Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]

But besides the memory consumed by your application, the JVM itself also needs some elbow room. - Garbage collection. - JIT optimization. - Off-heap allocations. - JNI code. - Metaspace.

But be carefull as it may depend on both the platform and the JVM vendor/version.

2 of 3
5

This could be due to the change in malloc behavior in glibc 2.10+, where malloc now creates per-thread memory pools (arenas). The arena size on 64-bit is 64MB. After using 8 arenas on 64-bit, malloc sets the number of arenas to be number_of_cpus * 8. So if you are using a machine with many processor cores, the virtual size is set to a large amount very quickly, even though the actual memory used (resident size) is much smaller.

Since you are seeing top show 12GB virtual size, you are probably using a 64-bit machine with 24 cores or HW threads, giving 24 * 8 * 64MB = 12GB. The amount of virtual memory allocated varies with number of cores, and the amount will change depending on the number of cores on the machine your job gets sent to run on, so this check is not meaningful.

If you are using hadoop or yarn and get the warning, set yarn.nodemanager.vmem-check-enabled in yarn-site.xml to false.

References:

See #6 on this page:

http://blog.cloudera.com/blog/2014/04/apache-hadoop-yarn-avoiding-6-time-consuming-gotchas/

which links to more in-depth discussion on this page:

https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage

Note this is already partially answered on this stackoverflow page:

Container is running beyond memory limits

🌐
Delft Stack
delftstack.com › home › howto › java › java vm memory
How to Increase Virtual Memory in Java | Delft Stack
March 11, 2025 - Virtual memory is a memory management capability that allows a computer to compensate for physical memory shortages by temporarily transferring data from random access memory (RAM) to disk storage.
Find elsewhere
🌐
NetIQ
netiq.com › documentation › operations-center-57 › server_configuration › data › bl24sl1.html
Configuring the Java Virtual Machine - Operations Center Server Configuration Guide
The size of memory allocated is usually 8MB, unless it is told to allocate more. The Java virtual machine has an inherent runtime overhead, which can amount to quite a bit of memory. There is no direct way to control the size of this overhead.
🌐
Trevorsimonton
trevorsimonton.com › blog › 2020 › 09 › 09 › java-native-memory.html
understanding java native memory - Trevor Simonton
September 9, 2020 - The amount of virtual memory that the Java process uses is only limited by the ability for memory addresses to be referenced by CPU instructions. The virtual space on 32-bit operating systems is generally limited between 2GB and 3GB (2^32 is 4GB on 32-bit systems, but not all of those 4GB are ...
🌐
Oracle
docs.oracle.com › javase › specs › jvms › se8 › html › jvms-2.html
Chapter 2. The Structure of the Java Virtual Machine
March 16, 2026 - The Java Virtual Machine assumes no particular type of automatic storage management system, and the storage management technique may be chosen according to the implementor's system requirements. The heap may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger heap becomes unnecessary. The memory for the heap does not need to be contiguous.
🌐
Trustgrid
docs.trustgrid.io › help-center › kb › default-settings › jvm
Java Virtual Machine (JVM) |
The Java Virtual Machine (JVM) is responsible for managing the memory used by the Trustgrid service on appliances.
🌐
Medium
medium.com › @amitvsolutions › jvm-part-3-memory-management-89acca050442
JVM Part-3: Memory Management. JVM Memory Management | by Amit Verma | Medium
April 22, 2024 - There exists only one heap memory for each running JVM process. Therefore, this is a shared part of memory regardless of how many threads are running. The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads.
🌐
GitHub
github.com › rkhmelyuk › memory
GitHub - rkhmelyuk/memory: virtual memory on java
Memory is a library that implementations a basic virtual memory on Java.
Author   rkhmelyuk
🌐
IBM
ibm.com › docs › en › om-jvm › 5.5.0
JVM Virtual Memory attributes
Get assistance for the IBM products, services and software you own · Provides fixes and updates for your system's software, hardware, and operating system
🌐
DigitalOcean
digitalocean.com › community › tutorials › java-jvm-memory-model-memory-management-in-java
Java Memory Management Explained | DigitalOcean
August 7, 2025 - Understanding these memory areas ... class loading, or memory-intensive workloads. In the Java Virtual Machine (JVM), heap memory is the primary area for dynamic memory allocation....
🌐
Baeldung
baeldung.com › home › java › jvm › can a java application use more memory than the heap size?
Can a Java Application Use More Memory Than the Heap Size? | Baeldung
January 8, 2024 - We can try other VM options of jcmd to have an overview of some specific areas of native memory, like Metaspace, symbols, and interned strings. ... <pid>: Total Usage - 1072 loaders, 9474 classes (1176 shared): ... Virtual space: Non-class space: 38.00 MB reserved, 36.67 MB ( 97%) committed Class space: 1.00 GB reserved, 5.62 MB ( <1%) committed Both: 1.04 GB reserved, 42.30 MB ( 4%) committed Chunk freelists: Non-Class: ...
🌐
Vogella
vogella.com › tutorials › JavaPerformance › article.html
Java Performance - Memory and Runtime Analysis - Tutorial
May 31, 2026 - This allows the OS to access 64GB. The OS uses then virtual memory to allow the individual process 4 GB of memory. Even with PAE enabled a process cannot access more than 4 GB of memory. Of course with a 64-bit OS this 4GB limitation does not exist anymore. Java manages the memory for use.
🌐
Red Hat
developers.redhat.com › articles › 2021 › 09 › 09 › how-jvm-uses-and-allocates-memory
How the JVM uses and allocates memory | Red Hat Developer
October 20, 2023 - The previous article introduced the stages and levels of garbage collection (including generational garbage collection) and showed how to check garbage collection behavior in your applications. This article goes into more depth about memory use in the Java Virtual Machine (JVM) and how to control ...
Top answer
1 of 6
11

I suspect that it is a leak too. But it can't be a leak of 'normal' memory because the -Xmx1024m option is capping the normal heap. Likewise, it won't be a leak of 'permgen' heap, because the default maximum size of permgen is small.

So I suspect it is one of the following:

  • You are leaking threads; i.e. threads are being created but are not terminating. They might not be active, but each thread has a stack segment (256k to 1Mb by default ... depending on the platform) that is not allocated in the regular heap.

  • You are leaking direct-mapped files. These are mapped to memory segments allocated by the OS outside of the regular heap. (@bestsss suggests that you look for leaked ZIP file handles, which I think would be a sub-case of this.)

  • You are using some JNI / JNA code that is leaking malloc'ed memory, or similar.

Either way, a memory profiler is likely to isolate the problem, or at least eliminate some of the possibilities.


A JVM memory leak is also a possibility, but it is unwise to start suspecting the JVM until you have definitively eliminated possible causes in your own code and libraries / applications that you are using.

2 of 6
3

Since your application is not a real-time application, you can do the following:

jps -v

Get the process id from that table, and save it as pid.

jmap -histo $pid > before-gc.hgr
jmap -histo:live $pid > after-gc.hgr
jstack -v $pid > threads.txt

The threads.txt file tells you what the process is doing at the moment.

The heap usage histograms before-gc.hgr and after-gc.hgr tell you how much memory a full garbage collection could free.

Maybe you get some hints from this as to what is happening.