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

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.

Related

some questions on Garbage Collection internals?

I am trying to understand how Garbage collection process works. Came across good link .
Most of the articles says that during minor GC collection object is moved from eden to survivor space and during major GC collection
object is moved from survivor to tenured space otherwise all unreachable objects memory is reclaimed. I have three questions(need to ask
in single go as they are related) based on above statements :-
1)Minor vs Major GC collection ? What is the difference between two that one is called major and other is called minor collection?
As per my understanding during minor collection happens in parallel to application run while major collection makes application to
pause during that period.
2) What actually happens when object is moved from eden to survivor space ? Does the memory location of object is changed internally?
3) Why not just one space exist instead of three i.e eden, survivor and tenured space exist ? I know there is must be a reason behind it but i am missing it.
My point is when GC runs , collect unreachable object and leaves the reachable ones in that space only. Just one space seems to be sufficient. So what advantage three different
spaces are proving over one?
1) Minor GC occurs on new generation, major GC occurs on old generation. Whether it is parallel to the application or not depends on the kind of GC, only CMS and G1 can work concurrently
2) Yes, moving object during GC changes its physical location so all pointers to this object will be updated
3) This is to avoid often and long application freezing during GC. If it was one big heap then application would often freeze for long periods of time. JVM creates objects in small young generation, GCs in it occur frequently but quickly. Most objects created by JVM die quickly and they never get to old generation, so major GC happens rarily or it may never happen at all.
Source for my answers is this Oracle article on GC basics, so these answers would apply for HotSpot. No clue as to other VMs, although I would guess that the general idea might remain the same if the same implementation techniques were used in other VMs.
Minor vs Major GC collection? What is the difference between two that one is called major and other is called minor collection?
Minor GC is GC of the young generation, where new objects are allocated. Major GC is GC of all live objects, including the permanent generation (which is a bit interesting to me, but that's what the article says). Also, it appears that both major and minor GC are stop-the-world events.
What actually happens when object is moved from eden to survivor space? Does the memory location of object is changed internally?
I can't seem to find a reference at the moment, but I would assume so. Allowing for memory location to be changed lets compaction be performed, which improves memory allocation performance and ease. Allowing each space to be compacted separately makes sense, so I would guess that moving an object from one part of the heap to another would involve physically moving the object from one memory location to another.
Why not just one space exist instead of three (i.e eden, survivor and tenured space) exist?
Short answer: efficiency. If you have only one space, you'd have to check all objects when you GC, which becomes inefficient if you have lots of long-lived objects (and you're almost guaranteed to have a decent number in a long-running application), as those long-lived objects are likely to still be reachable from one GC to the next. Splitting the heap allows for GC to be optimized, as most of the GC efforts can be concentrated where object life can be assumed to be short (i.e. young generation), with longer-living objects being GC'd less frequently.

How generation help garbage collector?

I've read few articles about how garbage collection works and still don't understand how using generations helps? As I understood the main idea is that we start collection from the youngest generation and move to older generations. But why the authors of this idea decided that starting from the youngest generation is the most efficient way?
The older the generation, means object has been used quite a many times, and possibly will need again.
Removing recently created object makes no sense, May be its temporary(scope : local) object.
The authors start with the youngest generation first simply because that's what gets filled up first after your application starts, however in reality which generation is being swept and when is non-deterministic as your application runs.
The important points with generational GC are:
the young generation uses a copying collector which is copying objects to a space that it considers to be empty (the unused survivor spaces) from eden and the current survivor space and is therefore fast and the GC pause is minimal.
add to this fact that most objects die young and therefore the pause required to copy a small number of surviving objects from the eden and the current surviver space is small as only objects with live references are copied, after which eden and the previous survivor space can be wiped.
after being copied several times objects are copied to the tenured (old) generation; Eventually the tenured generation will fill up, however, this time there's not a clean space to copy the objects to, so the garbage collector has to sweap and compact within the generation, which is slow (when compared to the copy performed in eden and the survivor space) meaning a longer pause.
the good news, based on the most objects die young heuristic is, major GCs happen much less frequently than minor keeping GC pauses to a minimum over the lifetime of an application.
there's also a benefit that all new objects are allocated on the top of the heap, meaning there's mininal instructions required to do so, with defragmentation occurring naturally as part of the copy process.
Both these pages, Oracle Garbage Collection Tuning and Useful JVM Flags – Part 5 (Young Generation Garbage Collection), describe this.
Read this one.
Using different generations, makes the allocation of objects easy and fast as MOST of the allocations are done in a single region of Heap - Eden. Based on the observation that most objects die young from Weak Generational Hypothesis, collections in Young generation have more garbage which will reclaim more memory and its relatively small compared to the heap which means that time taken to scan the objects is also less. Thats why Young generation GCs are fast.
For more details on GC and generations, you can refer to this

Heap,Non heap and stack ..intricacies of garbage collection

I have been going through the garbage collection in java(jdk 6 hot spot JVM).I have few questions which I hope the community will help me to resolve.
What I understand:
1)Heap is divided into
a)Young generation -Eden and Survivor :New objects and arrays are
created into the young generation.Minor garbage collection will
operate in the young generation. Objects, that are still alive, will
be moved from the eden space to the survivor space.
b)Old generation/Tenured Generation:Major collection will move the still alive objects from young generation to old generation.
2)Non Heap is divided into
a)Code Cache
b)Perm generation.
What I want to know:
1)what if survivor gets full..how will minor garbage collection work.
2)When and how is the perm generation garbage collected.
3)Also what happens to the stack..where is it stored or residing?How is its size controlled?
When the survivor space is full, objects are moved into the old generation. Although, technically, most of the time when an object gets moved from the survivor space into the old generation, it’s not because the survivor space is full, but because the object has survived a certain number of minor collections, usually 10–15.
Very rarely. It’s mostly the binary code for Java classes, so space can only be freed up if a bunch of classes are unloaded from memory. Most programs use the same set of classes throughout the life of the program, so collecting the permanent generation is generally a waste of time. Basically Java will only do a collection here if it’s about to run out of memory.
The stack is something outside the heap, and its size is controlled by the fact that objects are only stored on the stack if they are guaranteed to have a limited lifetime. These are mostly local variables. Suppose you have a local StringBuilder variable that you use to build up the return value of a method. You never pass it outside your own method, and you call stringBuilder().toString() to create a new object at the end of the method. Since Java can tell that the StringBuilder object won’t outlive the running of the method, it can put it on the stack and deallocate it immediately when the method returns instead of passing it off to the garbage collector.
The stack size is controlled by being fixed at the point that it is created. If you ever try to use more space than is available on the stack, you will get a "stack overflow" exception.
A stack is the part of the memory. The local automatic variable is created on this stack and method arguments are passed. When a process starts, it get a default stack size which is fixed for each process. In today's operating system, generally, the default stack size is 1 Mb, which is enough for most of the process. Under abnormal condition, the stack limit exceeds. This is known as stack overflow.

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