The old rule of thumb is that with GC one needs at least twice the memory than the max live heap. With little free space GC disables a lot of possible optimizations that bigger free space allows. Another Java-specific rule is that with heaps over 1-2GB one must think how to make data structures GC-friendly or how to split the application into separated processes. I guess the example program violated both of these.