views:

222

answers:

4

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?

+3  A: 

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.

BalusC
I am launching the GC directly from the profiler, I don't need to trigger it from the code. Thank you anyway.
Gnoupi
If launching the GC from profiler doesn't yield the expected result, then your profiler apparently does exactly the same as `Runtime#gc()`. I.e. it's up to the JVM whether it will actually run the GC. When you forcibly allocate the memory to a max to cause an (near) OOME, the JVM *will* execute the GC. To confirm this being happen, track `Object#destroy()`.
BalusC
@BalusC - fact is, calling the GC from the profiler will indeed show a use of it (in the appropriate tab), as well as a gain in memory. So in theory, everything works. But calling it after frees more memory, leading me to think that the first time was not enough.
Gnoupi
Ah, that way. It's also up to the JVM whether it will run a *thorough* GC. Sometimes it will only clean up the "oldest" unreferenced objects, or the ones consuming the most memory, or even clean nothing. You don't know. It's up to the JVM to decide since GC is a pretty expensive task. I think you need a better profiler so that you don't need to run GC.
BalusC
@BalusC - I'm using YourKit, and so far find it quite good a profiler. Do you have something better to suggest, or simply I haven't found some functions of it?
Gnoupi
+2  A: 

No, you cannot deterministically launch a full garbage collection.

If what you are after is trying to make sure that the GC has run at all during your profiling attempt, to remove any objects which would normally be removed (so that you can find where the actual leaks are), then it's best to make sure your test runs run for long enough where you can reasonably expect that the GC would run (or would run several times).

matt b
I'm launching the gc myself, so it's more about knowing that it did everything it could, and that remaining objects are indeed still referenced somehow. My goal is to not lose time on checking all references on something which would be eventually cleaned by the GC, later.
Gnoupi
+1  A: 

Basically, all you can do is this:

Runtime r = Runtime.getRuntime();
r.gc();

However, keep in mind that all references which can be reached somehow are considered to be alive, even if you think that they are not. Depending on the exact GC and settings this may or may not collect (most) memory which is not "alive".

Lucero
Except for [weak references](http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ref/WeakReference.html), of course.
Donal Fellows
The OP is already aware of this. `Runtime#gc()` will not forcibly execute the GC. It at hightest gives the JVM a *hint*. The JVM may or may not actually execute the GC. Often it will not.
BalusC
Yes, it's precisely to find the "even if you think that they are not" ones, that I would need to be sure of "the GC did everything it could". I can launch the gc() call from the profiler, so this is not a problem. The problem is only to reach a state of "is it for sure still referenced?"
Gnoupi
-1 "The call to garbage collector is not enough, though, and it seems that there is no guarantee of what it will clean."
matt b
+1  A: 

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.

Thomas Pornin
Yes, I was missing the way GC operates, obviously.
Gnoupi