Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to launch an aggressive and complete garbage collection in Java?

For memory optimization reasons, I'm launching myself the garbage collector during profiling, to check if objects are correctly cleaned after disposing of them.

The call to garbage collector is not enough, though, and it seems that there is no guarantee of what it will clean.

Is there a way to call it, to be sure it will recover as much as it can, in profiling conditions (this would have no point in production, of course)? Or is "calling it several times" the only way to be "almost sure"?

Or did I simply misunderstand something about the Garbage Collector?

like image 217
Gnoupi Avatar asked Apr 06 '10 13:04

Gnoupi


2 Answers

In all generality, a "complete garbage collection" is ill-defined. The GC detects unreachable objects, and reclaim them. It so happens that most GC implementations operate on a "cycle" basis, and once a complete cycle has run, it is possible to define an sound notion of "reclaimed space". Therefore, if you can run a full cycle and that cycle has found no reclaimable space at all, and the application is otherwise "idle" (e.g. not updating pointers), then you could say that you have temporarily reached a "fully collected" point.

Thus, you may want to do something like that:

Runtime r = Runtime.getRuntime();
r.gc();
long f = r.freeMemory();
long m = r.maxMemory();
long t = r.totalMemory();
for (;;) {
    r.gc();
    long f2 = r.freeMemory();
    long m2 = r.maxMemory();
    long t2 = r.totalMemory();
    if (f == f2 && m == m2 && t == t2) 
        break;
    f = f2; 
    m = m2; 
    t = t2; 
}   
System.out.println("Full GC achieved.");

This code relies on some assumptions, foremost of which being that Runtime.gc() is more than a "hint", and really forces some GC activity. Sun's JVM can be launched with the "-XX:-DisableExplicitGC" flag, which transforms Runtime.gc() into a no-op, effectively preventing the code above from doing anything.

There are a few caveats as well:

  • A JVM may have some background continuous activity, e.g. Timer threads. Some of that activity may be "hidden", i.e. not code written by the application developer. Also, think of the AWT / Swing event-dispatcher thread.

  • A GC run may trigger extra asynchronous activity, through finalizable objects or soft/weak/phantom references. Such activity is necessarily asynchronous and may run for unbounded amounts of time. Runtime.gc() will not wait for that to finish. It is actually a matter of definition: do you consider the GC to be "complete" if the finalizers and reference queues have not yet been processed ?

  • Nothing really forces the code above to terminate...

For a "more complete" GC, you may use the JVMTI interface (the one used by debuggers) which allows you to run the GC but also to pause the target JVM threads, which makes it much easier to define and achieve a "complete GC" state.

like image 144
Thomas Pornin Avatar answered Sep 30 '22 20:09

Thomas Pornin


Forcibly allocate the max amount of memory available. When JVM is on corner of OutOfMemoryError it's forced to run the GC.

byte[] boom = new byte[512 * 1024 * 1024]; // Assuming 512MB

Obviously for pure testing reasons :)

Another option is to use a better profiler. The one which gives better reports about memory usage and GC eligible objects.

like image 22
BalusC Avatar answered Sep 30 '22 20:09

BalusC