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.