Java Heap Behaviour - java

Given a java memory configuration like the following
-Xmx2048m -Xms512m
What would be the behaviour of the VM when memory usage increases past 512m? Is there a particular algorithm that it follows? ie. Does it go straight to the max, does it double, does it go in increments, or does it only allocate as it needs memory? How expensive an operation is it?
I'm looking specifically at the Oracle/Sun JVM, version 1.6. I assume this is documented on the Oracle website somewhere, but I'm having trouble finding it.

It's the garbage collector's job to decide when resizing is necessary, so it is determined by the GC parameter 'MinFreeHeapRatio.' If the GC needs more space, it will grow to a size where the % of heap specified by that value is available.
A typical value on a modern platform is 40ish, so if you start at 512MB and have less than 40% free, meaning you exceeded ~308MB, it will increase until 40% is free again. So say after collection there are still 400MB worth of live objects, your heap will go up to ~667MB. (Yes it is named ratio but expects a % value as argument... search me!)
Note this is a bit inexact, the garbage collector is "generational" and actually can resize individual generations, but it also has forced ratios between generations sizes and if your objects are distributed between long lived and short lived in roughly the way it estimates, it works out pretty well for back of the envelope.
This applies to defaults in Java 6. If you use custom garbage collector config it may be different. You can read about that here: http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#generation_sizing.total_heap
(The "expense" of the operation kind of depends on the operating system and what else is going on. If the system is loaded down and the OS has to do some swapping to make a contiguous block of memory for you, then it could be very expensive!)

Use of -verbose:gc and/or -XX:+PrintGCDetails options should give you many finer details.
Here is an example output with the -verbose:gc option switched on:
[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]
An explanation of the above taken from the official document:
Here we see two minor collections followed by one major collection.
The numbers before and after the arrow (e.g., 325407K->83000K from the
first line) indicate the combined size of live objects before and
after garbage collection, respectively. After minor collections the
size includes some objects that are garbage (no longer alive) but that
cannot be reclaimed. These objects are either contained in the tenured
generation, or referenced from the tenured or permanent generations.
The next number in parentheses (e.g., (776768K) again from the first
line) is the committed size of the heap: the amount of space usable
for java objects without requesting more memory from the operating
system. Note that this number does not include one of the survivor
spaces, since only one can be used at any given time, and also does
not include the permanent generation, which holds metadata used by the
virtual machine.
The last item on the line (e.g., 0.2300771 secs) indicates the time
taken to perform the collection; in this case approximately a quarter
of a second.
The format for the major collection in the third line is similar.
Running an application this way along with updating minimum and maximum heap sizes can give good insight into heap allocation and garbage collection patterns of the VM.

its to be noted that JVM on Initialization virtually reserves maximum address space but does not allocate physical memory. Typically the JVM will allocate space into Old and Young generation. There are intermidatory space in the Young Generation . The new objects invoked are contained in the Young Generation.
When the intermidatory space is filled up , GC is invoked which moves reference Objects to one of the Intermidatory space called Survivor Space in the Young Generation Segment. The GC might follow "stop-the-world" by saving thread's state algorithm or an alogorithm so the processes keep running(?).
When the Survivor space fills JVM invokes a full GC.

Related

-XX:G1ReservePercent and to-space exhausted

