I've read an extensive amount of documentation about the HotSpot GC of Java SE 6 and 7. When talking about strategies for obtaining contiguous regions of free memory, two 'competing' approaches are presented: that of Evacuation (usually applied on the young gen), where live objects are copied from 'from' to an empty 'to' and that of Compaction (fall-back of CMS), where live object are moved to one side inside a fragmented region to form a contiguous block of used an unused memory.
Both approaches are proportional to the size of the 'live set'. The difference is that evacuation requires x2 times space than the live set, where the compaction does not.
Why do we need the Evacuation technique at all? The amount of copying that needs to be done is the same, however it requires reservation of more heap size, and it does not allow for faster remapping of references.
True: the evacuation can be executed in parallel (where-as compaction cannot, or at least not as easily) but this trait is never mentioned and seems not that important (considering that remapping is much more expensive than moving).
One big problem is that with "evacuation" the vacated space is, indeed vacant, while with "compaction" some other object Y may be moved into the space where object X was. This makes it a lot harder to correct pointers, since one can't simply use the fact that a pointer points to an invalid location to clue the code that it needs to be updated. And one can't store the "forwarding pointer" in the "invalid" location.
This makes GC much less concurrent -- the app must be in "GC freeze" for a longer period of time.
Compaction is more suitable in cases where the number of reclaimable objects is expected to be low(e.g. Tenured generation) because after a few GC cycles the long living objects tend to occupy the lower portion of the heap and hence less work is needed to be done by the collector. If in such a case a copying collector is used that would perform very poorly because almost the same surviving objects from the previous cycles will need to be copied again and again from one location to the other.
Copying is suitable when the number of reclaimable objects is very high(e.g. Young generation) since very few surviving objects needs to be copied. If in such a case compaction is used that may perform poorly because the surviving objects may be scattered across the heap.
Other than that as mentioned in #Hot Licks answer Copying collector allows us to store a forwarding pointer which prevents from running into an infinite loop in case another object from the same "From" space refers to an already moved object.
Also, Compaction can not begin until all the live objects are identified, but live objects can be copied to the new location as soon as they are identified(using multiple threads).
Related
We all know that every object allocated in Java adds a weight into future garbage collection cycles, and Optional<T> objects are no different. We use these objects frequently to wrap nullable, which leads to safer code, but at what cost?
Does anyone have information on what kind of additional GC pressure optional objects add vs. simply returning nulls and what kind of impact this has on performance in high-throughput systems?
We all know that every object allocated in Java adds a weight into future garbage collection cycles,…
That sounds like a statement nobody could deny, but let’s look at the actual work of a garbage collector, considering common implementations of modern JVMs and the impact of an allocated object on it, especially objects like Optional instances which are typically of a temporary nature.
The first task of the garbage collector is to identify objects which are still alive. The name “garbage collector” puts a focus on identifying garbage, but garbage is defined as unreachable objects and the only way to find out which objects are unreachable, is via the process of elimination. So the first task is solved by traversing and marking all reachable objects. So the costs of this process do not depend on the total amount of allocated objects, but only those, which are still reachable.
The second task is to make the memory of the garbage available to new allocations. Instead of puzzling with the memory gaps between still reachable objects, all modern garbage collectors work by evacuating a complete region, transferring all alive objects withing that memory to a new location and adapting the references to them. After the process, the memory is available to new allocations as a whole block. So this is again a process whose costs do not depend on the total amount of allocated objects, but only (a part of) the still alive objects.
Therefore, an object like a temporary Optional may impose no costs on the actual garbage collection process at all, if it is allocated and abandoned between two garbage collection cycles.
With one catch, of course. Each allocation will reduce the memory available to subsequent allocations until there’s no space left and the garbage collection has to take place. So we could say, each allocation reduces the time between two garbage collection runs by the size of the allocation space divided by the object size. Not only is this a rather tiny fraction, it also only applies to a single threaded scenario.
In implementations like the Hotspot JVM, each thread uses a thread local allocation buffer (TLAB) for new objects. Once its TLAB is full, it will fetch a new one from the allocation space (aka Eden space). If there is none available, a garbage collection will be triggered. Now it’s rather unlikely that all threads hit the end of their TLAB right at the same time. So for the other threads which still have some space in their TLAB left at this time, it would not make any difference if they had allocated some more objects still fitting in that remaining space.
The perhaps surprising conclusion is that not every allocated object has an impact on the garbage collection, i.e. a purely local object allocated by a thread not triggering the next gc, could be entirely free.
Of course, this does not apply to allocating a large amount of objects. Allocating lots of them causes the thread to allocate more TLABs and eventually trigger the garbage collection earlier than without. That’s why we have classes like IntStream allowing to process a large number of elements without allocating objects, as would happen with a Stream<Integer>, while there is no problem in providing the result as a single OptionalInt instance. As we know now, a single temporary object has only a tiny impact on the gc, if any.
This did not even touch the JVM’s optimizer, which may eliminate object allocations in hot spots, if Escape Analysis has proven that the object is purely local.
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.
I am building a Java web app, using the Play! Framework. I'm hosting it on playapps.net. I have been puzzling for a while over the provided graphs of memory consumption. Here is a sample:
The graph comes from a period of consistent but nominal activity. I did nothing to trigger the falloff in memory, so I presume this occurred because the garbage collector ran as it has almost reached its allowable memory consumption.
My questions:
Is it fair for me to assume that my application does not have a memory leak, as it appears that all the memory is correctly reclaimed by the garbage collector when it does run?
(from the title) Why is java waiting until the last possible second to run the garbage collector? I am seeing significant performance degradation as the memory consumption grows to the top fourth of the graph.
If my assertions above are correct, then how can I go about fixing this issue? The other posts I have read on SO seem opposed to calls to System.gc(), ranging from neutral ("it's only a request to run GC, so the JVM may just ignore you") to outright opposed ("code that relies on System.gc() is fundamentally broken"). Or am I off base here, and I should be looking for defects in my own code that is causing this behavior and intermittent performance loss?
UPDATE
I have opened a discussion on PlayApps.net pointing to this question and mentioning some of the points here; specifically #Affe's comment regarding the settings for a full GC being set very conservatively, and #G_H's comment about settings for the initial and max heap size.
Here's a link to the discussion, though you unfortunately need a playapps account to view it.
I will report the feedback here when I get it; thanks so much everyone for your answers, I've already learned a great deal from them!
Resolution
Playapps support, which is still great, didn't have many suggestions for me, their only thought being that if I was using the cache extensively this may be keeping objects alive longer than need be, but that isn't the case. I still learned a ton (woo hoo!), and I gave #Ryan Amos the green check as I took his suggestion of calling System.gc() every half day, which for now is working fine.
Any detailed answer is going to depend on which garbage collector you're using, but there are some things that are basically the same across all (modern, sun/oracle) GCs.
Every time you see the usage in the graph go down, that is a garbage collection. The only way heap gets freed is through garbage collection. The thing is there are two types of garbage collections, minor and full. The heap gets divided into two basic "areas." Young and tenured. (There are lots more subgroups in reality.) Anything that is taking up space in Young and is still in use when the minor GC comes along to free up some memory, is going to get 'promoted' into tenured. Once something makes the leap into tenured, it sits around indefinitely until the heap has no free space and a full garbage collection is necessary.
So one interpretation of that graph is that your young generation is fairly small (by default it can be a fairly small % of total heap on some JVMs) and you're keeping objects "alive" for comparatively very long times. (perhaps you're holding references to them in the web session?) So your objects are 'surviving' garbage collections until they get promoted into tenured space, where they stick around indefinitely until the JVM is well and good truly out of memory.
Again, that's just one common situation that fits with the data you have. Would need full details about the JVM configuration and the GC logs to really tell for sure what's going on.
Java won't run the garbage cleaner until it has to, because the garbage cleaner slows things down quite a bit and shouldn't be run that frequently. I think you would be OK to schedule a cleaning more frequently, such as every 3 hours. If an application never consumes full memory, there should be no reason to ever run the garbage cleaner, which is why Java only runs it when the memory is very high.
So basically, don't worry about what others say: do what works best. If you find performance improvements from running the garbage cleaner at 66% memory, do it.
I am noticing that the graph isn't sloping strictly upward until the drop, but has smaller local variations. Although I'm not certain, I don't think memory use would show these small drops if there was no garbage collection going on.
There are minor and major collections in Java. Minor collections occur frequently, whereas major collections are rarer and diminish performance more. Minor collections probably tend to sweep up stuff like short-lived object instances created within methods. A major collection will remove a lot more, which is what probably happened at the end of your graph.
Now, some answers that were posted while I'm typing this give good explanations regarding the differences in garbage collectors, object generations and more. But that still doesn't explain why it would take so absurdly long (nearly 24 hours) before a serious cleaning is done.
Two things of interest that can be set for a JVM at startup are the maximum allowed heap size, and the initial heap size. The maximum is a hard limit, once you reach that, further garbage collection doesn't reduce memory usage and if you need to allocate new space for objects or other data, you'll get an OutOfMemoryError. However, internally there's a soft limit as well: the current heap size. A JVM doesn't immediately gobble up the maximum amount of memory. Instead, it starts at your initial heap size and then increases the heap when it's needed. Think of it a bit as the RAM of your JVM, that can increase dynamically.
If the actual memory use of your application starts to reach the current heap size, a garbage collection will typically be instigated. This might reduce the memory use, so an increase in heap size isn't needed. But it's also possible that the application currently does need all that memory and would exceed the heap size. In that case, it is increased provided that it hasn't already reached the maximum set limit.
Now, what might be your case is that the initial heap size is set to the same value as the maximum. Suppose that would be so, then the JVM will immediately seize all that memory. It will take a very long time before the application has accumulated enough garbage to reach the heap size in memory usage. But at that moment you'll see a large collection. Starting with a small enough heap and allowing it to grow keeps the memory use limited to what's needed.
This is assuming that your graph shows heap use and not allocated heap size. If that's not the case and you are actually seeing the heap itself grow like this, something else is going on. I'll admit I'm not savvy enough regarding the internals of garbage collection and its scheduling to be absolutely certain of what's happening here, most of this is from observation of leaking applications in profilers. So if I've provided faulty info, I'll take this answer down.
As you might have noticed, this does not affect you. The garbage collection only kicks in if the JVM feels there is a need for it to run and this happens for the sake of optimization, there's no use of doing many small collections if you can make a single full collection and do a full cleanup.
The current JVM contains some really interesting algorithms and the garbage collection itself id divided into 3 different regions, you can find a lot more about this here, here's a sample:
Three types of collection algorithms
The HotSpot JVM provides three GC algorithms, each tuned for a specific type of collection within a specific generation. The copy (also known as scavenge) collection quickly cleans up short-lived objects in the new generation heap. The mark-compact algorithm employs a slower, more robust technique to collect longer-lived objects in the old generation heap. The incremental algorithm attempts to improve old generation collection by performing robust GC while minimizing pauses.
Copy/scavenge collection
Using the copy algorithm, the JVM reclaims most objects in the new generation object space (also known as eden) simply by making small scavenges -- a Java term for collecting and removing refuse. Longer-lived objects are ultimately copied, or tenured, into the old object space.
Mark-compact collection
As more objects become tenured, the old object space begins to reach maximum occupancy. The mark-compact algorithm, used to collect objects in the old object space, has different requirements than the copy collection algorithm used in the new object space.
The mark-compact algorithm first scans all objects, marking all reachable objects. It then compacts all remaining gaps of dead objects. The mark-compact algorithm occupies more time than the copy collection algorithm; however, it requires less memory and eliminates memory fragmentation.
Incremental (train) collection
The new generation copy/scavenge and the old generation mark-compact algorithms can't eliminate all JVM pauses. Such pauses are proportional to the number of live objects. To address the need for pauseless GC, the HotSpot JVM also offers incremental, or train, collection.
Incremental collection breaks up old object collection pauses into many tiny pauses even with large object areas. Instead of just a new and an old generation, this algorithm has a middle generation comprising many small spaces. There is some overhead associated with incremental collection; you might see as much as a 10-percent speed degradation.
The -Xincgc and -Xnoincgc parameters control how you use incremental collection. The next release of HotSpot JVM, version 1.4, will attempt continuous, pauseless GC that will probably be a variation of the incremental algorithm. I won't discuss incremental collection since it will soon change.
This generational garbage collector is one of the most efficient solutions we have for the problem nowadays.
I had an app that produced a graph like that and acted as you describe. I was using the CMS collector (-XX:+UseConcMarkSweepGC). Here is what was going on in my case.
I did not have enough memory configured for the application, so over time I was running into fragmentation problems in the heap. This caused GCs with greater and greater frequency, but it did not actually throw an OOME or fail out of CMS to the serial collector (which it is supposed to do in that case) because the stats it keeps only count application paused time (GC blocks the world), application concurrent time (GC runs with application threads) is ignored for those calculations. I tuned some parameters, mainly gave it a whole crap load more heap (with a very large new space), set -XX:CMSFullGCsBeforeCompaction=1, and the problem stopped occurring.
Probably you do have memory leaks that's cleared every 24 hours.
This question is limited in scope to HotSpot generations. Is there any way to programmatically find out in which generation a particular instance lives. Data such as:
Young or old generation?
If young, which survivor space?
Inside TLAB? Which thread?
Any technique (ex., BTrace, JVMTI) works so long as I can do something like this:
Object x = new Object();
HotSpotGenerationInfo info = HotSpotGenerationUtil.getInfo(x);
Beggars can't be choosers but ideally I could also learn when the instance of interest was being moved from one generation to another at the moment it happens (i.e., event callback based -- not interested in the delay & overhead implicit in polling.)
Not interested in answers that just say "no" without justification :)
As far as I know, you can not directly query which memory pool an object currently lives in. However, objects are promoted to a different memory pool by a garbage collection run, and you can query the number of major/minor gc runs since VM start using JMX. If you additionally take note of these counters when the object is created, you can reconstruct whether there was a GC since and from that which pool the object lives in.
There's an additional complication to the "count the number of GCs since the object was created" approach - it doesn't take into account premature object promotion.
If the survivor spaces are basically too small, and memory pressure from Eden (ie the rate of objects surviving at least once) is high, then objects will be promoted to tenured before they hit the full tenuring threshold.
In real examples, healthy applications will typically have non-zero percentages of premature promotion. In fact, a 0% premature promotion rate is a really bad sign - it says that your survivor spaces are much, much too big and you're wasting a lot of memory.
I understand that time taken by YGC is proportional to number of live objects in Eden. I also understand that how the live objects are figured out in Major collections (All the objects in thread stacks and static objects and further objects reachable from those objects transitively.)
But I dont understand how the live objects are figured out in young generation collection ?
if it parses the thread stacks, then it need to parse eden + tenured space which is not the case I think. So how does JVM find the live objects in eden and copies them in To Survivor space ?
how the live objects are figured out in young generation collection ?
A good high-level description of how generational collection is implemented in HotSpot may be found in this article.
In general a generational collector marks the young generation as follows (assuming we just have two generations):
It marks young objects and traces references starting with the thread stack frames and the static frames. When it finds a reference to an old generation object it ignores it.
Then it repeats the process for references in the old generation that refer to young generation objects. The tricky bit is identifying these references in the old generation without marking the entire old generation.
Now we have marked all of the objects in the new generation that are reachable ... and the rest (in that generation) can be reclaimed.
In HotSpot, old generation objects that contain young generation references are identified using the "Card Table". The old generation is divided into regions of 512 bytes, and each region has a "Card". If the region contains any old -> new generation pointers, a bit in the Card is set. Objects in regions with the Card bit set are then traced during a new generation collection.
The tricky thing is maintaining the Card table as new space references are written to old generation objects. In HotSpot, this is implemented using a software write-barrier that sets the appropriate Card's dirty bit whenever a new space reference is written into the memory region corresponding to the Card. As the linked article notes, this makes setting a reference field in an object more expensive, but it is apparently worth it due to the time saved by being able to collect only the new generation most of the time.
To trace just the youngest generation, the garbage collector scans the same root set (stacks and registers) and ALSO all the older (non-collected) generations that have been modified since the previous young generation scan. Only those objects that have been modified can possibly point at young generation objects, as unmodified objects cannot possibly point at objects that were created after their last modification.
So the tricky part is, how does the GC know which objects have been modified since the last GC? There are a number of techniques that can be used, but they basically come down to keeping track of writes to old generation objects. This can be done by trapping writes (write barriers) or just keeping track of all write targets (write buffers, card marking), all of which add overhead to the execution of the program while the GC is not running (so it doesn't show up as GC pause time, but does show up in the total elapsed time). Hardware support helps a lot, if its available. The tracking need not be exact, as long as every modified older generation object is scanned (scanning unmodified objects is a waste of time, but won't hurt anything).
I am quoting the relevant text from the article by Brian Goetz here.
Tracing garbage collectors, such as
copying, mark-sweep, and mark-compact,
all start scanning from the root set,
traversing references between objects,
until all live objects have been
visited. A generational tracing
collector starts from the root set,
but does not traverse references that
lead to objects in the older
generation, which reduces the size of
the object graph to be traced.