jvmemorypig
October 11, 2005 8:01 AM   RSS feed for this thread Subscribe

Okay, so I have a java program that's supposed to run in the background all the time. The problem is that the JVM allocates all the memory it can use, and then just sits there taking up a half-gig of ram. Is there any way I can get the thing to GC aggressively and release memory back to the system when it's done?

In this case, I can't simply set a smaller max memory size for the system.
posted by delmoi to computers & internet (16 comments total)
Does calling System.gc() do anything? That's supposed to manually invoke the garbage collector. If that doesn't work, maybe the problem is that you have a lot of variables that are not candidates for garbage collection? Java, as far as I understand it, does garbage collection automatically, cleaning up variables that don't have references. If you're storing anything in global variables I would bet they don't get collected.

I had a similar problem once, long, long ago (so the vm's of today may be much different), calling System.gc() worked for me. The problem I had at the time was that the jvm by default limited itself to 50 megs or so and I had to manually increase that.
posted by RustyBrooks at 8:30 AM on October 11, 2005


Hard to say how to fix this from just what you've posted.

You could explicitly force garbage collection [System.gc() or Runtime.gc() as appropriate], if you know you have some object that is a memory hog, and is only referenced occasionally. You'd incur some performance hit, maybe, if the memory allocation of the object on recall forced your system into swap, but on average, unless you are CPU bound, or using a really old JVM, forcing garbage collection explicitly doesn't buy you much, as a system without a maxed CPU is going to have enough resources to do gc itself, pretty well, as this Slashdot discussion from a few days ago covers.

Does this happen immediately when your program loads, or is it a problem that develops over time? Is the behavior occurring with some program action you can identify, i.e. a particular class loading, a looping condition, array initialization, etc.?
posted by paulsc at 8:34 AM on October 11, 2005


Running the VM in server mode might also help: java -server MyClass
posted by sbutler at 8:36 AM on October 11, 2005


This is one of my biggest complaints about Java. I don't know what planet they think, on modern operating systems, that they need to manage their own $*@($ heap.

That said, here are a few tips:

1) Even though the GC is supposed to be able to get rid of all objects, it can't do it if you haven't stopped referencing that object. Examples of things that might hold on to objects without you knowing:

- Items on a global undo stack
- Actions for UI elements, especially menu bars

It doesn't sound like you're developing a GUI app, but the same kinds of patterns will certainly appear.

2) You can call System.gc(), but in fact this can often do nothing- it only requests nicely to actually garbage collect.

3) There are some awesome bizarre options for better controlling the heap- see here for examples. For instance, since Java is a "generational" garbage collector, which means it'll hang onto stuff with no references until they're old (or it's out of memory), you can make its tolerance for old objects lower. Or you can make it so when it does allocate new memory, it only allocates a tiny bit at a time.

4) Lastly, you can use a profiler to track where the memory is. Here's a good one. It will show you the objects in memory, by size, and show you what still has references to them.

I hope I didn't misunderstand the question- I've never written an answer this long for ask.mefi.
posted by thethirdman at 8:41 AM on October 11, 2005


Why can't you set a smaller max heap size? You're not the one starting the VM?

If it makes y ou feel any better, this is probably all virtual memory that the JVM has allocated. You don't say whether you're on a UNIX system or windows, but some memory measurement tools will report a bajillion megs in use when there are only really a few physical pages allocated. The rest is reserved address space that hasn't been used yet.

Having said that, try to find where the JVM is being started and set the max heap size smaller via the -Xmx flag. That's the only way to do what you want.
posted by GuyZero at 8:59 AM on October 11, 2005


You can try compiling it with GCJ. In some cases it can reduce memory requirements tenfold.
posted by Sharcho at 9:11 AM on October 11, 2005


It's not an actual memory leak, where things get stored somewhere (like in a vector or some other collection) and never dereferenced, because the program never actually runs out of memory. It's just java.exe taking up so much memory and keeping it.

The problem is, it bogs down other processes running in the same machine.
posted by delmoi at 9:13 AM on October 11, 2005


Also, when I try "-server" I get:

Error: no `server' JVM at `C:\Program Files\Java\j2re1.4.2_07\bin\server\jvm.dll'.
posted by delmoi at 9:16 AM on October 11, 2005


Having said that, try to find where the JVM is being started and set the max heap size smaller via the -Xmx flag. That's the only way to do what you want.

The JVM needs a large heap in order to handle large documents. Small heap = small maximum file size.

I need to load the whole document into memory in order to create an SQL query with the file's contents, believe it or not (I didn't design the database). Otherwise I'd set the heap to 16 megs and push the files as a stream, rather then one-at a time.

If it makes y ou feel any better, this is probably all virtual memory that the JVM has allocated. You don't say whether you're on a UNIX system or windows, but some memory measurement tools will report a bajillion megs in use when there are only really a few physical pages allocated. The rest is reserved address space that hasn't been used yet.

Right, that's exactly the problem, and my (non-technical) boss is saying it's slowing down his PC. *sigh*

This product is basically done, set in stone. I'm just trying to figure out how to tweak the JVM to be less of a resource hog
posted by delmoi at 9:22 AM on October 11, 2005


