I am trying to understand how Java GC works. Let assume that due to an allocation request, the Eden space is full. Minor GC happens, collecting all Eden and Survival1 objects into Survival2. But there are more objects than space in Survival2. As far as I understand, when this happens, the spillover is moved to the Tenured space (prematurly?—as in, before the predefined number of GC iterations that an object is supposed to stay in the Young space). Does such an event also trigger a GC for the Tenured space if the Tenured space has enough space for the spillover?
I'm not considering the G1 garbage collection here.
Your assumption about how the survivor spaces work is correct. If the 'to' survivor space does not have enough space for objects being copied from the 'from' space plus those that have been collected from Eden, objects will be prematurely promoted (before the tenuring threshold has been reached) to the old generation.
The old gen. is separate so a GC will only be triggered if the objects being promoted from the Young gen. cause it. With algorithms like CMS and G1, they use values like the occupancy fraction to decide when to initiate GC. GC of the old gen. may be triggered if there is still enough space for the promotion but it's not a certainty.
Related
I was told not to create too many long-lived objects to get a better gc performance. Because long-lived objects will be moved to old gen. And it's more expensive to collect objects in old gen.
But what does long-lived mean? Is 100 milliseconds too long? And what about 10 seconds?
My app takes 15G heap memory and uses G1 gc, I can't find some configuration like 'how long to move an object to old gen'
The tenuring threshold is the number of times an object can survive a young gen collection before being promoted to the old gen. This can be configured with these options:
-XX:InitialTenuringThreshold: The initial tenuring threshold (default is 7).
-XX:MaxTenuringThreshold: Maximum tenuring threshold (default is 15 for the parallel collector and 4 for CMS).
-XX:+PrintTenuringDistribution: Print tenuring age information.
Long-lived objects are objects that survive enough minor collections and are 'moved' to old generation.
At first GC, live objects are moved to survivor space from new generation.
Then, after a number of gcs, they are tenured to old space.
As shown here, survivor doc, the number of gc objects survive before being tenured depend on the survivor space size.
At each GC, the JVM determines the number of times an object can be copied before it is tenured, called the tenure threshold. This threshold is chosen to keep the survivor space half full.
If you want to have more control, you can use the JVM param XX:MaxTenuringThreshold
After many cycles of GC, Objects that are survived in young generation are moved to the Old generation memory space.
Please clarify, Minor GC is responsible for this ? or Major GC?
Please clarify, Minor GC is responsible for this ? or Major GC?
Either of them is responsible for object to move from young gen to old gen.
Have a look at "General Garbage Collection Process" section # oracle garbage collection tutorial
Summary:
First, any new objects are allocated to the eden space. Both survivor spaces start out empty.
When the eden space fills up, a minor garbage collection is triggered
Referenced objects are moved to the first survivor space. Unreferenced objects are deleted when the eden space is cleared.
At the next minor GC, the same thing happens for the eden space. Unreferenced objects are deleted and referenced objects are moved to a survivor space. However, in this case, they are moved to the second survivor space (S1)
At the next minor GC, the same process repeats. However this time the survivor spaces switch. Referenced objects are moved to S0. Surviving objects are aged. Eden and S1 are cleared.
After a minor GC, when aged objects reach a certain age threshold (8 in this example) they are promoted from young generation to old generation.
As minor GCs continue to occur, objects will continue to be promoted to the old generation space.
Eventually, a major GC will be performed on the old generation which cleans up and compacts that space.
Objects can be moved from young to tenured space in either a Minor GC (young space) or Full GC (everything). A Major GC collection only collects in the tenured space.
Smaller objects are created in the Eden space, Large obejcts e.g. arrays in the tenured space.
When the Eden space is cleared out, surviving objects are copied to the survivors spaces. They are copied back and forth between the two survivors spaces until their age (number of times copied) reaches the tenuring threshold and in which case it is copied to the tenured space.
If there is too many objects in the Eden space to be copied to the survivor spaces, a full GC is triggered and all live object go straight to the tenured space.
Considering the size (survivorRatio) survivor size will always be greater than eden
The survivor ratio is how much smaller the survivor space is than the Eden space. e.g. -XX:SurvivorRatio=8 means the survivors space is 1/10th of young generation. There is two survivors spaces (1/10th each) and the Eden space is 8 times larger (8/10ths)
Even if the survivors space was larger than the Eden, it's the amount of free space in the survivor which matters. You could have a survivor space which is 90% full for example (as it still has objects from the last N collections)
So will there be ever a scenario to have that objects directly gets copied from eden to Old if objects are smaller in size (not humongous) ?
If you make the survivor spaces small enough to trigger a full collection each time, the objects will go from Eden to Tenured. I don't recommend going this.
Heap memory is divided into Young Gen, Old Gen and PermGem.
In young gen, one eden space and two survivor spaces are allocated. According to GC in our machines, one survivor space should always be available so that the next live nodes references can be stored there when GC marks the already present references (scans) in the young gen (eden space + 1 survivor space) and upgrade them to old gen.
First question is, is this understanding correct?
If yes, at all the times some portion of Young Gen will be vacant/available in the form of 1 survivor space. So how to see in GC Logs that the young gen is full (i.e. GC is triggered) or that is misleading? which means only usable young gen memory is shown to us in GC logs.
How to analyse the Heap memory is full i.e. OutOfMemory when some portion of Young Gen will always be having some vacant space, hence the all around heap memory.
Thanks in advance.
There are 2 kinds of GC collections
Minor GC - this occurs when the young generation fills up
Full GC - this occurs when the tenured or the old generation fills up.
An OutOfMemory occurs when there is no space left on the heap to move objects into the old generation.
You should read up more on Java GC process. You can start with - http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
To read and analyze the GC logs you can refer to How to read a verbose:GC output?
Assuming you're using hotspot then logging with -XX:+PrintGCDetails -XX:+PrintHeapAtGC should be verbose enough to cover all the things you're interested on.
What is the criteria to put a young object in an old region making it an old object or keep it in survivor regions?
The point 4 of Young GC of the official tutorial state:
"Live objects are evacuated (i.e., copied or moved) to one or more
survivor regions. If the aging threshold is met, some of the objects
are promoted to old generation regions."
But I can't find what that criteria is.
EDIT:
Amit Bhati pointed me to the MaxTenuringThreshold parameter. I don't understand much from the official doc about it but I think I started to understand how it works.
With your help I think I found the answer here:
-XX:InitialTenuringThreshold=7 Sets the initial tenuring threshold for use in adaptive GC sizing in the parallel young collector. The
tenuring threshold is the number of times an object survives a young
collection before being promoted to the old, or tenured, generation.
-XX:MaxTenuringThreshold=n Sets the maximum tenuring threshold for use in adaptive GC sizing. The current largest value is 15. The default
value is 15 for the parallel collector and is 4 for CMS.
It is under the Debugging Options title :)
Under Garbage First (G1) Garbage Collection Options you can find this:
-XX:MaxTenuringThreshold=n Maximum value for tenuring threshold. The default value is 15.
It is not very descriptive if you have not read InitialTenuringThreshold description on the other section. It seems InitialTenuringThreshold is not a valid G1 option but I think the algorithm is the described there.
The following doc is good at explaining how to alter (reduce) the rate at which items are promoted from the survivor spaces to the Old Gen in the G1 collector.
http://java-is-the-new-c.blogspot.co.uk/2013/07/tuning-and-benchmarking-java-7s-garbage.html (the section entitled Tuning The Young Generation)
As the above answers say, the MaxTenuringThreshold is the key setting, but this is only an upper limit, and would be ignored if your YoungGen wasn't big enough to allow this to be honoured. In which case you'd need to increase either the overall YoungGen via NewRatio or just the SurvivorSpace via SurvivorRatio
From the Javadocs:
The heap space is divided into the old and the new generation. The new
generation includes the new object space (eden), and two survivor
spaces. The JVM allocates new objects in the eden space, and moves
longer lived objects from the new generation to the old generation.
The young generation uses a fast copying garbage collector which
employs two semi-spaces (survivor spaces) in the eden, copying
surviving objects from one survivor space to the second. Objects that
survive multiple young space collections are tenured, meaning they are
copied to the tenured generation. The tenured generation is larger and
fills up less quickly. So, it is garbage collected less frequently;
and each collection takes longer than a young space only collection.
Collecting the tenured space is also referred to as doing a full
generation collection.
The frequent young space collections are quick (a few milliseconds),
while the full generation collection takes a longer (tens of
milliseconds to a few seconds, depending upon the heap size).
Other GC algorithms, such as the Concurrent Mark Sweep (CMS)
algorithm, are incremental. They divide the full GC into several
incremental pieces. This provides a high probability of small pauses.
This process comes with an overhead and is not required for enterprise
web applications.
Also check this article: Java Garbage Collectors – Moving to Java7 Garbage-First (G1) Collector
The young generation comprises of one Eden and two Survivor spaces.
The live objects in Eden are copied to the initially empty survivor
space, labeled S1 in the figure, except for ones that are too large to
fit comfortably in the S1 space. Such objects are directly copied to
the old generation. The live objects in the occupied survivor space
(labeled S0) that are still relatively young are also copied to the
other survivor space, while objects that are relatively old are copied
to the old generation. If the S1 space becomes full, the live objects
from Eden or S0 that have not been copied to it are tenured,
regardless of their age. Any objects remaining in Eden or the S0 space
after live objects have been copied are not live and need not be
examined. Figure below illustrates the heap after young generation
collection:
The young generation collection leads to stop the world pause. After
collection, eden and one survivor space are empty. Now let’s see how
CMS handles old generation collection. It essentially consists of two
major steps – marking all live objects and sweeping them.
We know that there's few main memory domains: Young, Tenured (Old gen) and PermGen.
Young domain is divided into Eden and Survivor (with two).
OldGen is for surviving objects.
MaxTenuringThreshold keeps objects from being finally copied to the OldGen space too early. It's pretty clear and understandable.
But how does it work? How is the garbage collector dealing with these objects which are still surviving till MaxTenuringThreshold and in what way? Where are they located?
Objects are being copied back to Survivor spaces for garbage collection.. or does it happen somehow else?
Each object in Java heap has a header which is used by Garbage Collection (GC) algorithm. The young space collector (which is responsible for object promotion) uses a few bit(s) from this header to track the number of collections object that have survived (32-bit JVM use 4 bits for this, 64-bit probably some more).
During young space collection, every single object is copied. The Object may be copied to one of survival spaces (one which is empty before young GC) or to the old space. For each object being copied, GC algorithm increases it's age (number of collection survived) and if the age is above the current tenuring threshold it would be copied (promoted) to old space. The Object could also be copied to the old space directly if the survival space gets full (overflow).
The journey of Object has the following pattern:
allocated in eden
copied from eden to survival space due to young GC
copied from survival to (other) survival space due to young GC (this could happen few times)
promoted from survival (or possible eden) to old space due to young GC (or full GC)
the actual tenuring threshold is dynamically adjusted by JVM, but MaxTenuringThreshold sets an upper limit on it.
If you set MaxTenuringThreshold=0, all objects will be promoted immediately.
I have few articles about java garbage collection, there you can find more details.
(Disclaimer: This covers HotSpot VM only)
As Alexey states, the actually used tenuring threshold is determined by the JVM dynamically. There is very little value in setting it. For most applications the default value of 15 will be high enough, as usually way more object survive the collection.
When many objects survive the collection, the survivor spaces overflow directly to old. This is called premature promotion and an indicator of a problem. However it seldom can be solved by tuning MaxTenuringThreshold.
In those cases sometimes SurvivorRatio might be used to increase the space in the survivor spaces, allowing the tenuring to actually work.
However, most often enlarging the young generation is the only good choice (from configuration perspective).
If you are looking from coding perspective, you should avoid excess object allocation to let tenuring work as designed.
To answer exactly what you asked:
When an object reaches its JVM determinded tenuring threshold, it is copied to old. Before that, it will be copied to the empty survivor space. Objects that have been surviving a while but are de-referenced before reaching the threshold are cleaned from survivor very efficiently.