views:

357

answers:

6

Let's say I have a Java application which does roughly the following:

  1. Initialize (takes a long time because this is complicated)
  2. Do some stuff quickly
  3. Wait idly for a long time (your favorite mechanism here)
  4. Go to step 2.

Is there a way to encourage or force the JVM to flush its memory out to disk during long periods of idleness? (e.g. at the end of step 2, make some function call that effectively says "HEY JVM! I'm going to be going to sleep for a while.")

I don't mind using a big chunk of virtual memory, but physical memory is at a premium on the machine I'm using because there are many background processes.

+2  A: 

The operating system should handle this, I'd think.

Otherwise, you could manually store your application to disk or database post-initialization, and do a quicker initialization from that data, maybe?

Dean J
Downvoted because the OS does **not** release memory from Java applications. I've seen Java apps continue to consume their max available RAM for hours.
Zan Lynx
The OS isn't supposed to release the memory; that wasn't what I was trying to say. The OS *should* page the memory to disk if it's not actively being used, though, which would solve the OP's problem.
Dean J
OS paging is awful for Java. Java garbage collection starts a swap storm and then performance of that Java app goes into the trash.
Zan Lynx
If your application isn't doing anything, the garbage collector won't be triggered, and OS paging should be just fine. In the more general case, you're correct, but for the OP's point, well, I'm standing by what I said.
Dean J
+2  A: 

Instead of having your program sit idle and use up resources, why not schedule it with cron? Or better yet, since you're using Java, schedule it with Quartz? Do your best to cache elements of your lengthy initialization procedure so you don't have to pay a big penalty each time the scheduled task runs.

Asaph
is Quartz good at encouraging the JVM to page out memory to disk during idle periods?
Jason S
@Jason S: Quartz is basically cron written in Java. So all it does is schedule tasks which run their course and terminate, which naturally frees up all the memory. Basically what I am suggesting is that you re-think your design so that your task is a single execution that can be invoked multiple times by a scheduler, rather than a daemon.
Asaph
@Asaph Since Quartz is a cron in Java you have to keep your application running anyway, so the cron is scheduled in first place. Hance the problem of collecting the memory remains. This would only solve step 3
OscarRyz
@Oscar Reyes: Quartz is the daemon that is always running. Your program runs and terminates when Quartz calls it. As long as Quartz itself has a reasonable memory footprint, there is no memory problem.
Asaph
@Asaph: But, who call Quartz in first place ( I have never know this ) and this is more my own question. Does Quartz lives in it's own VM and from there start a new one to execute the task? Or does it live in a virtual machine and just creates and execute the task to be done in the **same** VM?
OscarRyz
@Oscar Reyes: Quartz is a daemon that runs in its own VM. It launches independent processes for the scheduled tasks which run in their own VM (if they are Java programs, which they don't have to be).
Asaph
+1  A: 

It's a bit of a hack to say the very least, but assuming you are on Win32 and if you are prepared to give up portability - write a small DLL that calls SetProcessWorkingSetSize and call into it using JNI. This allows you to suggest to the OS what the WS size should be. You can even specify -1, in which case the OS will attempt to page out as much as possible.

deeringc
Just make sure that the application really does *nothing* while it's waiting. Or at least as little as possible. That way the OS will also cut down the working set quite fast even without a call to SetProcessWorkingSetSize if memory pressure is high. And if memory pressure isn't high - then who cares? :^)
pgroke
A: 

Assuming this is something like a server that's waiting for a request, could you do this?

  1. Make two classes, Server and Worker.
  2. Server only listens and launches Worker when required.
  3. If Worker has never been initialised, initialise it.
  4. After Worker has finished doing whatever it needed to do, serialize it, write it to disk, and set the Worker object to null.
  5. Wait for a request.
  6. When a request is received, read the serialized Worker object from disk and load it into memory.
  7. Perform Worker tasks, when done, serialize, write out and set Worker object to null.
  8. Rinse and repeat.

This means that the memory-intensive Worker object gets unloaded from memory (when the gc next runs, and you can encourage the gc to run by calling System.gc() after setting the Worker object to null), but since you saved it's state, you have the ability to reload it from disk and let it do it's work without going through initialization again. If it needs to run every "x" hours, you can put a java.util.Timer in the Server class instead of listening on a socket.