How are you determining memory usage? Is this windows or unix? In Linux, my JVM has a "RES" (under top) of usually about 8MB. The VIRT or whatever column indicates it has access to 200MB of RAM, which for a while I mistakenly thought it was using. It turns out the JVM is only using a relatively small amount of memory. (I have a Java program that I wrote running 24/7 on my primary computer, and no problems because of it.)
posted by knave at 9:53 AM on October 11, 2005


If the product is, in fact, "set in stone" and your max doc size requires half a gig of RAM, the quickest way to keep the boss happy may be to add some RAM to his machine. Seriously.

That said, and supposing you are looking for something a bit more elegant, is it possible you could make better use of the RDBMS to do some of the work you are trying to do internally in your Java program in SQL? Take a look at suggestions 9 and 10 in this article on tips for Java with Oracle, as a general idea.
posted by paulsc at 9:53 AM on October 11, 2005


I don't believe the JVM ever returns allocated memory to the system. So either set a smaller maximum heap size, or convince your boss all that RAM is swapped out and he should ignore it.
posted by Nelson at 10:00 AM on October 11, 2005


I wrote a little utility program that would get the values for Runtime.freeMemory(), as well as the 'max' and 'total' memory. running the program with -Xmx32m worked fine, without trying to parse large documents. -Xmx256m also worked fine

At the end of the run, I'm back to having totalMemory() return about 1.9 megs, so it's not a problem with the program, just the JVM.

Are there any other JVM programs I can drop in to replace the sun one that arn't so piggish?
posted by delmoi at 11:58 AM on October 11, 2005


This is the stupidest solution ever, but I know of at least one large server app that had a cron job running that'd manually invoke the garbage collector hourly...
posted by mikeh at 1:38 PM on October 11, 2005


I doubt there is a "drop in" JVM for Windows that is substantially better than the Sun JVM, in terms of managing memory allocation dynamically with the Windows malloc, but if there is, banging Google isn't turning it up for me. MIght be nice if there were, but I could see all sorts of programming issues if the JVM were being resized smaller constantly, and so that has never been The Java Way, insofar as I'm aware. For one thing, it would be extremely tough to make gc work at all decently, and from the outset, gc was a big design point for Java. It still supports most applications very well, and has improved over time with new releases of the Java SDK and related tools.

If I understand all this correctly, your program starts and pulls some big data file into the JVM heap, the JVM swells up to 500 MB+ to hold it, you do some SQL statement(s) on the heap contents, do something with the result(s), and exit. For whatever reason, you can't use the conventional file streaming access methods to keep from having to handle the whole data file. You want the JVM to dynamically increase in size to handle the data files, and shrink its memory footprint when it isn't busy. Growing the JVM heap is automatically done by the JVM, but getting the JVM to "give back" memory it is not using to Windows isn't happening. Consequently, all other concurrent Windows applications bog down on low memory whenever you hit production sized data files with your Java program.

From an earlier response above, you appear to be working with the 1.4.2 Sun JVM on Windows. You may be able to take advantage of some of the new garbage collectors of this JVM. But gc works only within the JVM, so at best, the alternate gc types introduced with 1.4.2 might enable you to forestall the problem you are having (if you have the luxury of running data files in increasing size order), rather than eliminate it entirely. But don't get too excited; these collectors are basically optimizations for multi-processor systems, where parallel gc can benefit programs experiencing thread blocking in the JVM, and they work best with the -server JVM, which you'd need to download and install on your bosses machine, and call explicitly. Eventually, when you hit the big files, and even if you let the JVM allocate dynamically, your JVM is being told to "get big" to handle them, and it is going to stay that size until it is unloaded. Finally, even if you do spend time putting up the -server JVM, testing various gc strategies, etc., you can still run head on into Windows' page file management. Basically, that's the common wisdom that gc does not play nice with Windows virtual memory management, because the JVM simply isn't aware of what the operating system is doing to provide it with memory, and if gc is run, touching memory pages in the JVM that have been paged to disk, performance takes a big hit, as I/O goes way up. Even worse, I suppose, on a machine where the JVM itself is pushing all other applications to page out everything they can already.

Hence, my previous suggestion that the simplest solution may be to add more RAM.

If your process could run intermittently, and you wouldn't mind the overhead of starting and killing a JRE at each instantiation, I suppose you could package your application into a JAR archive, and then use the java application launcher tool to create a JRE for each run, and tear it down when you are done. A little kludgy, but straightforward. Even so, I think you'd feel punch whenever your Java program demanded half a gig. But, in theory, you'd get some performance back "automagically" when your Java run was done.

If you need to daemonize it, you could run JavaService But all this said, I do admit, I've never wrangled a half a gig JVM, that I didn't consider a prime candidate for program analysis... Good luck with this beast, and I'm not snarking, at all, saying that.
posted by paulsc at 5:42 PM on October 11, 2005


The latest Sun JRE is up to 1.5.0_05. You might want to try that one, it may have improvements in how it manages memory.
posted by Rhomboid at 6:10 PM on October 11, 2005


« Older Wireless Internet help....   |   Where can I find a very accura... Newer »
This thread is closed to new comments.