tags:

views:

255

answers:

8

In the spirit of question http://stackoverflow.com/questions/3356005/java-why-does-maxpermsize-exist, I'd like to ask why the Sun JVM uses a fixed upper limit for the size of its memory allocation pool.

The default is 1/4 of your physical RAM (with upper & lower limit); as a consequence, if you have a memory-hungry application you have to manually change the limit (parameter -Xmx), or your app will perform poorly, possible even crash with an OutOfMemoryError.

Why does this fixed limit even exist? Why does the JVM not allocate memory as needed, like native programs do on most operating systems?

This would solve a whole class of common problems with Java software (just google to see how many hints there are on the net on solving problems by setting -Xmx).

Edit:

Some answers point out that this will protect the rest of the system from a Java program with a run-away memory leak; without the limit this would bring the whole system down by exhausting all memory. This is true, however, it is equally true for any other program, and modern OSes already let you limit the maximum memory for a programm (Linux ulimit, Windows "Job Objects"). So this does not really answer the question, which is "Why does the JVM do it differently from most other programs / runtime environments?".

+2  A: 

I think part of it has to do with the implementation of the Garbage Collector (GC). The GC is typically lazy, meaning it will only start really trying to reclaim memory internally when the heap is at its maximum size. If you didn't set an upper limit, the runtime would happily continue to inflate until it used every available bit of memory on your system.

That's because from the application's perspective, it's more performant to take more resources than exert effort to use the resources you already have to full utilization. This tends to make sense for a lot of (if not most) uses of Java, which is a server setting where the application is literally the only thing that matters on the server. It tends to be slightly less ideal when you're trying to implement a client in Java, which will run amongst dozens of other applications at the same time.

Remember that with native programs, the programmer typically requests but also explicitly cleans up resources. That isn't typically true with environments who do automatic memory management.

Mark Peters
I know that early JVM GCs behaved this way. Is it still true with "modern" versions of the JVM?Most other collected languages have built in some sort of dynamic GC threshold these days.
Andrew Shelansky
"The GC is typically lazy, meaning it will only start really trying to reclaim memory internally when the heap is at its maximum size. " I don't think this is true. -Xmx only sets the *maximum* heap size. On startup, the JVM will allocate less from the OS, and will only expand the heap as it sees fit (you can observe this if you start a JVM with a large -Xmx and watch its memory usage). So the JVM will GC as soon as the *current* heap is exhausted; it will (apparently) only expand the heap if the GC does not free enough memory.
sleske
@sleske and Andrew: I certainly didn't say it won't do *any* GC until the max heap is reached. But it will not, to my understanding, go to the same effort it will to avoid an OutOfMemoryError. That's just too costly. So @sleske, I think you're wrong in thinking that it will only expand the heap if it's at full utilization.
Mark Peters
@sleske: Actually I'm doing some testing and it looks like you're right: it does seem to do a full GC before any heap increse. I would love a link to something that explains this if either of you found one.
Mark Peters
@Mark: Well, there is a paper from Sun (eh, Oracle) that explains a lot about GC internals: http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html . There it says *By default, the virtual machine grows or shrinks the heap at each collection to try to keep the proportion of free space to live objects at each collection within a specific range.* (section 4.1). So it's not really "full GC before heap increase", rather it's "heap increase if full GC does not free up enough"...
sleske
+11  A: 

Why does this fixed limit even exist? Why does the JVM not allocate memory as needed, like native programs do on most operating systems?

The reason is NOT that the GC needs to know before hand what the maximum heap size can be. The JVM is clearly capable of expanding its heap ... up to the maximum ... and I'm sure it would be a relatively small change to remove that maximum. (After all, other GC'ed languages do this.) And it would equally be possible to have a simple way to say "use as much as you like".

I'm sure that the real reason is to protect the host operating system against the effects of faulty Java applications using all available memory. Running with an unbounded heap is potentially dangerous.

Basically, many operating systems (e.g. Windows, Linux) suffer serious performance degradation if some application tries to use all available memory. On Linux for example, the system may thrash badly, resulting in everything on the system running incredibly slowly. In the worst case, the system won't be able to start new processes, and existing processes may start crashing when the operating system refuses their (legitimate) requests for more memory. Often, the only option is to reboot.

