Docs/code/detailed explanation for "Ext Root Scanning" on G1 gc?
Asked Answered
I

1

6

1] Can anyone point to docs or explain in detail how "Ext Root Scanning" works in G1 GC, especially for JNI handles? (Specific to Java 7 if possible please)

2] Bonus: How different can we expect the openJDK code for G1 gc to be from Hotspot? If we can expect this to be the same, please can you point to relevant parts of the openJDK code for G1 GC ext root scanning?

thanks

Ide answered 25/4, 2017 at 16:16 Comment(1)
Take a look at Distributed, Embedded and Real-time Java Systems - the book, and Java Performance Companion. There really isn't much external data that goes in depth in relation to external root scanning.Hiragana
A
4

Overview

From Oracle Doc:

When performing garbage collections, G1 operates in a manner similar to the CMS collector. G1 performs a concurrent global marking phase to determine the liveness of objects throughout the heap.

And external root region scanning is one of the phase of marking process.

From Java Performance Companion:

During this phase the external (off-heap) roots such as the JVM’s system dictionary, VM data structures, JNI thread handles, hardware registers, global variables, and thread stack roots are scanned to find out if any point into the current pause’s collection set (CSet).

Detail and Code

Yes, we can expect the code for g1 of openjdk and hotspot to be same as here stated. So we can explain the detail process by reading source code.

From G1CollectedHeap:

void
G1CollectedHeap::
g1_process_strong_roots(bool collecting_perm_gen,
                        SharedHeap::ScanningOption so,
                        OopClosure* scan_non_heap_roots,
                        OopsInHeapRegionClosure* scan_rs,
                        OopsInGenClosure* scan_perm,
                        int worker_i) {
  //...
  process_strong_roots(false, // no scoping; this is parallel code
                       collecting_perm_gen, so,
                       &buf_scan_non_heap_roots,
                       &eager_scan_code_roots,
                       &buf_scan_perm);
  //...
}

Then in process_strong_roots:

  // Global (strong) JNI handles
  if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do))
    JNIHandles::oops_do(roots);

And the core process about JNI: it iterate the JNI handles block and find out if this handle block's oops (oop: Java's reference abstraction) is point to heap area, which means whether this JNI oop can be a root for gc.

for (JNIHandleBlock* current = current_chain; current != NULL;
     current = current->_next) {
  assert(current == current_chain || current->pop_frame_link() == NULL,
    "only blocks first in chain should have pop frame link set");
  for (int index = 0; index < current->_top; index++) {
    oop* root = &(current->_handles)[index];
    oop value = *root;
    // traverse heap pointers only, not deleted handles or free list
    // pointers
    if (value != NULL && Universe::heap()->is_in_reserved(value)) {
      f->do_oop(root);
    }
  }
  // the next handle block is valid only if current block is full
  if (current->_top < block_size_in_oops) {
    break;
  }
}

And then, this root is remembered in a array, processed by OopClosure when array is full, in which case, iterate the reference of root to mark live objects.

More:

Avunculate answered 13/5, 2017 at 3:54 Comment(1)
Perfect. Nice one, Tony !Ide

© 2022 - 2024 — McMap. All rights reserved.