-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
1.2.0, 1.2.1, 1.2.2, 1.3.0
-
generic
-
generic
Name: dkC59003 Date: 06/24/99
The displayed below MemoryEater program tries to eat all the heap, and may explicitly invoke
the method System.gc() just trying to improve memory utilization. Surprisingly, I have
found that MemoryEater allocates 2.5-3 Mb of memory more if it avoids to invoke System.gc()
method. This looks like explicit GC activation eats memory rather then helps to recycle it.
I have tried MemoryEater for HotSpot releases 1.0-fcs, 1.3-betaH, and 2.0-devA, as well as
for classic VM shipped with JDK releases 1.2, 1.2.1, 1.2.2U, and 1.3H, both with JIT on and
off. All of those VMs suffer this problem, so I believe this is a bug in implementation of
System.gc() method.
Here are the results of MemoryEater execution under JDK 1.3-H on the following computers:
- Pentium-II, 350MHz, 128Mb RAM, Windows NT 4.0 Workstation, IE 4.01
- Sparc Ultra-1, 200MHz, 128Mb RAM, Solaris 2.7
If System.gc() was invoked, HotSpot 1.3-betaH for Win32 results in:
>>>>D:\jdk1.3H\bin\java MemoryEater invokeGC
pool[3313874]=new Object(); // elapsed 2.654s
pool[3313875]=new Object(); // elapsed 3.485s
pool[3313876]=new Object(); // elapsed 2.403s
pool[3313877]=new Object(); // elapsed 2.414s
pool[3313878]=new Object(); // elapsed 2.413s
pool[3313879]=new Object(); // elapsed 2.403s
pool[3313880]=new Object(); // elapsed 2.414s
pool[3313881]=new Object(); // elapsed 2.413s
pool[3313882]=new Object(); // elapsed 2.404s
pool[3313883]=new Object(); // elapsed 2.413s
pool[3313884]=new Object(); // elapsed 2.404s
pool[3313885]=new Object(); // elapsed 2.423s
pool[3313886]=new Object(); // elapsed 2.404s
pool[3313887]=new Object(); // elapsed 2.403s
pool[3313888]=new Object(); // elapsed 2.414s
pool[3313889]=new Object(); // elapsed 2.413s
pool[3313890]=new Object(); // elapsed 2.414s
pool[3313891]=new Object(); // elapsed 2.403s
pool[3313892]=new Object(); // elapsed 2.404s
pool[3313893]=new Object(); // elapsed 2.413s
pool[3313894]=new Object(); // elapsed 2.413s
pool[3313895]=new Object(); // elapsed 2.414s
pool[3313896]=new Object(); // elapsed 2.423s
pool[3313897]=new Object(); // elapsed 2.404s
pool[3313898]=new Object(); // elapsed 2.423s
OK: heap seems almost exhausted.
And if System.gc() was not invoked, HotSpot 1.3-betaH for Win32 results in:
>>>>D:\hotspot\jdk1.3H\bin\java MemoryEater
pool[3313881]=new Object(); // elapsed 2.653s
pool[3346962]=new Object(); // elapsed 1.051s
pool[4133305]=new Object(); // elapsed 4.266s
pool[4133306]=new Object(); // elapsed 65.495s
OK: heap seems almost exhausted.
So, explicit invocations of System.gc() prevented the HotSpot VM shipped with
JDK 1.3-H for Win32 from allocating ~800000 extra instances of the class Object.
(I guess, that each Object consumes at least 4 bytes to keep a reference to its
Class instance, so that 800000 objects could occupy at least 3.2Mb in the heap.)
Similarly, if System.gc() was invoked, libsunwjit.so shipped with JDK 1.3-H for
Sparc results in:
>>>> time $JAVA MemoryEater invokeGC
pool[1053143]=new Object(); // elapsed 1.422s
pool[1053144]=new Object(); // elapsed 1.373s
pool[1053145]=new Object(); // elapsed 1.35s
pool[1053146]=new Object(); // elapsed 1.345s
pool[1053147]=new Object(); // elapsed 1.35s
pool[1053148]=new Object(); // elapsed 1.349s
pool[1053149]=new Object(); // elapsed 1.35s
pool[1053150]=new Object(); // elapsed 1.347s
pool[1053151]=new Object(); // elapsed 1.345s
pool[1053152]=new Object(); // elapsed 1.35s
pool[1053153]=new Object(); // elapsed 1.363s
pool[1053154]=new Object(); // elapsed 1.344s
pool[1053155]=new Object(); // elapsed 1.345s
pool[1053156]=new Object(); // elapsed 1.349s
pool[1053157]=new Object(); // elapsed 1.347s
pool[1053158]=new Object(); // elapsed 1.346s
pool[1053159]=new Object(); // elapsed 1.351s
pool[1053160]=new Object(); // elapsed 1.378s
pool[1053161]=new Object(); // elapsed 1.35s
pool[1053162]=new Object(); // elapsed 1.346s
pool[1053163]=new Object(); // elapsed 1.363s
pool[1053164]=new Object(); // elapsed 1.347s
pool[1053165]=new Object(); // elapsed 1.348s
pool[1053166]=new Object(); // elapsed 1.348s
pool[1053167]=new Object(); // elapsed 1.349s
pool[1053168]=new Object(); // elapsed 1.345s
pool[1053169]=new Object(); // elapsed 1.35s
pool[1053170]=new Object(); // elapsed 1.348s
pool[1053171]=new Object(); // elapsed 1.345s
pool[1053172]=new Object(); // elapsed 1.349s
pool[1053173]=new Object(); // elapsed 1.347s
pool[1053174]=new Object(); // elapsed 1.37s
pool[1053175]=new Object(); // elapsed 1.346s
pool[1053176]=new Object(); // elapsed 1.346s
pool[1053177]=new Object(); // elapsed 1.343s
pool[1053178]=new Object(); // elapsed 1.347s
pool[1053179]=new Object(); // elapsed 1.349s
pool[1053180]=new Object(); // elapsed 1.343s
pool[1053181]=new Object(); // elapsed 1.35s
pool[1053182]=new Object(); // elapsed 1.349s
pool[1053183]=new Object(); // elapsed 1.345s
pool[1053184]=new Object(); // elapsed 1.344s
pool[1053185]=new Object(); // elapsed 1.355s
pool[1053186]=new Object(); // elapsed 1.367s
pool[1053187]=new Object(); // elapsed 1.345s
OK: heap seems almost exhausted.
69.0u 0.0s 1:12 95% 0+0k 0+0io 0pf+0w
And if System.gc() was not invoked, the same VM results in:
>>>> time $JAVA MemoryEater
pool[1053145]=new Object(); // elapsed 1.46s
pool[1270989]=new Object(); // elapsed 1.326s
pool[1577092]=new Object(); // elapsed 2.098s
pool[1673356]=new Object(); // elapsed 1.269s
pool[1674103]=new Object(); // elapsed 1.292s
pool[1674109]=new Object(); // elapsed 1.267s
pool[1674110]=new Object(); // elapsed 1.311s
pool[1674111]=new Object(); // elapsed 1.296s
pool[1674112]=new Object(); // elapsed 3.888s
pool[1674113]=new Object(); // elapsed 1.314s
pool[1674114]=new Object(); // elapsed 3.889s
pool[1674115]=new Object(); // elapsed 3.869s
pool[1674116]=new Object(); // elapsed 3.844s
pool[1674117]=new Object(); // elapsed 3.902s
pool[1674118]=new Object(); // elapsed 6.446s
pool[1674119]=new Object(); // elapsed 9.192s
OK: heap seems exhausted.
106.0u 0.0s 1:48 97% 0+0k 0+0io 0pf+0w
Again, explicit invocations of System.gc() prevented the classic VM 1.3-H for
Sparc from allocating ~600000 extra instances of the class Object. (I.e.: method
System.gc() has eaten ~2.5Mb in the heap, rather than improve memory utilization.)
Results for other JDK releases are similar: classic VM looses ~2.5Mb of memory
when invokes System.gc(), and HotSpot looses ~3Mb.
Following is the source of MemoryEater class:
/* @(#)MemoryEater.java 1.1 99/06/24
* Copyright 99/06/24 Sun Microsystems, Inc.
*/
/**
* The class MemoryEater tries to allocate as many tiny instances of the type
* <code>Object</code> as possible. Command-line parameters may either enforce
* or do not enforce explicit invocation of garbage collector:
* <br>
* java MemoryEater [invokeGC]
*
* <p>When heap becomes almost exhausted, JVM beging to spend esential amounts of time
* to allocate every new <code>Object</code>. If the parameter <code>invokeGC</code>
* presents, MemoryEater invokes <code>System.gc()</code> when allocations speed slows
* down in order to allow garbage collector to work better.
*
* <p>Surprisingly, I have found, that the class MemoryEater could allocate 600 to 800
* thousands more of <code>Object</code> instances if <code>System.gc()</code> was not
* explicitly invoked. If my guess is true, that every <code>Object</code> descriptor
* consumes at least 4 bytes for reference to its class, then implicit invocations of
* <code>System.gc()</code> prevented MemoryEater from allocating at least 2.4Mb to
* 3.2Mb of extra memory.
*
* <p>I have observed this problem for the latest classic VMs released with JDK 1.2,
* 1.2.1, 1.2.2, and 1.3, and for HotSpot releases 1.0-fcs both Win32 and Sparc, and
* 1.3-betaI, and 2.0-devA for Win32. It looks like <code>System.gc()</code> consumes
* too much of memory for its own needs, and this results in that GC seems to eat
* memory rather than recycle it.
*
* <p>Note, that execution is terminated if memory allocation slows down crucially.
* This is a workaround intended to avoid the HotSpot bug:
* <br>
* #4248801 (P1/S5) slow memory allocation when heap is almost exhausted
*
* @author Eugene I. Latkin
*/
public class MemoryEater {
/**
* Parameter <code>args[]</code> may contain the keyword ``<code>invokeGC</code>''.
*/
public static void main (String args[]) {
// Parse parameter.
boolean invokeGC = true;
switch (args.length) {
case 0:
invokeGC = false;
break;
case 1:
if (args[0].equals("invokeGC"))
break;
default:
System.err.println("Illegal command-line parameter(s).");
System.err.println("Execute:");
System.err.println(" java MemoryEater invokeGC");
System.err.println("or:");
System.err.println(" java MemoryEater");
return;
};
// Repository for objects, which should be allocated:
Object pool[];
int index=0;
try {
pool = new Object [1<<23]; // ~8 millions
} catch (OutOfMemoryError oome) {
System.err.println("Cannot initiate the test: need more memory.");
return;
};
// Sum up time spent, when it was hard to JVM to allocate next object
// (i.e.: when JVM has spent more than 1 second to allocate new object):
double totalDelay = 0;
long timeMark = System.currentTimeMillis();
try {
for (; index<pool.length; index++) {
//-------------------------
pool[index] = new Object();
long nextTimeMark = System.currentTimeMillis();
long elapsed = nextTimeMark - timeMark;
timeMark = nextTimeMark;
//----------------------
if (elapsed > 1000) {
double seconds = elapsed / 1000.0;
System.out.println("pool[" + index + "]=new Object(); // elapsed "+ seconds + "s");
totalDelay += seconds;
if (totalDelay > 60) {
System.out.println("OK: heap seems almost exhausted.");
return;
};
if (invokeGC)
System.gc();
};
};
} catch (OutOfMemoryError oome) {
pool = null;
};
System.out.println("OK: heap seems exhausted.");
};
}
======================================================================