Java Memory Leak Question

I looked at the profiling data for my application and found a large number of objects active that had been created by a specific line. I assumed these objects would have been considered unreachable. Here is a little background:

A utility class with one instance in the application contains something like the following method:

public final static int utilityMethod (int i) {

if (…) {
int result=new int[2];
result[0]=98;
result[1]=527;
return result;
}
return null;
}
The application does something like this:

class Application {
Utility utl=new utility();

void applicationMethod () {
int result;
for (int i=0;i<99999;i++) {
result=utl.utilityMethod(i);
}
}
}
The profiling data shows that the following line in the utilityMethod:
int result=new int[3];

has created an enourmous number of active objects. The longer I run the app the active objects and memory associated with that line continues to increase. Any idea why?

Additional info:
I’m using Sun’s JVM 1.4.2
I switched the “result” variable in the utilityMethod to be an instance variable instead of a local variable and the problem went away

Disclaimer: I’m no Java expert, although I have done quite a bit of programming in it. This is just my $0.02.

One of the advantages of Java over C++ (at least for people like me) is that you don’t have to collect freed objects manually, and hence your program can’t have a memory leak. The JRE might well have a leak, but I don’t think that’s a problem - at least, I haven’t come across any reports thereof.

The garbage collector only runs at somewhat infrequent intervals, though, to avoid impacting too much on system resources. If you’re creating a lot of objects in a hurry, you might notice a huge spike in memory usage. Have you tried running System.gc() to see if it helps? That requests (but not forces) the VM to run the garbage collection as soon as convenient.

hmm, perhaps I don’t understand the question, so sorry if I’m restating the obvious. this is what i see: an int array is being dynamically allocated every time utilityMethod is called, and applicationMethod calls utilityMethod it 100K times in a row. So memory is consumed rapidly, by all those int arrays.

another solution: pass result as a second arg to utilityMethod – the output will be written to this array argument, which can be read after utilityMethod returns (arrays are not copied when they are passed as args). I don’t think the overhead would be any less than if result were a member variable, but perhaps its safer if you’re using threads.

I’m not too familiar with Java, so apologies in advance for any inaccuracies.

When you say this:

…do you mean you made it a static variable? Or did you make it an instance variable and change utilityMethod to be an instance method? Because I believe (correct me if I’m wrong with Java) that you can’t access instance variables from static methods (unless, of course, you get hold of an instance some other way, such as passing one as a parameter or creating one in the method body). Whichever way you did it (static/static or instance/instance), what happens if you do it the other way?

Also, what happens if you keep result as a local variable, but change utilityMethod to be a non-static instance method? What about if you leave it static but call it through the class rather than the instance (that is, replace result=utl.utilityMethod(i); with result=Utility.utilityMethod(i);)?

As Dervorin suggests, I do think the most likely cause is the runtime environment simply seeing no need to do a collection. But, contrary to popular belief, memory leaks (or at least memory usage patterns so similar to leaks that you might as well call them leaks) can occur in garbage-collected environments. A very common cause of these are misbehaving static members. Static variables are never collected by the GC, and if they happen to point to objects that point to other objects that point to other objects, that entire object graph is ineligible for collection. Although your code doesn’t appear to have any static variables (just a static function), it’s possibly a good place to start looking.

I use the term “leak” because others do, but I understand the difference.

I didn’t try to force gc, I did think about it, but it looked to me like the problem was a little deeper. The profiler shows the number of objects in the active column and I assume nothing in the active column will get collected.

Yes it’s dynamically allocated, but each iteration the old pointer is lost and I assumed that would move the object out of the active column on the profiler.

That would work, but would require a lot of rewrite.

Good point. I know I didn’t change the method, so the variable must be static not instance. It’s at home so I can’t check it.

Sounds like I need to dig deeper on this utility class and the static modifier. Originally it was entirely static, but it grew and some functionality required me to create an instance and pass that around.

Does your JVM do defragmentation? If you’re allocating a bunch of things between when you allocate each array of 2 ints, then when all of the garbage is collected, your memory space will have all these tiny little holes that are probably unusable by most other objects that you might want to create.

Well, the pointer is lost, but what it points to is still taking up memory – it can’t be re-used until GC runs. Just keep in mind that allocating memory is expensive: garbage collectors are great but they run faster when there’s nothing to collect. The solution you proposed (of a member variable) sounds like it works because it eliminates the “leaked” memory. My suggestion was just a syntactic variation of the same idea.

I’ve never actually run a profiler on Java, but it sounds like ‘active’ is also tracking memory that is ready to be collected. From the code you have up, I couldn’t see another explanation.

There is no good reason why those objects aren’t eligible for GC, and in cases like this, where the profiler claims tons of objects eating memory, I try a couple of approaches:

  1. Stick a few forced GCs in your code to prove the hypothesis. If that doesn’t release a whole mess of memory, then you are barking up the wrong tree.
  2. Fiddle around with the memory parameters to the JVM.

This stuff is annoying enough without wasting tons of time profiling just to find that the GC didn’t feel like getting off its lazy ass to do some work.