Understanding the CMS Collector
CMS has three basic operations:
- CMS collects the young generation (stopping all application threads).
- CMS runs a concurrent cycle to clean data out of the old generation.
- If necessary, CMS performs a full GC.
concurrent cycle
- The JVM starts a concurrent cycle based on the occupancy of the heap. When it is
sufficiently full, the JVM starts background threads that cycle through the heap and
remove objects. - The concurrent cycle starts with an initial mark phase, which stops all the application
threads.This phase is responsible for finding all the GC root objects in the heap. - The next phase is the mark phase, and it does not stop the application threads.Since it is just a
marking phase, it hasn’t done anything to the heap occupancy, and so no data is shown
about that. - Next comes a preclean phase, which also runs concurrently with the application threads:
- The next phase is a remark phase, but it involves several operations:
- Next comes another concurrent phase—the sweep phase:
- Next comes the concurrent reset phase:
concurrent mode failure:
- When a young collection occurs and there isn’t enough room in the old generation to
hold all the objects that are expected to be promoted, CMS executes what is essentially
a full GC. All application threads are stopped, and the old generation is cleaned of any
dead objects, reducing its occupancy to 1,366 MB—an operation which kept the application
threads paused for a full 5.6 seconds. That operation is single-threaded, which is one reason it takes so long (and one reason why concurrent mode failures are worse as
the heap grows). - The second problem occurs when there is enough room in the old generation to hold
the promoted objects, but the free space is fragmented and so the promotion fails:- As a result, in the middle of the young collection (when all threads were already stopped),
CMS collected and compacted the entire old generation. The good news is that
with the heap compacted, fragmentation issues have been solved (at least for a while).
But that came with a hefty 28-second pause time. This time is much longer than when
CMS had a concurrent mode failure because the entire heap was compacted; the concurrent
mode failure simply freed objects in the heap. The heap at this point appears as
it did at the end of the throughput collector’s full GC (Figure 6-2): the young generation
is completely empty, and the old generation has been compacted. - This occurs when permgen has filled up and needs to be collected; notice that the size
of the CMS Perm space has dropped. In Java 8, this can also occur if the metaspace needs
to be resized. By default, CMS does not collect permgen (or the metaspace), so if it fills
up, a full GC is needed to discard any unreferenced classes.
- As a result, in the middle of the young collection (when all threads were already stopped),
summary
- CMS has several GC operations, but the expected operations are
minor GCs and concurrent cycles. - Concurrent mode failures and promotion failures in CMS are
quite expensive; CMS should be tuned to avoid these as much as
possible. - By default, CMS does not collect permgen.
Understanding the G1 Collector
- G1 is a concurrent collector that operates on discrete regions within the heap. Each
region (there are by default around 2,048 of them) can belong to either the old or new
generation, and the generational regions need not be contiguous. The idea behind having
regions in the old generation is that when the concurrent background threads look
for unreferenced objects, some regions will contain more garbage than other regions.
The actual collection of a region still requires that application threads be stopped, but
G1 can focus on the regions that are mostly garbage and only spend a little bit of time
emptying those regions. This approach—clearing out only the mostly garbage regions
—is what gives G1 its name: Garbage First. - That doesn’t apply to the regions in the young generation: during a young GC, the entire
young generation is either freed or promoted (to a survivor space or to the old generation).
Still, the young generation is defined in terms of regions, in part because it makes
resizing the generations much easier if the regions are predefined.
G1 has four main operations:- A young collection
- A background, concurrent cycle
- A mixed collection
- If necessary, a full GC
young collection
- The G1 young collection is triggered when eden fills up (in this case, after filling four
regions). After the collection, there are no regions assigned to eden, since it is empty.
There is at least one region assigned to the survivor space (partially filled in this example),
and some data has moved into the old generation. - 23.430: [GC pause (young), 0.23094400 secs]
…
[Eden: 1286M(1286M)->0B(1212M)
Survivors: 78M->152M Heap: 1454M(4096M)->242M(4096M)]
[Times: user=0.85 sys=0.05, real=0.23 secs] - Collection of the young generation took 0.23 seconds of real time, during which the GC
threads consumed 0.85 seconds of CPU time. 1,286 MB of objects were moved out of
eden (which was resized to 1,212 MB); 74 MB of that was moved to the survivor space
(it increased in size from 78 M to 152 MB) and the rest were freed. We know they were
freed by observing that the total heap occupancy decreased by 1,212 MB. In the general
case, some objects from the survivor space might have been moved to the old generation,
and if the survivor space were full, some objects from eden would have been promoted
directly to the old generation—in those cases, the size of the old generation would
increase.
concurrent G1 cycle
- Finally, notice that the old generation (consisting of the regions marked with an O or
an X) is actually more occupied after the cycle has completed. That’s because the young
generation collections that occurred during the marking cycle promoted data into the old generation. In addition, the marking cycle doesn’t actually free any data in the old
generation: it merely identifies regions that are mostly garbage. Data from those regions
is freed in a later cycle. - The G1 concurrent cycle has several phases, some of which stop all application threads
and some of which do not. The first phase is an initial-mark phase. That phase stops all
application threads—partly because it also executes a young collection:- As in a regular young collection, the application threads were stopped (for 0.28 seconds),
and the young generation was emptied (71 MB of data was moved from the young
generation to the old generation). The initial-mark output announces that the background
concurrent cycle has begun. Since the initial mark phase also requires all application
threads to be stopped, G1 takes advantage of the young GC cycle to do that
work. The impact of adding the initial mark phase to the young GC wasn’t that large: it
used 20% more CPU cycles than the previous collection, even though the pause was
only slightly longer. (Fortunately, there were spare CPU cycles on the machine for the
parallel G1 threads, or the pause would have been longer.)
- As in a regular young collection, the application threads were stopped (for 0.28 seconds),
- Next, G1 scans the root region:
- 350.994: [GC pause (young)
351.093: [GC concurrent-root-region-scan-end, 0.6100090]
351.093: [GC concurrent-mark-start],
0.37559600 secs] - This takes 0.58 seconds, but it doesn’t stop the application threads; it only uses the
background threads. However, this phase cannot be interrupted by a young collection,
so having available CPU cycles for those background threads is crucial. If the young
generation happens to fill up during the root region scanning, the young collection
(which has stopped all the application threads) must wait for the root scanning to complete.
In effect, this means a longer-than-usual pause to collect the young generation. - The GC pause here starts before the end of the root region scanning, which (along with
the interleaved output) indicates that it was waiting. The timestamps show that application
threads waited about 100 ms—which is why the duration of the young GC pause
is about 100 ms longer than the average duration of other pauses in this log.
- 350.994: [GC pause (young)
- After the root region scanning, G1 enters a concurrent marking phase. This happens
completely in the background; a message is printed when it starts and ends:- 111.382: [GC concurrent-mark-start]
….
120.905: [GC concurrent-mark-end, 9.5225160 sec]
- 111.382: [GC concurrent-mark-start]
- Concurrent marking can be interrupted, so young collections may occur during this
phase. The marking phase is followed by a remarking phase and a normal cleanup phase:- 120.910: [GC remark 120.959:
[GC ref-PRC, 0.0000890 secs], 0.0718990 secs]
[Times: user=0.23 sys=0.01, real=0.08 secs]
120.985: [GC cleanup 3510M->3434M(4096M), 0.0111040 secs]
[Times: user=0.04 sys=0.00, real=0.01 secs]
- 120.910: [GC remark 120.959:
- These phases stop the application threads, though usually for a quite short time. Next
there is an additional cleanup phase that happens concurrently:- 120.996: [GC concurrent-cleanup-start]
120.996: [GC concurrent-cleanup-end, 0.0004520]
- 120.996: [GC concurrent-cleanup-start]
- And with that, the normal G1 cycle is complete—insofar as finding the garbage goes, at
least. But very little has actually been freed yet. A little memory was reclaimed in the
cleanup phase, but all G1 has really done at this point is to identify old regions that are
mostly garbage and can be reclaimed (the ones marked with an X in Figure 6-7).
mixed GCs.
- Now G1 executes a series of mixed GCs. They are called mixed because they perform
the normal young collection, but they also collect some number of the marked regions
from the background scan. - As is usual for a young collection, G1 has completely emptied eden and adjusted the
survivor spaces. Additionally, two of the marked regions have been collected. Those
regions were known to contain mostly garbage, and so a large part of them was freed. Any live data in those regions was moved to another region (just as live data was moved
from the young generation into regions in the old generation). This is why G1 ends up
with a fragmented heap less often than CMS—moving the objects like this is compacting
the heap as G1 goes along. - 79.826: [GC pause (mixed), 0.26161600 secs]
….
[Eden: 1222M(1222M)->0B(1220M)
Survivors: 142M->144M Heap: 3200M(4096M)->1964M(4096M)]
[Times: user=1.01 sys=0.00, real=0.26 secs] - Notice that the entire heap usage has been reduced by more than just the 1,222 MB
removed from eden. That difference (16 MB) seems small, but remember that some of
the survivor space was promoted into the old generation at the same time; in addition,
each mixed GC cleans up only a portion of the targeted old generation regions. As we
continue, we’ll see that it is important to make sure that the mixed GCs clean up enough
memory to prevent future concurrent failures. - The mixed GC cycles will continue until (almost) all of the marked regions have been
collected, at which point G1 will resume regular young GC cycles. Eventually, G1 will
start another concurrent cycle to determine which regions should be freed next.
full GC
- As with CMS, there are times when you’ll observe a full GC in the log, which is an
indication that more tuning (including, possibly, more heap space) will benefit the application
performance. There are primarily four times when this is triggered: - Concurrent mode failure
- G1 starts a marking cycle, but the old generation fills up before the cycle is completed.
In that case, G1 aborts the marking cycle: - 51.408: [GC concurrent-mark-start]
65.473: [Full GC 4095M->1395M(4096M), 6.1963770 secs]
[Times: user=7.87 sys=0.00, real=6.20 secs]
71.669: [GC concurrent-mark-abort] - This failure means that heap size should be increased, or the G1 background processing
must begin sooner, or the cycle must be tuned to run more quickly (e.g., by
using additional background threads).
- G1 starts a marking cycle, but the old generation fills up before the cycle is completed.
- Promotion failure
- G1 has completed a marking cycle and has started performing mixed GCs to clean
up the old regions, but the old generation runs out of space before enough memory
can be reclaimed from the old generation. In the log, a full GC immediately follows
a mixed GC: - 2226.224: [GC pause (mixed)
2226.440: [SoftReference, 0 refs, 0.0000060 secs]
2226.441: [WeakReference, 0 refs, 0.0000020 secs]
2226.441: [FinalReference, 0 refs, 0.0000010 secs]
2226.441: [PhantomReference, 0 refs, 0.0000010 secs]
2226.441: [JNI Weak Reference, 0.0000030 secs]
(to-space exhausted), 0.2390040 secs]
….
[Eden: 0.0B(400.0M)->0.0B(400.0M)
Survivors: 0.0B->0.0B Heap: 2006.4M(2048.0M)->2006.4M(2048.0M)]
[Times: user=1.70 sys=0.04, real=0.26 secs]
2226.510: [Full GC (Allocation Failure)
2227.519: [SoftReference, 4329 refs, 0.0005520 secs]
2227.520: [WeakReference, 12646 refs, 0.0010510 secs]
2227.521: [FinalReference, 7538 refs, 0.0005660 secs]
2227.521: [PhantomReference, 168 refs, 0.0000120 secs]
2227.521: [JNI Weak Reference, 0.0000020 secs]
2006M->907M(2048M), 4.1615450 secs]
[Times: user=6.76 sys=0.01, real=4.16 secs] - This failure means the mixed collections need to happen more quickly; each young
collection needs to process more regions in the old generation.
- G1 has completed a marking cycle and has started performing mixed GCs to clean
- Evacuation failure
- When performing a young collection, there isn’t enough room in the survivor
spaces and the old generation to hold all the surviving objects. This appears in the
GC logs as a specific kind of young GC: - 60.238: [GC pause (young) (to-space overflow), 0.41546900 secs]
- This is an indication that the heap is largely full or fragmented. G1 will attempt to
compensate for this, but you can expect this to end badly: G1 will resort to performing
a full GC. The easy way to overcome this is to increase the heap size,
- When performing a young collection, there isn’t enough room in the survivor
- Humongous allocation failure
- Applications that allocate very large objects can trigger another kind of full GC in
G1; see “G1 allocation of humongous objects” on page 169. There are no tools to
diagnose that situation specifically from the standard GC log, though if a full GC
occurs for no apparent reason, it is likely due to an issue with humongous allocations.
- Applications that allocate very large objects can trigger another kind of full GC in
Quick Summary
- G1 has a number of cycles (and phases within the concurrent
cycle). A well-tuned JVM running G1 should only experience
young, mixed, and concurrent GC cycles. - Small pauses occur for some of the G1 concurrent phases.
- G1 should be tuned if necessary to avoid full GC cycles.
原文地址:https://www.cnblogs.com/parkdifferent/p/9691718.html
时间: 2024-10-09 09:08:46