JVM maximum size (32bit/64bit)

First of all, let's have a global look about JVM Memory.
If we are using a 32bit OS, then the jvm can't have more than 4GB of RAM
Over this 4Gig, Os have their own needs. For exemple Windows needs 2GB for its kernel usage. So it left 2GB for our JVM.
But JVM implementation needs to have a continous memory region for the heap.

Here is the explanation :

The reason we need a contiguous memory region for the heap is that we have a bunch of side data structures that are indexed by (scaled) offsets from the start of the heap. For example, we track object reference updates with a "card mark array" that has one byte for each 512 bytes of heap. When we store a reference in the heap we have to mark the corresponding byte in the card mark array. We right shift the destination address of the store and use that to index the card mark array. Fun addressing arithmetic games you can't do in Java that you get to (have to :-) play in C++.
Usually we don't have trouble getting modest contiguous regions (up to about 1.5GB on Windohs, up to about 3.8GB on Solaris. YMMV.). On Windohs, the problem is mostly that there are some libraries that get loaded before the JVM starts up that break up the address space. Using the /3GB switch won't rebase those libraries, so they are still a problem for us.
We know how to make chunked heaps, but there would be some overhead to using them. We have more requests for faster storage management than we do for larger heaps in the 32-bit JVM. If you really want large heaps, switch to the 64-bit JVM. We still need contiguous memory, but it's much easier to get in a 64-bit address space.

So usually, JVM heap cannot be bigger than 1.2-1.6BG.

JVM memory overview

We could resume Java process heap with the following schema :

Java Heap & GC tuning

Java "Heap" is a continous memory region where all Objects data will be stored (by data, we mean instance of class, primitive and references). It's a big part of the process heap.
It can be configured using the following parameters :
  • -Xmx : max heap size (ex: -Xmx1024)
  • -Xms : min heap size. Having -Xms = 1.8GB (32bit) can be bad, because you don't let memory for anything else.
  • -Xmn : the size of the heap for the young generation
    Young generation represents all the objects which have a short life of time. Young generation objects are in a specific location into the heap, where the garbage collector will pass often. All new objects are created into the young generation region (called "eden"). When an object survive is still "alive" after more than 2-3 gc cleaning, then it will be swap has an "old generation" : they are "survivor" .
    Good size is 33%
  • -XX:NewRatio : the same as Wmn, but using a % (dynamic fs static -Xmn option). -XX:NewRatio=3 means that the ratio between the old and young generation is 1:3
  • -XX:NewSize - Size of the young generation at JVM init. Calculated automatically if you specify -XX:NewRatio
  • -XX:MaxNewSize - The largest size the young generation can grow to (unlimited if this value is not specified at command line)
  • -XX:SurvivorRatio : "old generation" called tenured generation, ratio, in %. For example, -XX:SurvivorRatio=6 sets the ratio between each survivor space and eden to be 1:6 (eden is where new objects are created)
  • -XX:MinHeapFreeRatio: default is 40%. JVM will allocate memory to always have as minimum 40% of free memory. When -Xmx = -Xms, it's useless.
  • -XX:MaxHeapFreeRatio: default is 70%. The same as Min, to avoid unecessary memory allocation.

More informationabout Sun HotSpot GC tuning here and general gc tuning here
At this point, we get a problem. In a multithread application, object can be created at the same time. Thread could try to write data into the same heap location at the same time.
To avoid this problem, we allow each thread to have a private piece into the eden space.
jdk 1.5> uses dynamic sizing algorithm, specific for each thread. (otherwise, parameter such as -XX:UseTLAB or -XX:TLABSize can be used to tune this parameter)

And EveryThing Else...

Every thing else is...
  • Permanent Space : It's the third part of the memory. Here are stored classes, methods etc.
    • -XX:PermSize: initial value
    • -XX:MaxPermSize: max value
  • Code generation : Converted byte code into native code. Shouldn't cause troubles.
  • Socket Buffer (contains the 2 buffers for each sockets: receive/send)
  • Thread Stacks: Each thread has its own stack. It makes possible to get your methods thread-safe.
    • -Xss: change the space of a thread stack. 2048 could be a write value. It can cause a java.lang.stackOverFlow Error
    • If you get a "java.lang.OutOfMemoryError : unable to create new native Thread, you can decrease -Xss or decrease the java heap using -Xmx/-Xms (to increase the thread stack space)
  • Direct memory space (ability to let Java developers map memory outside the Java Object Heap. Can be adjusted using -XX:MaxDirectMemory=
  • JNI Code, if you use it
  • Garbage Collection (the GC has its own thread/informations)

A nice gotcha...

Major collection don't run until tenured is full.
This mean that using -Xmx1024, current heap could be 750MB with 500MB of "dead" object. If the JVM is idle, it could stay like that during a very long time => wasting 500MB or RAM for an idle JVM !

More informations about GC memory & monitoring here
You should also read this awesome slideshare from Filip Hanik, Covalent (gotcha, etc... most of this informations come from this slides)