Young GC

As mentioned above, Young GC (hereinafter referred to as YGC) refers to the new generation of garbage collection. The YGC process of G1 will be discussed in detail below.

Choose CSet

The Collection process of YGC is located in G1CollectedHeap::do_collection_pause_at_safepoint(). Before garbage Collection, it creates a Collection Set (CSet) to store the regions that need to be cleaned. The choice of the right Region to put into the CSet is intended to give G1 a reasonable pause time that users expect. The CSet creation process is shown in Listing 11-2:

Listing 11-2 Selecting Region to put into CSet

void G1Policy::finalize_collection_set(...) {// Select the new generation Region first, and the maximum pause time expected by the user is target_pause_time_ms // after G1 calculates the possible cleanup time of the new generation Region, Double time_remaining_MS = _collection_set->finalize_young_part(...) ; _collection_set->finalize_old_part(time_remaining_ms); }Copy the code

G1’s YGC is only responsible for cleaning up new generation regions, so finalize_old_part() does not select any regions, so just focus on finalize_young_part(). Finalize_young_part prepares the garbage collection after all Eden and Survivor regions are added to the CSet.

G1 creates G1ParTask in mystery _collect_set() and then blocks it until G1ParTask is completed, meaning that applications are STW throughout YGC. Similar to the YGC of Parallel GC, execution of G1ParTask is done by thread group GangWorker to minimize STW time. It is not difficult to see that the actual work of YGC is located in G1ParTask, which is mainly divided into three stages:

1) clean up the root set (G1RootProcessor: : evacuate_roots);

2) Process RSet (G1RemSet::oops_into_collection_set_do);

3) object replication (G1ParEvacuateFollowersClosure: : do_void).

Clean up the root set

The first stage is to clean up the root set. Chapter 10 says that HotSpot VM belongs to GCRoot in many ways, and that G1ParTask asks how it evacuates GC roots() in search of surviving objects. Using thread stacks as an example, G1 scans each stack frame in the thread stacks of all JavaThread and VMThreads in the virtual machine, finds object references, and applies G1ParCopyClosure to them, as shown in Listing 11-3:

Listing 11-3 G1ParCopyClosure

void G1ParCopyClosure<barrier, do_mark_object>::do_oop_work(T* p) { ... oop obj = CompressedOops::decode_not_null(heap_oop); const InCSetState state = _g1h->in_cset_state(obj); If (state.is_in_cset()) {oop forwardee; markOop m = obj->mark_raw(); Forwardee = (oop) m->decode_pointer(); Forwardee = _par_scan_state-> copy_to_SURVIVor_space (...) ; } // modify the reference to this object in the root set to RawAccess<IS_NOT_NULL>::oop_store(p, forwardee); . } else { ... }}Copy the code

The core code for cleaning up the root set is copy_to_SURVIVor_space, which moves objects younger than 15 in Eden Region to Survivor Region and objects older than 15 to Old Region. After applying G1ParCopyClosure to references in the root set that pointed to Eden Region objects, Eden Region objects will be copied to SurvivorRegion, so references in the root set need to be redirected accordingly, as shown in Figure 11-3.

Figure 11-3 Clearing the root set

Copy_to_survivor_space after moving objects will also deal with members of the object, with G1ScanEvacuatedObjClosure if members also belong to the CSet, then put them into a G1ParScanThreadState queue, Wait for the third phase to copy them to Survivor Regions. In summary, the first phase copies objects directly reachable from the root set to Survivor Regions, queues the members of these objects, and updates the root set pointing.

Processing RSet

The first stage marks objects from GC Root to Eden Region. For objects from OldRegion to Eden Region, RSet is required. This step is done by G1RemSet::oops_into_collection_set_do of G1ParTask, which consists of updating RSet (update_REM_set) and scanning RSet (scan_rem_set).

Scan_rem_set traverses all regions in the CSet, finding the reference and marking the living object as the starting point.

Object replication

After the previous steps, all live objects discovered by YGC will be in the G1ParScanThreadState queue. Object replication is responsible for copying all surviving objects in the queue to Survivor regions or promoting them to Old regions, as shown in Listing 11-4:

Listing 11-4 Object copy

Template <class T> void G1ParScanThreadState::do_oop_evac(T* p) {// Copy only live objects in CSet oop obj = RawAccess<IS_NOT_NULL>::oop_load(p); const InCSetState in_cset_state = _g1h->in_cset_state(obj); if (! in_cset_state.is_in_cset()) { return; } // Copy the object to Survivor Region (or promote it to Old Region) markOop m = obj->mark_raw(); if (m->is_marked()) { obj = (oop) m->decode_pointer(); } else { obj = copy_to_survivor_space(in_cset_state, obj, m); } RawAccess<IS_NOT_NULL>::oop_store(p, obj); If (HeapRegion::is_in_same_region(p, obj)) {return; if (HeapRegion:: is_same_region (p, obj)); RSet HeapRegion* from = _g1h->heap_region_containing(p); if (! from->is_young()) { enqueue_card_if_tracked(p, obj); }}Copy the code

Object replication is the last step of YGC. After this, all surviving objects of the new generation will be moved to Survivor Region or promoted to Old Region, and the Eden space before can be reclaimed. In addition, YGC replication algorithm is equivalent to cleaning up heap fragments, such as sorting out possible fragments in Eden Region.

Public account: Java Architects Association, updated daily technical good articles