Reputation: 2883
I tried to see how -Xmx
and -Xms
parameters impact on my program and check how much memory does my process consume.
I wrote a simple program but am not able to reason out the results. Kindly help.
public static void main( String[] args ) {
char[] array = new char[69926904];
}
I ran with parameters -Xms5M -Xmx200M
. Ideally, since a character takes 2 bytes, it should hold 100M characters before exceeding the memory limit. Even if we say, few space is getting used for pointer and length, I don't know, why it is throwing error after 69926904 length.
Thanks.
Upvotes: 16
Views: 3178
Reputation: 2798
This information comes from Oracles page on Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine.
First it is important to know how the heap works. As you might know objects (like your array) are stored in the heap. However, the heap is not one uniform space. It is actually divided into three generations, namely the Young, the Tenured and the Permanent generation.
Garbage collection occurs in each generation when the generation fills up. Objects are first allocated in the young generation, and because of infant mortality most objects die there. Some surviving objects are moved to a tenured generation. When the tenured generation needs to be collected there is a major collection that is often much slower because it involves all live objects.
"The young generation consists of eden plus two survivor spaces . Objects are initially allocated in eden. One survivor space is empty at any time, and serves as a destination of the next, copying collection of any live objects in eden and the other survivor space. Objects are copied between survivor spaces in this way until they are old enough to be tenured, or copied to the tenured generation."
"A third generation closely related to the tenured generation is the permanent generation. The permanent generation is special because it holds data needed by the virtual machine to describe objects that do not have an equivalence at the Java language level. For example objects describing classes and methods are stored in the permanent generation."
In addition to the -Xms
and the -Xmx
parameters that control the total heap size, there are parameters that control the size of the individual segments. In your example, you were setting the max total heap size but you were still using the default generation ratio's.
"By default, the young generation size is controlled by NewRatio. For example, setting -XX:NewRatio=3 means that the ratio between the young and tenured generation is 1:3. In other words, the combined size of the eden and survivor spaces will be one fourth of the total heap size."
"In an ideal minor collection the live objects are copied from one part of the young generation (the eden space plus the first survivor space) to another part of the young generation (the second survivor space). However, there is no guarantee that all the live objects will fit into the second survivor space. To ensure that the minor collection can complete even if all the objects are live, enough free memory must be reserved in the tenured generation to accommodate all the live objects. In the worst case, this reserved memory is equal to the size of eden plus the objects in non-empty survivor space. When there isn't enough memory available in the tenured generation for this worst case, a major collection will occur instead.
What you're trying to do is store away one huge massive object. This object has to fit in one of the generations, and there is simply no generation big enough. Even if the total size of the heap is bigger than the object, that doesn't help because objects cannot span across multiple generations.
Try adding the following parameter when running your code: -XX:NewRatio=10
. As described above, this will make the tenured generation ten times as big as the young generation. As a result, a much bigger object will be able to fit inside the tenured generation.
Another solution is to simply refrain from using objects that massive. Instead of having one massive array, have several smaller ones.
Upvotes: 11
Reputation: 98284
Read carefully JVM Garbage Collection Tuning Guide about Generational Heap, and it will hopefully answer your question.
Run Java with -XX:+PrintGCDetails
option, and everything should become clear:
Heap
PSYoungGen total 3584K, used 294K [0x00000000fbd60000, 0x00000000fc160000, 0x0000000100000000)
eden space 3072K, 9% used [0x00000000fbd60000,0x00000000fbda9860,0x00000000fc060000)
from space 512K, 0% used [0x00000000fc0e0000,0x00000000fc0e0000,0x00000000fc160000)
to space 512K, 0% used [0x00000000fc060000,0x00000000fc060000,0x00000000fc0e0000)
PSOldGen total 136576K, used 136576K [0x00000000f3800000, 0x00000000fbd60000, 0x00000000fbd60000)
object space 136576K, 100% used [0x00000000f3800000,0x00000000fbd60000,0x00000000fbd60000)
PSPermGen total 21248K, used 2595K [0x00000000ee600000, 0x00000000efac0000, 0x00000000f3800000)
object space 21248K, 12% used [0x00000000ee600000,0x00000000ee888db0,0x00000000efac0000)
Your 200M Java Heap consists of 2 generations: 1/3 (66.7M) is YoungGen and 2/3 (133.3M) is OldGen.
-XX:NewRatio
option allows to change the proportion, but the default value of 2 means that YoungGen will reserve 1/(2+1) part of the heap.
Java objects cannot span generations, so the maximum size of object cannot be larger than the largest generation. In your case the largest generation is OldGen: 136576K = 139853824 which is exactly the size of char[69926904]
(16 bytes header + 2 * 69926904 bytes of data).
Upvotes: 28