If the JVM ran with an unbounded heap by default, any time someone ran a Java program with a storage leak ... or that simply tried to use too much memory ... they would risk bringing down the entire operating system.

In summary, having a default heap bound is a good thing because:

  • it protects the health of your system,
  • it encourages developers / users to think about memory usage by "hungry" applications, and
  • it potentially allows GC optimizations. (As suggested by other answers: it is plausible, but I cannot confirm this.)

EDIT

In response to the comments:

  • It doesn't really matter why Sun's JVMs live within a bounded heap, where other applications don't. They do, and advantages of doing so are (IMO) clear. Perhaps a more interesting question is why other managed languages don't put a bound on their heaps by default.

  • The -Xmx and ulimit approaches are qualitatively different. In the former case, the JVM has full knowledge of the limits it is running under and gets a chance to manage its memory usage accordingly. In the latter case, the first thing a typical C application knows about it is when a malloc call fails. The typical response is to exit with an error code (if the program checks the malloc result), or die with a segmentation fault. OK, a C application could in theory keep track of how much memory it has used, and try to respond to an impending memory crisis. But it would be hard work.

  • The other thing that is different about Java and C/C++ applications is that the former tend to be both more complicated and longer running. In practice, this means that Java applications are more likely to suffer from slow leaks. In the C/C++ case, the fact that memory management is harder means that developers don't attempt to build single applications of that complexity. Rather, they are more likely to build (say) a complex service by having a listener process fork of child processes to do stuff ... and then exit. This naturally mitigates the effect of memory leaks in the child process.

  • The idea of a JVM responding "adaptively" to requests from the OS to give memory back is interesting. But there is a BIG problem. In order to give a segment of memory back, the JVM first has to clear out any reachable objects in the segment. Typically that means running the garbage collector. But running the garbage is the last thing you want to do if the system is in a memory crisis ... because it is pretty much guaranteed to generate a burst of virtual memory paging.

Stephen C
As a sysamdin who has had to deal with leaky Java apps I can attest that it's better to bound the usage than to let the JVM consume the entire system's resources.It would be better still for JVMs to use some form of adaptive handling --- for example for a thread to monitor swap utilization and reduce block new memory allocations (and return memory to the OS) as soon as paging is detected.Obviously this should be optional and different strategies should be available/configurable. But it would be a preferable default for one of the most common usage cases (detected Java app servers).
Jim Dennis
The point about "protecting the rest of the system" is valid. However, this is equally true for any other program, and modern OSes already let you limit the maximum memory for a programm (Linux ulimit, Windows "Job Objects"). So this does not really answer the question, which is "Why does the JVM do it differently from most other programs / runtime environments?".
sleske
Memory allocated with the brk/sbrk syscalls is permanant, but most allocators include options to use a private mmap of /dev/zero as well, which can be returned to the OS with a simple call to munmap.
Recurse
@Stephen C: Thanks for the explanations of possible reasons for limiting memory. I found the comparison to C/C++ quite interesting. So I guess there is no single big reason for having a heap limit; it was a design decision, which is always a tradeoff which has many reasons.
sleske
Most operating systems also have a way to limit the memory usage of each application. It seems much cleaner to me if this is something the OS should handle, not each application by itself, also not the JVM.
Albert
@Albert - That would be nice. The problem is that the JVM *has* to know how much memory it should work with, otherwise GC cannot be implemented efficiently. Also, it is a plain fact that an OS *cannot accurately predict* how much memory an application / JVM will need.
Stephen C
A: 

It does allocate memory as needed, up to -Xmx ;)

One reason I can think of is that once the JVM allocates an amount of memory for its heap, it will never let it go. So if your heap has no upper bound, the JVM may just grab all the free memory on the system and then never let it go.

The upper bound also tells the JVM when it needs to do a full garbage collection. If your app is still under the upper bound, the JVM will postpone garbage collection and let the memory footprint of your application grow.

Native programs can die due to out of memory errors as well since native applications also have a memory limit: the memory available on the system - the memory already held by other applications.

The JVM also needs a contiguous block of system memory in order for garbage collection to be performed efficiently.

