This blog is about the a question I received:
Are the GC roots of
Minor GC
andMajor GC
the same?
GC Algorithm
If we are asked to come up with a GC collection algorithm, we may think about a solution : in which we maintain the count of reference to some objects, if count drop to 0, we claim the memory. This is called Reference Count
, which can’t solve the cyclic reference, is a wrong algorithm.
The GC collection algorithm adopted by JVM is marking
algorithm from GC roots. The GC roots sounds very mysterious, but its meaning is very simple: GC roots is the object that live outside Java heap and referring to object in Java heap.
From the Hotspot JVM source code, we can see the following GC roots types:
enum RootType {
universe = 1,
jni_handles = 2,
threads = 3,
object_synchronizer = 4,
system_dictionary = 5,
class_loader_data = 6,
management = 7,
jvmti = 8,
code_cache = 9
};
And from comment of source code, we can conclude the meaning of those roots:
- Universe: is a name space holding known system classes and objects in the VM. The object heap is allocated and accessed through Universe, and various allocation support is provided.
- JNIHandles:
JNI
code - Threads: live threads
- ObjectSynchronizer: monitor object
- Management: JVM used management class
- JvmtiExport: JVM tool interface, for debug & profiling;
- SystemDictionary: Loaded classes are accessible through the SystemDictionary.
- ClassLoaderDataGraph: A class loader represents a linkset. Conceptually, a linkset identifies the complete transitive closure of resolved links that a dynamic linker can produce. A ClassLoaderData also encapsulates the allocation space, called a metaspace, used by the dynamic linker to allocate the runtime representation of all the types it defines.
PSScavenge for Minor GC
In order to answer the question, we need to dive into the marking algorithm process. So, we choose the PSScavenge
as our target. Scavenging
is a garbage collector for young generation in Hotspot JVM, PS
represents parallel process.
A simple search we found the following method, which is the GC process:
PSScavenge::invoke()
Through some code reading, we can draw following invocation sequence (we take Universe
as an example, other roots are the same):
And in do_oop_work
, live object is copied to survivor space and card_table
(the utility used to avoid scan old generation when handle young generation) is updated:
// p is GC roots
oop o = RawAccess<OOP_NOT_NULL>::oop_load(p);
oop new_obj = o->is_forwarded()
? o->forwardee()
: copy_to_survivor_space<promote_immediately>(o);
RawAccess<OOP_NOT_NULL>::oop_store(p, new_obj);
if ((!PSScavenge::is_obj_in_young((HeapWord*)p)) &&
ParallelScavengeHeap::heap()->is_in_reserved(p)) {
if (PSScavenge::is_obj_in_young(new_obj)) {
PSScavenge::card_table()->inline_write_ref_field_gc(p, new_obj);
}
}
PSParallelCompact for Major GC
In order to compare, we then see the GC code for old generation. Using similar way, we can have the illustration of PSParallelCompact
's , which is garbage collector for old generation:
Until now, we still not find any differences between GC roots of young generation and old generation. When doing young generation garbage collection, card_table
should be used, but we didn’t see any clue. So we decided to review the PSScavenge::invoke()
.
This time, we find we miss a different task from ScavengeRootsTask
– OldToYoungRootsTask
OldToYoungRootsTask
In PSScavenge::invoke()
, Hotspot will open a thread pool to run a serial tasks. Except the ScavengeRootsTask
which handle all the roots as above listed, OldToYoungRootsTask
handle reference in card_table
:
So, we can understand where card_table
is used and there exists no special roots other than constant listed above. Now, can see the answer of our question: under the strict definition of GC roots, Minor GC
and Major GC
use same roots.
Let to be Solved
Although the problem can be answered, but more questions arises:
- The meaning and usage of
is_forwarded
&forwardee
is still unclear for me; - How young generation to old generation reference resolved in old generation mark and compaction? Didn’t find any clue in PSScavenge::invoke_no_policy();
Abstraction for Ref
OldToYoungRootsTask
: This task is used to scan old to young roots in parallel
The markOop
describes the header of an object.
OopsInGenClosure
: Closure for iterating roots from a particular generation
Note: all classes deriving from this MUST call this do_barrier method at the end of their own do_oop method! Note: no do_oop defined, this is an abstract class.
Code Ref
- ParCompactionManager::mark_and_push()
- MarkFromRootsTask::do_it()
- PSParallelCompact::invoke_no_policy()
- PSScavenge::invoke_no_policy()
- Universe::oops_do()
- ScavengeRootsTask::do_it()
- PSRootsClosure
- OldToYoungRootsTask::do_it()
Written with StackEdit.
评论
发表评论