I'm trying to understand what the -XX:G1ReservePercent actually does. The descriptions I found in the official documentation are not really comprehensive:
Sets the percentage of reserve memory to keep free so as to reduce the
risk of to-space overflows. The default is 10 percent. When you
increase or decrease the percentage, make sure to adjust the total
Java heap by the same amount.
and the desciption of to-space exhausted log entry is this:
When you see to-space overflow/exhausted messages in your logs, the G1
GC does not have enough memory for either survivor or promoted
objects, or for both.
[...]
To alleviate the problem, try the following adjustments:
Increase the value of the -XX:G1ReservePercent option (and the total
heap accordingly) to increase the amount of reserve memory for "to-space".
[...]
Judging by the quote the to-space exhausted means that when performing mixed evacuation we do not have enough a free region to move survivors to.
But this then contradicts to the following official tuning advice in case of Full GC (emphasize mine):
Force G1 to start marking earlier. G1 automatically determines the
Initiating Heap Occupancy Percent (IHOP) threshold based on earlier
application behavior. If the application behavior changes, these
predictions might be wrong. There are two options: Lower the target
occupancy for when to start space-reclamation by increasing the buffer
used in an adaptive IHOP calculation by modifying
-XX:G1ReservePercent;
So what is the buffer and what does setting -XX:G1ReservePercent do (From the first glance AdaptiveIHOP has nothing to do with it...) ?
Does it keep some heap space always reserved so when mixed evacuation occur we always have free regiong to move survivors to?
Or the space is used for G1 internal housekeeping tasks? If so it is not clear what data the to-space contains so it exhausted?
To me, understanding what it really does, means to go the source code. I chose jdk-15.
The best description of this flag is here:
It determines the minimum reserve we should have in the heap to minimize the probability of promotion failure.
Excellent, so this has to do with "promotion failures" (whatever those are). But according to you quote in bold, this has something to do with AdaptiveIHOP also? Well yes, this parameter matters only in AdaptiveIHOP (which is on by default).
Another thing to notice is that G1ReservePercentage has no guarantee to be maintained all the time, it is a best effort.
If you look at how it is used in the very next line:
if (_free_regions_at_end_of_collection > _reserve_regions) {
absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions;
}
things start to make some sense (for that bold statement). Notice how _reserve_regions are extracted for some computation. G1 will reserve that space for "promotion failures" (we will get to that).
If G1 reserves that space, it means less space is available for actual allocations. So if you "increase this buffer" (you make G1ReservePercent bigger as the quote suggests), your space for new objects becomes smaller and as such the time when GC needs to kick in will come sooner, so the time for when space reclamation needs to happen will come sooner too ("Lower the target occupancy for when to start space-reclamation..."). It is one complicated sentence, but in simple words it means that :
If you increase G1ReservePercentage, space will be needed to be reclaimed faster (more often GC calls).
Which, to be fair, is obvious. Not that I agree that you should increase that value, but this is what that sentence says.
After a certain GC cycle, be that minor, mixed or major, G1 knows how many regions are free:
_free_regions_at_end_of_collection = _g1h->num_free_regions();
Which of course, is the length of "free list":
return _free_list.length();
Based on this value and _reserve_regions (G1ReservePercent) plus the target pause time (200 ms by default) it can compute how many regions it needs for the next cycle. By the time next cycle ends, there might be a case when there are no empty regions (or the ones that are empty can not take all the live Objects that are supposed to be moved). Where is Eden supposed to move live Objects (Survivor), or, if old region is fragmented - where are live objects supposed to be moved to defragmente? This is what this buffer is for.
It acts as a safety-net (the comments in the code make this far more easier to understand). This is needed in the hopes that it will avoid a Full GC. Because if there are no free regions (or enough) to move live Objects a Full GC needs to happen (most probably followed by a young GC also).
This value is usually known to be small when this message is present in logs. Either increase it, or much better give more heap to the application.

Understanding GC: Allocation failure and filled OldGen with temporary String objects

I have followed up with a couple of good questions and their answers but I still have a doubt.
This is what I understand and would like to see if the understanding is correct.
GC (Allocation Failure) kicks in whenever new memory is to be allocated on YoungGen.
Also, the fact that depending on the size of the object, some objects might have to be pushed to OldGen and significantly larger objects could directly be moved to OldGen.
Application Behavior: The reason for 'Allocation Failure' was the creation of huge strings. On debugging further with JFR and HeapDump, everything points to a lot of char[] and String objects which are created in our system on a temporary basis (i.e. YoungGen candidate). Some of these strings indeed are huge (~25KB each). Although, there was enough space available in the YoungGen as per the error message and Heap is not even close to maximum memory possible.
During the same time, OldGen was increasing and was not getting cleaned even after full GC. There could be another memory leak but there is nothing that points to that. So, I don't understand why OldGen remains at the same level even after the full GC.
Apart from the validation of my understanding, the question is: Can the creation of a lot of temporary String/char[] objects (via strA + strB, new String()/StringBuilder().toString(), String.split(), String.substring(), Stream->buffer conversion etc.) cause GC to run very frequently even when the application has a lot of memory available in the YoungGen and heap in general? If yes, when and what are the alternatives?
Thanks!
I would say that the answer is a conditional yes.
Remember that young gen is split into 3 parts, eden, S0 and S1 which means that you do not have as much memory in young gen as you might think. If you overflow one of the survivor spaces, the remainder will be pushed to old gen (premature promotion), filling up old gen. Note also that promotion from young gen to old is based on the number of gc cycles. If you have frequent young gen gc where objects supposed to be short-lived are moved to old gen (because you have not finished with the temp objects), then you will fill up old gen. Note also that that just because you do a full gc, there are no guarantees that you will actually get any memory back.
So, use a tool like censum to analyse your gc logs and look especially for premature promotion.
It might be that you will have to resize your young gen/old gen ratio.

Automatically promoting an object to Old Generation

