views:

45

answers:

2

I am currently debugging a web application which has been causing intermittent problems especially when the hit rate is high. As each new user is given a session containing personalised navigation menu I thought I would try to see how much memory the session variables need.

In a code section of the JSP which handles the creation of session objects, I added the following code:

System.gc();
long orgHeap = Runtime.getRuntime().freeMemory();

... create session variables ...

System.gc();
log.info("Heap delta: " + (Runtime.getRuntime().freeMemory() - orgHeap));

What surprised me is that adding System.gc() breaks the app. It does not work any more as some objects are getting lost (or so it seems). Values which had been initialised are null. When the gc() calls are removed the app works fine. gc() should only remove objects which have been marked for deletion - right?

Has any one else had similar problems?

A: 

Garbage collection will never remove live objects, so I think your problem is elsewhere.

As you state this is under load - "hit rate is high" - this could be a case of using EJB's incorrectly, where you expect something to happen because it does so under low load (like getting the same EJB repeatedly when asking for it) but that change under high load (somebody else got that EJB, you get another).

Thorbjørn Ravn Andersen
+2  A: 

The only case I can think of where this might actually happen as you describe, is if you're using WeakReferences (or expired SoftReferences, which are broadly similar in this situation) to refer to objects that you still want to keep. The objects the might get into a state where they're collectable by the GC; but until it actually runs, you'll still be able to reach them through your references. Calling System.gc(), even though it doesn't have any guaranteed semantics, might cause the collector to run and harvest all these weakly-reachable objects.

That seems unlikely, though, because

  1. Accidentally using WeakReferences for strongly-reachable objects doesn't seem like an easy mistake to fall into. Even if you're using libraries, I find it hard to think of a case where you might feasibly end up using weak references by mistake.
  2. If this did happen, the behaviour of the application would be undefined anyway. Garbage collections could happen at any time, so you'd likely see inconsistent behaviour without the System.gc() call. You'd always have some bit of code that runs just after a collection anyway and so couldn't find its referent object.
  3. System.gc() doesn't theoretically do anything so it shouldn't be causing this.

That last point is the important one - why are you calling System.gc() anyway, when it is almost always counterproductive to call? I don't believe that you have a legitimate need to call it, it doesn't do anything you can rely on, and apparently it's breaking your application.

So if your app works fine without making the call, then just stop making it.

I would still consider inspecting how your app fits together though, because this is not going to be the actual cause of the problem and you likely have a deeper issue which is very fragile and just waiting to break on you later.

EDIT: Another possible reason for this could be simple timing. Calling System.gc() is likely to take a non-negligible amount of time. During this period, other threads could progress and change state in a way that the GCing thread isn't expecting. Hence when it returns from the call, the state of the world breaks its expectations and hence logic errors result. Again, this is just a guess, but is more plausible than the WeakReference one.

Andrzej Doyle
Thanks for your comments. I was wondering if something like deletion of WeakReferences is possible but I agree that it is unlikely. The reason for adding gc() is to try to determine how much memory is being used by the session objects. I wanted to tidy the heap before creating new objects. Also each session create runs on a single thread.
paul