Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Sun JVM continue to consume ever more RSS memory even when the heap, etc sizes are stable?

Over the past year I've made huge improvements in my application's Java heap usage--a solid 66% reduction. In pursuit of that, I've been monitoring various metrics, such as Java heap size, cpu, Java non-heap, etc. via SNMP.

Recently, I've been monitoring how much real memory (RSS, resident set) by the JVM and am somewhat surprised. The real memory consumed by the JVM seems totally independent of my applications heap size, non-heap, eden space, thread count, etc.

Heap Size as measured by Java SNMP Java Heap Used Graph http://lanai.dietpizza.ch/images/jvm-heap-used.png

Real Memory in KB. (E.g.: 1 MB of KB = 1 GB) Java Heap Used Graph http://lanai.dietpizza.ch/images/jvm-rss.png

(The three dips in the heap graph correspond to application updates/restarts.)

This is a problem for me because all that extra memory the JVM is consuming is 'stealing' memory that could be used by the OS for file caching. In fact, once the RSS value reaches ~2.5-3GB, I start to see slower response times and higher CPU utilization from my application, mostly do to IO wait. As some point paging to the swap partition kicks in. This is all very undesirable.

So, my questions:

  • Why is this happening? What is going on "under the hood"?
  • What can I do to keep the JVM's real memory consumption in check?

The gory details:

  • RHEL4 64-bit (Linux - 2.6.9-78.0.5.ELsmp #1 SMP Wed Sep 24 ... 2008 x86_64 ... GNU/Linux)
  • Java 6 (build 1.6.0_07-b06)
  • Tomcat 6
  • Application (on-demand HTTP video streaming)
    • High I/O via java.nio FileChannels
    • Hundreds to low thousands of threads
    • Low database use
    • Spring, Hibernate

Relevant JVM parameters:

-Xms128m   -Xmx640m   -XX:+UseConcMarkSweepGC   -XX:+AlwaysActAsServerClassMachine   -XX:+CMSIncrementalMode      -XX:+PrintGCDetails  -XX:+PrintGCTimeStamps   -XX:+PrintGCApplicationStoppedTime   -XX:+CMSLoopWarn   -XX:+HeapDumpOnOutOfMemoryError  

How I measure RSS:

ps x -o command,rss | grep java | grep latest | cut -b 17- 

This goes into a text file and is read into an RRD database my the monitoring system on regular intervals. Note that ps outputs Kilo Bytes.


The Problem & Solutions:

While in the end it was ATorras's answer that proved ultimately correct, it kdgregory who guided me to the correct diagnostics path with the use of pmap. (Go vote up both their answers!) Here is what was happening:

Things I know for sure:

  1. My application records and displays data with JRobin 1.4, something I coded into my app over three years ago.
  2. The busiest instance of the application currently creates
    1. Over 1000 a few new JRobin database files (at about 1.3MB each) within an hour of starting up
    2. ~100+ each day after start-up
  3. The app updates these JRobin data base objects once every 15s, if there is something to write.
  4. In the default configuration JRobin:
    1. uses a java.nio-based file access back-end. This back-end maps MappedByteBuffers to the files themselves.
    2. once every five minutes a JRobin daemon thread calls MappedByteBuffer.force() on every JRobin underlying database MBB
  5. pmap listed:
    1. 6500 mappings
    2. 5500 of which were 1.3MB JRobin database files, which works out to ~7.1GB

That last point was my "Eureka!" moment.

My corrective actions:

  1. Consider updating to the latest JRobinLite 1.5.2 which is apparently better
  2. Implement proper resource handling on JRobin databases. At the moment, once my application creates a database and then never dumps it after the database is no longer actively used.
  3. Experiment with moving the MappedByteBuffer.force() to database update events, and not a periodic timer. Will the problem magically go away?
  4. Immediately, change the JRobin back-end to the java.io implementation--a line line change. This will be slower, but it is possibly not an issue. Here is a graph showing the immediate impact of this change.

Java RSS memory used graph http://lanai.dietpizza.ch/images/stackoverflow-rss-problem-fixed.png

Questions that I may or may not have time to figure out:

  • What is going on inside the JVM with MappedByteBuffer.force()? If nothing has changed, does it still write the entire file? Part of the file? Does it load it first?
  • Is there a certain amount of the MBB always in RSS at all times? (RSS was roughly half the total allocated MBB sizes. Coincidence? I suspect not.)
  • If I move the MappedByteBuffer.force() to database update events, and not a periodic timer, will the problem magically go away?
  • Why was the RSS slope so regular? It does not correlate to any of the application load metrics.
like image 573
Stu Thompson Avatar asked Oct 23 '09 11:10

Stu Thompson


People also ask

Why JVM heap utilization is too high?

This is because the JVM steadily increases heap usage percentage until the garbage collection process frees up memory again. High heap usage occurs when the garbage collection process cannot keep up. An indicator of high heap usage is when the garbage collection is incapable of reducing the heap usage to around 30%.

Why JVM use more memory than XMX?

What you have specified via the -Xmx switches is limiting the memory consumed by your application heap. But besides the memory consumed by your application, the JVM itself also needs some elbow room. The need for it derives from several different reasons: Garbage collection.

Why does Java use more memory?

Java is also a very high-level Object-Oriented programming language (OOP) which means that while the application code itself is much easier to maintain, the objects that are instantiated will use that much more memory.

What happens when heap memory is full in Java?

Java objects reside in an area called the heap. The heap is created when the JVM starts up and may increase or decrease in size while the application runs. When the heap becomes full, garbage is collected. During the garbage collection objects that are no longer used are cleared, thus making space for new objects.


1 Answers

Just an idea: NIO buffers are placed outside the JVM.

EDIT: As per 2016 it's worth considering @Lari Hotari comment [ Why does the Sun JVM continue to consume ever more RSS memory even when the heap, etc sizes are stable? ] because back to 2009, RHEL4 had glibc < 2.10 (~2.3)

Regards.

like image 168
ATorras Avatar answered Sep 28 '22 02:09

ATorras