An object can be promoted from Young Generation to Old Generation when it reaches the Tenuring Threshold or when the "TO" Survival Space is full when it is being transferred.
Therefore, my question is: In order to improve performance, if I know my object will be frequently used (referenced), is it possible to automatically/manually declare an object in Old/Permanent Generation so that not declaring it in the Eden would delay the necessity of Minor Garbage Collection, thus delaying the "Stop The World" event and improving the applications performance?
Generally:
No - not for a specific single object.
In more detail:
An allocation rougly looks the following:
Use thread local allocation buffer (TLAB), if tlab_top + size <= tlab_end. This is the fastest path. Allocation is just the tlab_top pointer increment.
If TLAB is almost full, create a new TLAB in the Eden space and retry in a fresh TLAB.
If TLAB remaining space is not enough but is still to big to discard, try to allocate an object directly in the Eden space. Allocation in the Eden space needs to be done using an atomic operation, since Eden is shared between all threads.
If allocation in the Eden space fails (eden_top + size > eden_end), typically a minor collection occurs.
If there is not enough space in the Eden space even after a Young GC, an attempt to allocate directly in the old generation is made.
"Hack":
The following parameter:
XX:PretenureSizeThreshold=size
This parameter is default set to 0, so it is deactivated. If set, it defines a size threshold for objects to be automatically allocated into the old generation.
BUT:
You should use this with care: Setting this parameter wrong may change your GCs behaviour drastically. And: Only a few percent of objects survive their first GC, so most objects don't have to be copied during the young GC.
Therefore, the young GC is very fast and you should not really need to "optimize" it by forcing object allocation to old generation.
Java parameters:
If you want to get an overview over possible Java paramters, run the following:
java -XX:+PrintVMOptions -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version
This will print all flags you can set.
Different garbage collectors:
Also keep in mind that there are different garbage collectors out there, and that it is planned that Java 9 should use the garbage first (G1) GC as default garbage collector, which again may handle big objects differently (by allocating them into humangous regions).
Additional source:
Stack overflow question: Size of Huge Objects directly allocated to Old Generation
You cannot create an object directly in old generation, it has to go through the eden space and survivor spaces (the young generation) before reaching the old generation. However, if you know that your objects are long lived (For example if your have implemented something like a cache) you can set the following JVM parameters:
-XX:InitialTenuringThreshold=7: Sets the initial tenuring threshold to 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.
Source: http://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html
So, you can reduce the application's tenuring threshold.
I actually did this and the stop the world GC time reduced for minor GC ( I had a huge 250GB JVM so the effect was quite profound
).

What is the normal behavior of Java GC and Java Heap Space usage?

I am unsure whether there is a generic answer for this, but I was wondering what the normal Java GC pattern and java heap space usage looks like. I am testing my Java 1.6 application using JMeter. I am collecting JMX GC logs and plotting them with JMeter JMX GC and Memory plugin extension. The GC pattern looks quite stable with most GC operations being 30-40ms, occasional 90ms. The memory consumption goes in a saw-tooth pattern. The JHS usage grows constantly upwards e.g. to 3GB and every 40 minutes the memory usage does a free-fall drop down to around 1GB. The max-min delta however grows, so the sawtooth height constantly grows. Does it do a full GC every 40mins?
Most of your descriptions in general, are how the GC works. However, none of your specific observations, especially numbers, hold for general case.
To start with, each JVM has one or several GC implementations and you could choose which one to use. Take the mostly applied one i.e. SUN JVM (I like to call it this way) and the common server GC pattern as example.
Firstly, the memory are divided into 4 regions.
A young generation which holds all of the recently created objects. When this generation is full, GC does a stop-the-world collection by stopping your program from working, execute a black-gray-white algorithm and get the obselete objects and remove them. So this is your 30-40 ms.
If an object survived a certain rounds of GC in the young gen, it would be moved into a swap generation. The swap generation holds the objects until another number of GCs - then move them to the old generation. There are 2 swap generations which does a double buffering kind of thing to facilitate the young gen to work faster. If young gen dumps stuff to swap gen and found swap gen is mostly full, a GC would happen on swap gen and potentially move the survived objects to old gen. This most likely makes your 90ms, though I am not 100% sure how swap gen works. Someone correct me if I am wrong.
All the objects survived swap gen would be moved to the old generation. The old generation would only be GC-ed until it's mostly full. In your case, every 40 min.
There is another "permanent gen" which is used to load your jar target byte code and resources.
All size of the areas can be adjusted by JVM parameters.
You can try to use VisualVM which would give you a dynamic idea of how it works.
P.S. not all JVM / GC works the same way. If you use G1 collector, or JRocket, it might happens slightly different, but the general idea holds.
Java GC work in terms of generations of objects. There are young, tenure and permament generations. It seems like in your case: every 30-40ms GC process only young generation (and transfers survived objects into tenure generation). And every 40 mins it performs full collecting (it causes stop-the-world pause). Note: it happens not by time, but by percentage of used memory.
There are several JVM options, which allows you to chose generation's sizes, type of GC (there are several algorithms for GC, in java 1.6 Serial GC is used by default, for example -XX:-UseConcMarkSweepGC), parameters of GC work.
You'd better try to find good articles about generations and different types of GC (algorithms are really different, some of them allow to avoid stop-the-world pauses at all!)
yes, most likely. Instead of guessing you can use jstat to monitor your GCs.
I suggest you use a memory profiler to ensure there is nothing simple you can do ti improve the amount of garbage you are producing.
BTW, If you increase the size of the young generation, you can reduce how much garbage makes it into the tenured space reducing the frequency of full collections. You may find you less than one full collection per day if you tune it enough.
For a more extreme case, I have tuned a trading system to less than one collection per day (minor or major)

MaxTenuringThreshold - how exactly it works?

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.

Categories

Resources