EDIT: There is also a JVM option -Xmx which sets the maximum size of the JVM's heap. This is probably not helpful in this case, but just thought I'd throw it in there.

Chinmay Kanchi
I don't think this helps solve the OP's problem. He wants to be able to get his application to minimize its use of physical memory while idle. Your suggestion would reduce the space allocated to non-garbage objects, but not virtual memory usage, let alone physical memory usage. (And if you forced a GC to reclaim the freed objects, you'd actually INCREASE physical memory usage as the GC runs. Bad idea.)
Stephen C
If the app were written this way, it would take a bare minimum of RAM when it wasn't computing stuff, while not requiring repeated re-initialization (I can't see one ServerSocket or one Timer instance causing too many problems). In effect, you're creating a "swap" file and writing out your object to it and unloading it from RAM. Also, note that I didn't say that calling System.gc() was a good idea, only that it was possible. It's up to the OP to determine whether hinting that the gc should run is a good idea.
Chinmay Kanchi
PS: I know that this doesn't cause the program to be paged out. I see it more as a "home-grown" allocation of virtual memory. This is a guaranteed way for as much of the program as possible to be unloaded from physical RAM and makes no assumptions about how either the VM or the OS will behave.
Chinmay Kanchi
+2  A: 

The very first thing you must make sure, is that your object are garbage collectible. But that's just the first step.

Secondly, the memory used by the JVM may not be returned to the OS at all.

For instance. Let's say you have 100 mb of java objects, your VM size will be 100mb aprox. After the garbage collection you may reduce the heap usage to to 10 mb, but the VM will stay in something around 100 mb. This strategy is used to allow the VM have available memory for new objects.

To have the application returning "physical" memory to the system you have to check if your VM support such a thing.

There are additional VM options that may allow your app return more memory to the OS:

-XX:MaxHeapFreeRatio=70 Maximum percentage of heap free after GC to avoid shrinking.

-XX:MinHeapFreeRatio=40 Minimum percentage of heap free after GC to avoid expansion.

In my own interpretation using those options the VM will shirk if it fall below 70%. But quite frankly I don't know if only the heap will be shrink and returned to the OS or only shrink inside the VM.

For a complete description on hot the memory management works see:

Description of HotSpot GCs: Memory Management in the Java HotSpot Virtual Machine White Paper: http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

And please, please. Give it a try and measure and let us know back here if that effectively reduces the memory consumption.

OscarRyz
Oh yes, let us know. I did some work on this and found that with good attention to detail the entire scheduling was reduced to a stack. There was no way to effectively insure needed behaviour. Suggest nulling all ref's not needed during the sleepdown phase, reduce Thread priority ( bump back up on entry to step 2 above ) and use sleep(0) rather than yield or sleep() as Intel engineers found that was the only solution that was effective ( in c ) ... call long sleeps ( over a second or so ) and assume none of this is reliable and must be tested on deployment environment.
Nicholas Jordan
He doesn't want the memory returned to the OS. He just want's it out of the physical memory, i.e. he want's the process' working set trimmed.
pgroke
A: 

Isn't this what page files are for? If your JVM is idle for any length of time and doesn't access it's memory pages. It'll very likely get paged and thus won't be using much actual RAM.

One thing you could do though... Most daemon programs have a startup phase (where they parse files and create data structures etc) and a running phase where they use the objects created at startup. If the JVM is allowed to it will start on the second phase without doing a garbage collection potentially causing the size of the process to grow and then stay that big for the lifetime of the process (since GC never/infrequently reduces the actual size of the process).

If you make sure that all memory allocated at each distinct phase of the programs life is GCable before the next phase starts then you can use the -Xmx setting to force down the maximum size of the process and cause your program to constantly GC between phases. I've done that before with some success.

Benj
Not in Windows XP. I'm running a process monitor + the JVM continues to sit there and take whatever physical memory it has. Of course when the OS is down to tens of megabytes of RAM left, it tries doing things, but by then it's too late >:(
Jason S
What even if you restrict the maximum heap with -Xmx ? That shouldn't be possible!
Benj