OutOfMemoryError when Eden space only 1/3 full
Hi,
I've been reading up on the inner workings of the different GC algorithms and I thought I had a good handle on them all, but I'm a bit baffled with the behaviour I'm seeing. I'm running a server process on RH Linux, AMD 64-bit. The machine has 4G of physical memory and two processors. My Java process is running with Xmx3000m and XX:MaxPermSize=92m.
The trouble is that I am running into OutOfMemoryErrors on my process. Before I order more memory I thought I'd try and see if there was any tweaking I could do so I fired up visualgc. At the moment of the OutOfMemory, my Eden space is 1G, but only 333M is committed, of which 305M is used. Survivor 0 & 1 are 333M committed, 0 used. The old generation is 2G committed, 2G used.
How should I understand that? Why are my survivor spaces empty? Why is the eden space only at 333M? And the numbers don't seem to add up: why do I get OOM when I have 3G of heap and only 2.3G used? (And why do the totals of the "max" numbers add up to 3.6G?)
Thanks for any pointers you can give me!
Robert
[1087 byte] By [
RGibsona] at [2007-10-3 7:33:40]

To come back to this question: I have a related symptom in a small test app (below). My test app just allocates blocks of memory in decreasing chunks until there is no memory left. But on my test machine it fails much earlier that I would have expected - it doesn't manage to allocate 35M when the free memory should be about 140M.
I again used visualgc to look at the heap and again what I don't understand is that my Eden Space is shown as (170.5M, 3.0M):121K which I take to mean that the maximum capacity is 170M but the current capacity is just 3M (of which 121K are used). Why hasn't the current capacity increased to the max capacity? Why does my application throw OOME at this point? How can I realistically estimate the free memory available to my application? Is this just a pathological case that I'm triggering in my test app that I'm unlikely to run up against in production?
Here are the last few lines from the GC logs just before the OOME - at this point my test is trying to allocate 35M and there should be about 140M free. The test code follows and I was running it with JDK 1.6 on an AMD 64-bit machine under RedHat Linux FC6 with java -verbose:gc -XX:+PrintHeapAtGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -ea -Xmx512M tests.MemoryTest
Thanks a lot!
R{Heap before GC invocations=5 (full 2):
PSYoungGentotal 3584K, used 0K [0x00002aaac9b40000, 0x00002aaaca240000, 0x00002aaad45e0000)
eden space 3072K, 0% used [0x00002aaac9b40000,0x00002aaac9b40000,0x00002aaac9e40000)
from space 512K, 0% used [0x00002aaac9e40000,0x00002aaac9e40000,0x00002aaac9ec0000)
tospace 512K, 0% used [0x00002aaaca1c0000,0x00002aaaca1c0000,0x00002aaaca240000)
PSOldGentotal 349568K, used 318506K [0x00002aaab45e0000, 0x00002aaac9b40000, 0x00002aaac9b40000)
object space 349568K, 91% used [0x00002aaab45e0000,0x00002aaac7ceaad0,0x00002aaac9b40000)
PSPermGentotal 21248K, used 2481K [0x00002aaaaf1e0000, 0x00002aaab06a0000, 0x00002aaab45e0000)
object space 21248K, 11% used [0x00002aaaaf1e0000,0x00002aaaaf44c780,0x00002aaab06a0000)
4.882: [Full GC [PSYoungGen: 0K->0K(3584K)] [PSOldGen: 318506K->318501K(339520K)] 318506K->318501K(343104K) [PSPermGen: 2481K->2478K(21248K)], 0.0143080 secs]
Heap after GC invocations=5 (full 2):
PSYoungGentotal 3584K, used 0K [0x00002aaac9b40000, 0x00002aaaca240000, 0x00002aaad45e0000)
eden space 3072K, 0% used [0x00002aaac9b40000,0x00002aaac9b40000,0x00002aaac9e40000)
from space 512K, 0% used [0x00002aaac9e40000,0x00002aaac9e40000,0x00002aaac9ec0000)
tospace 512K, 0% used [0x00002aaaca1c0000,0x00002aaaca1c0000,0x00002aaaca240000)
PSOldGentotal 339520K, used 318501K [0x00002aaab45e0000, 0x00002aaac9170000, 0x00002aaac9b40000)
object space 339520K, 93% used [0x00002aaab45e0000,0x00002aaac7ce96d0,0x00002aaac9170000)
PSPermGentotal 21248K, used 2478K [0x00002aaaaf1e0000, 0x00002aaab06a0000, 0x00002aaab45e0000)
object space 21248K, 11% used [0x00002aaaaf1e0000,0x00002aaaaf44bac8,0x00002aaab06a0000)
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.nexgenfs.apps.tests.memory.MemoryTest.main(MemoryTest.java:24)
Heap
PSYoungGentotal 3584K, used 123K [0x00002aaac9b40000, 0x00002aaaca240000, 0x00002aaad45e0000)
eden space 3072K, 4% used [0x00002aaac9b40000,0x00002aaac9b5ec40,0x00002aaac9e40000)
from space 512K, 0% used [0x00002aaac9e40000,0x00002aaac9e40000,0x00002aaac9ec0000)
tospace 512K, 0% used [0x00002aaaca1c0000,0x00002aaaca1c0000,0x00002aaaca240000)
PSOldGentotal 349568K, used 318501K [0x00002aaab45e0000, 0x00002aaac9b40000, 0x00002aaac9b40000)
object space 349568K, 91% used [0x00002aaab45e0000,0x00002aaac7ce96d0,0x00002aaac9b40000)
PSPermGentotal 21248K, used 2519K [0x00002aaaaf1e0000, 0x00002aaab06a0000, 0x00002aaab45e0000)
object space 21248K, 11% used [0x00002aaaaf1e0000,0x00002aaaaf455f28,0x00002aaab06a0000)
package tests;
import java.util.LinkedList;
import java.util.List;
public class MemoryTest
{
public static void main (String[] args) throws InterruptedException
{
final int periodicity = 1000;
final List<byte[]> fillMemory = new LinkedList<byte[]> ();
while (true)
{
final long mem = getFreeMemory ();
showFree ();
final int take;
if (mem / 4 > (long) Integer.MAX_VALUE)
{
take = Integer.MAX_VALUE;
} else
{
take = (int) (mem / 4);
}
System.out.println ("take = " + (take/1024) + "K");
fillMemory.add (new byte[take]);
System.out.println ("Taken");
showFree ();
Thread.sleep (periodicity);
}
}
private static long getFreeMemory ()
{
// Get total free memory rather than total free committed memory
final Runtime rt = Runtime.getRuntime ();
long free = rt.freeMemory () + rt.maxMemory () - rt.totalMemory ();
return free;
}
private static void showFree ()
{
long free = getFreeMemory ();
System.out.println ("Memory.availableMemory () = " + (free /1024) + "K");
}
}
Your test program is trying to allocate a chunk of memory that is larger than the
currently committed young generation. That allocation is done out of the old
generation until the old generation fills up and I think that is when you get the
out-of-memory. Because the young generation is never filled, it is never collected
separately. Resizing of the young generation happens only after a collection so
the young generation is never resized. It's not a good policy for your test program
but a policy that automatically expanded the young generation in order to do the
allocation out of the young generation might not be the best policy in general.
If you application does not do 35m allocations, I'd suggest reducing the allocation size
so that several fit into the initial young generation. If you application really does to
35m allocations, try increasing the initial size of the young generation. I ran your
test program with
-Xmx512M -Xms512m -Xmn170m
and the final state of the heap at the out-of-memory was
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at tests.MemoryTest.main(MemoryTest.java:24)
Heap
PSYoungGentotal 154112K, used 125893K [0xf1400000, 0xfc000000, 0xfc000000)
eden space 132096K, 95% used [0xf1400000,0xf8ef1440,0xf9500000)
from space 22016K, 0% used [0xf9500000,0xf9500000,0xfaa80000)
tospace 22016K, 0% used [0xfaa80000,0xfaa80000,0xfc000000)
PSOldGentotal 352256K, used 344446K [0xdbc00000, 0xf1400000, 0xf1400000)
object space 352256K, 97% used [0xdbc00000,0xf0c5fb20,0xf1400000)
PSPermGentotal 16384K, used 1686K [0xd7c00000, 0xd8c00000, 0xdbc00000)
object space 16384K, 10% used [0xd7c00000,0xd7da5990,0xd8c00000)