JVM process Virtual Memory Usage (Resident Set Size) On A Linux 2.6.25 Kernel

Introduction

This article expands on the previous Virtual Memory post by exploring Resident Set Size growth while running a Java program that will consume all available Java Heap memory. Four different JVM implementations are used to compare results of these experiments. These JVMs come from two different vendors(IBM & Sun) and span two different versions of Java (1.5 & 1.6).

This article tries to tie Java Heap utilization, Resident Set Size, and Virtual Memory address space size statistics together.

Experiment Description

The following program was written to slowly consume all Java Heap in a running JVM.

import java.util.HashMap;

class I
{
Integer I [] = null;
public I()
{
I = new Integer[1000000];
}
}

public class Tester
{
HashMap i = null;
public Tester()
{
i = new HashMap(100000);
}
public void run()
{
for(int k = 0; k <1000000; k++) { try { i.put(new Integer(k), new I()); Thread.sleep(1000); } catch(Exception e) { e.printStackTrace(); } } } public static void main(String args []) { Tester t = new Tester(); t.run(); } } This program was used to generate all data presented in this article. The following JDKs were used in this experiment: Sun JDK 1.6.0_11
Sun JDK 1.5.0_17
IBM JDK 1.5 (SR9-0)
IBM JDK 1.6 (SR3-0)

A Linux 2.6.25 kernel (Fedora Core 9) running on a VMWare Workstation Virtual Machine (64-bit) was the experiment’s platform.

The Java Heap minimum and maximum were both set to 512MB of memory. In all scenerios, a 64-bit JDK was used. Besides these items, default JVM settings were used.

Java Heap

The Sun JVM uses a multi-generational heap that has three generations. Only two generations are used by application code (the last is used to store Java Class definitions and related data structures). The IBM JVM, by default, is a single-generation heap.

These implementation differences can be seen in the RSS graphs presented below. The Sun JVM fills its young generation, keeps longer-lived objects in Survivor Regions for a time until moving them to the Tenured (Old) Generation of memory. The initial, linear memory growth seen in the Sun graphs is the Young Generation being filled for the first time. Then, Tenured Objects are moved to the Old Generation for the first time; this event corresponds to the first spike in the RSS numbers. Then, the Young Generation is filled again and its contents copied to the Old Generation–the second spike. This pattern continues until the Java Heap is exhausted and the process dies. The details of Sun’s JVM Garbage Collection algorithms will be introduced in a future article.

In the IBM JVM, the single-generation of Java Heap continues to grow (with RSS increasing linearly) until the JVM runs out of memory and the process exits.

The Garbage Collector(s) in each JVM are able to do little to reclaim Java Heap space in these experiments because essentially every object that is allocated is kept(forever or until the JVM runs out of memory, whichever comes first).

Resident Set Size

The following graphs show physical memory (Resident Set Size) used by the java process over its lifetime. The behavior between each vendor’s implementation between 1.5 & 1.6 is relatively constant. The IBM implementation of the Java Heap shows a steady increase of RSS. The Sun implementation initially starts with a steady rise of the RSS data, but then there are a series of large jumps followed by no increase.

The RSS statistics were gathered with the following command:

while [ 1 ]; do ps -eo pid,cmd,rss | grep java | grep -v ps | grep -v grep | awk ‘{print $NF}’; date; sleep 1; done

The steady-state Virtual Memory address space size of each JVM is summarized below. Note, the “steady-state” size is defined as the VIRT statistic that top reports after the java process has been running for ten seconds. In all cases, the VIRT statistic doesn’t increase during the life of the test run (after ten seconds).

IBM 1.5 726MB
IBM 1.6 744MB
Sun 1.5 875MB
Sun 1.6 829MB

IBM Java 1.6

IBM Java 1.5

Sun Java 1.6

Sun Java 1.5

All Data

IBM Java 1.5 With “gencon” Multi-Generational Heap

If the “-Xgcpolicy:gencon” parameter is used to give the IBM JVM Multi-Generational heap, the RSS graph becomes something similar to the following. Obviously, different algorithms are at play here, but you can see where the Older Genereation of the Java Heap is growing in chunks.

Reference

[1] Sun JDK 1.6.0_11
[2] Sun JDK 1.5.0_17
[3] IBM JDK 1.5 (SR9-0)
[4] BM JDK 1.6 (SR3-0)
[5] http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html
[6] http://download.boulder.ibm.com/ibmdl/pub/software/dw/jdk/diagnosis/GCandMemory-042005.pdf