It sounds like you have not given the JVM enough heap. Sure, the working set may fit in 1Gbytes, but you still need to give it more. To understand why, read on.
Let us assume that when a garbage collector runs, it does an amount of work W1
that is proportional to the amount of non-garbage that it scans in order to identify the garbage and another amount of work W2
that is proportional to the amount of garbage that it finds. (In fact, it is a bit more complicated than this ... but lets keep the analysis simple.)
Suppose that you have a heap 1Gb heap with 0.9Gb occupied. Each time the GC runs, it can reclaim at most 0.1Gb of the heap space, and in doing so it needs to do W1 * 0.9Gb + W2 * 0.1Gb
of work. The amount of work per byte reclaimed is (W1 * 0.9Gb + W2 * 0.1Gb) / 0.1Gb
; i.e. 9 * W1 + W2
.
Now suppose that you have a 2Gb heap with 0.9Gb occupied. Now the amount of work per byte reclaimed is (W1 * 0.9Gb + W2 * 1.1Gb) / 1.1Gb
or W1 * 9/11 + W2
.
If you compare these two, you will see that the GC does roughly W1 * 8
more work per byte reclaimed in the 1Gb heap compared to the 2Gb heap.
As a general rule the closer to full you run the heap, the more inefficient the garbage collector is going to be. The lessons are:
configure the JVM to use a generous heap, and
configure the JVM to throw an OOM if there is less than (say) 25% of the heap free after running a full GC.