• Post Reply Bookmark Topic Watch Topic
  • New Topic

Memory Usage in Java  RSS feed

 
Christopher Arthur
Ranch Hand
Posts: 149
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Recently I ran into some problems with memory usage in java that I would like to share with the ranch here.

What I used to think about memory usage is that the garbage collector is our java way of releasing memory that is no longer needed. For most of my tasks I have found that relying on the g.c. is a convenient way to program: just make an object whenever you need one, and when you're done with it just null out the reference and forget about it.

Recently, however, I have discovered that this attitude towards garbage doesn't work in some cases. I found myself in a situation where the garbage was accumulating faster than the g.c. could recycle it, and the VM would crash.

In the end, I found myself doing a lot of revision of my code. It started to look more like C code than my typical java style.

I got rid of all dynamic collections like arraylists and instead just set up a single, static array that was as large as I figured that my largest arraylist could be. I had been making arraylists of arraylists, but with a little thinking I realized that I never needed more than one at a time.

I removed almost all the calls to construct "new" objects when the number of such calls was variable and set up reusable buffers. And for methods that needed to return a primitive array type, especially, I got rid of statements like "return new double[]{.....}" and set up again a buffer on the level of instance where I knew that the result could be stored.

After these revisions the code was phenomenally faster and more efficient. What had been crashing in heaps of 350M was instead running in less than 25M.

I guess that one thing I'm wondering now about garbage collection has to do with local variables. For local variables whose references aren't returned out, are they more quickly disposed than instance types whose reference has been nulled?

Chris
 
Stan James
(instanceof Sidekick)
Ranch Hand
Posts: 8791
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've never run into a need to null out member variables. I guess that would happen when an object is done with its parts but somebody else further up the food chain is not done with the object. I'd be curious about a design that ran into that situation very often.

Local variables are eligibile for GC as soon as you exit the block in which they are declared. That might be inside the {} of an if test or a loop or the whole method. I think of GC as actually happening only when the JVM tries to allocate an object and can't, though newer JVMs are really free to do some any time they want. If a method is done with a large object but still has a lot to do, so that you are tempted to null out the local variable before going on, I'd say the method is crying out to be two methods.

All in all, I prioritize writing for dependency management and consideration of future readers and maintainers way over memory or performance tricks, and only been bitten by the choice once or twice. Ever.

Tell us more about how you got into memory trouble. Let's see if there are more Java-friendly ways out.
[ October 11, 2006: Message edited by: Stan James ]
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 16060
88
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Setting reference variables to null is almost never necessary. If the variable goes out of scope, the reference goes away automatically, and the object it was referencing becomes eligible for garbage collection (if there are no other references pointing to the same object, ofcourse).

It sounds to me like your problem didn't have much to do with garbage collection or how memory was allocated and freed - you initially just made a poor choice in algorithms, or didn't think about the memory usage of your program at all.

Garbage collection makes life easy, but it is not an excuse to forget about memory usage completely.
I guess that one thing I'm wondering now about garbage collection has to do with local variables. For local variables whose references aren't returned out, are they more quickly disposed than instance types whose reference has been nulled?

I don't know all the details about Java's garbage collector, but I do know that it contains some very sophisticated algorithms do to its work as efficiently as possible, and there is a certain optimization for short-lived objects.

See this for a lot of details: Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine
 
Christopher Arthur
Ranch Hand
Posts: 149
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper got it right---actually I didn't think about memory usage at all until after I got my program running. Only after I got most of the bugs out of the algorithms did I go back and try to refine it.

The program would start with a double array of 1000x1000x2 and do some operations on it. The data wasn't totally random, it was a kind of smooth vector field, like a map of a small part of a river, where each point has an arrow pointing as the flow on the surface.

The routine was to chart curves that follow the flow, with a few extra requirements. I had a method that would take a point, then iterate through the field along the flow, and I would do this thousands of times, even interpolating within the 1000x1000 array. Each time I finished with a flowline, I would store it in an ArrayList as a set of coordinates. I also used a boolean array of same size to keep track of where I had already been.
When I was done, I suppose I should have had a new set of data that shouldn't have been greater than 1000x1000x2x64 or about 128 megabytes, but it was much much larger than that.

There was another catch to the algorithm. At each step I wanted to scale the step size to be proportional to a glyph from a long sequence, so at each step I would create a new Shape2D just to get its bounding box and then discard it.

In the end I realized that I didn't need to have all the flowlines in memory at the same time, so I just started writing them out to disk, holding only one in memory at a time and then reusing the same fixed buffer of type double[][][] to store it before writing it out.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!