mlaw
Incorrect; the JVM absolutely does release memory back to the system.
Software Monkey
"The JVM also needs a contiguous block of system memory in order for garbage collection to be performed efficiently." That seems very dubious; why would it need that? Do you have any references for the claim?
sleske
@Software Monkey: Prove it or cite it. I've personally tried on two different systems to ever see my Java process (running a program that takes a GB of ram then immediately releases it) release memory back to the OS and have never seen it happen.
Mark Peters
Software Monkey
+1  A: 

It is due to the design of the JVM. Other JVM's (like the one from Microsoft and some IBM ones) can use all the memory available in the system if needed, without an arbitrary limit.

I believe it allows for GC-optimizations.

Thorbjørn Ravn Andersen
A: 

Just one of the many places where Java is really good for servers and really bad for desktops. Awesome that you can give it a max and min amount of memory on a server, the JVM would lose a lot of power if it just grew until it started paging to disk. However, this makes it really terrible for a desktop application running on many different machines :)

bwawok
-1 That's not answering the question.
sleske
Yes it does, it explains the focus of Java and hence the behavior. Java in it's current state has a focus on a server environment, where you will manually configure the memory parameters. Most servers would prefer hand tuning the memory like this...
bwawok
Absolutely not true about desktops - I wish Firefox and Thunderbird (which I love, BTW) were so bounded by default. I hate that FF sometimes eats 200+ MB. Furthermore I have written a dozen Java apps of varying complexity, including GUIs and an Application server environment, none of which require > 64 MB heap in typical use.
Software Monkey
A: 

Would it not make more sense to separate the upper bound that triggers GC and the maximum that can be allocated ? Once the memory allocated hits the upper-bound, GC can kick in and release some memory to the free pool. sort of like how I clean my desk that I share with my co-worker. I have a large desk, and my threshold of how much junk I can tolerate on the table is much less than the size of my desk. I don't need to have fill up every available inch before I garbage collect.

I could also return some of the desk space that I using to my co-worker, who is sharing my desk....I understand jvms don't return memory back to the system after they've allocated it to themselves, but it does not have to be that way no ?

unmaskableinterrupt
"Would it not make more sense to separate the upper bound that triggers GC and the maximum that can be allocated ?" This is already the case. -Xmx only sets the *maximum* heap size. On startup the heap will typically be smaller. The JVM will trigger a GC when the heap is full, and will only enlarge the heap if the GC did not free enough. Just watch the memory usage of a running Java app.
sleske
A: 

I think that the upper limit for memory is is linked to the fact that JVM is a VM.

As any physical machine has a given (fixed) ammount of RAM so the VM has one.

The maximal size makes the JVM easier to manage by the operating system and ensures some performance gains(less swapping).

Sun' JVM also works in quite limited hardware architecture(embedded ARM systems) and there the management of resources is crucial.

Daniel Voina
+1  A: 

Hm, I'll try summarizing the answers so far.

There is no technical reason why the JVM needs to have a hard limit for its heap size. It could have been implemented without one, and in fact many other dynamic languages do not have this.

Therefore, giving the JVM a heap size limit was simply a design decision by the implementors. Second-guessing why this was done is a bit difficult, and there may not be a single reason. The most likely reason is that it helps protect a system from a Java program with a memory leak, which might otherwise exhaust all RAM and cause other apps to crash or the system to thrash.

Sun could have omitted the feature and simply told people to use the OS-native resource limiting mechanisms, but they probably wanted to always have a limit, so they implemented it themselves. At any rate, the JVM needs to be aware of any such limit (to adapt its GC strategy), so using an OS-native mechanism would not have saved much programming effort.

Also, there is one reason why such a built-in limit is more important for the JVM than for a "normal" program without GC (such as a C/C++ program):

Unlike a program with manual memory management, a program using GC does not really have a well-defined memory requirement. It only has a minimum requirement, i.e. the sum of the sizes of all objects that are actually live (reachable) at a given point in time. However, in practice a program will need additional memory to hold dead, but not yet GCed objects, because the GC cannot collect every object right away, as that would cause too much GC overhead. So GC only kicks in from time to time, and therefore some "breathing room" is required on the heap, where dead objects can await the GC.

This means that the memory required for a program using GC is really a compromise between saving memory and having good througput (by letting the GC run less often). So in some cases it may make sense to set the heap limit lower than what the JVM would use if it could, so save RAM at the expense of performance. To do this, there needs to be a way to set a heap limit.

sleske