Mar*_*ger 16 java garbage-collection jvm heap-memory jvm-hotspot
介绍:
在大学里,人们了解到Java(和类似语言)中的典型垃圾收集根是加载类的静态变量,当前运行线程的线程局部变量,"外部引用"(如JNI句柄)和GC特定(如old-to) -在世代垃圾收集器的Minor GCs期间的年轻指针.从理论上讲,这听起来并不难.
问题:
我读的热点的源代码和感兴趣的是如何在VM内检测到这些垃圾收集根,即,该方法是使用内部的JVM源代码访问所有的根源.
调查:
我发现psMarkSweep.cpp属于各种GC实现的各种文件(例如)包含非常相似的结构.
以下是我认为涵盖强大根源的方法PSMarkSweep::mark_sweep_phase1方法psMarkSweep.cpp:
ParallelScavengeHeap::ParStrongRootsScope psrs;
Universe::oops_do(mark_and_push_closure());
JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles
CLDToOopClosure mark_and_push_from_cld(mark_and_push_closure());
MarkingCodeBlobClosure each_active_code_blob(mark_and_push_closure(), !CodeBlobToOopClosure::FixRelocations);
Threads::oops_do(mark_and_push_closure(), &mark_and_push_from_cld, &each_active_code_blob);
ObjectSynchronizer::oops_do(mark_and_push_closure());
FlatProfiler::oops_do(mark_and_push_closure());
Management::oops_do(mark_and_push_closure());
JvmtiExport::oops_do(mark_and_push_closure());
SystemDictionary::always_strong_oops_do(mark_and_push_closure());
ClassLoaderDataGraph::always_strong_cld_do(follow_cld_closure());
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(mark_and_push_closure()));
Run Code Online (Sandbox Code Playgroud)
以下代码psScavenge.cpp似乎为不同类型的GC根添加任务:
if (!old_gen->object_space()->is_empty()) {
// There are only old-to-young pointers if there are objects
// in the old gen.
uint stripe_total = active_workers;
for(uint i=0; i < stripe_total; i++) {
q->enqueue(new OldToYoungRootsTask(old_gen, old_top, i, stripe_total));
}
}
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::universe));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jni_handles));
// We scan the thread roots in parallel
Threads::create_thread_roots_tasks(q);
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::object_synchronizer));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::flat_profiler));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::management));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::system_dictionary));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::class_loader_data));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti));
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache));
Run Code Online (Sandbox Code Playgroud)
看一下ScavangeRootsTask,我们看到熟悉的代码类似于以下代码psMarkSweep:
void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
assert(Universe::heap()->is_gc_active(), "called outside gc");
PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which);
PSScavengeRootsClosure roots_closure(pm);
PSPromoteRootsClosure roots_to_old_closure(pm);
switch (_root_type) {
case universe:
Universe::oops_do(&roots_closure);
break;
case jni_handles:
JNIHandles::oops_do(&roots_closure);
break;
case threads:
{
ResourceMark rm;
CLDClosure* cld_closure = NULL; // Not needed. All CLDs are already visited.
Threads::oops_do(&roots_closure, cld_closure, NULL);
}
break;
case object_synchronizer:
ObjectSynchronizer::oops_do(&roots_closure);
break;
case flat_profiler:
FlatProfiler::oops_do(&roots_closure);
break;
case system_dictionary:
SystemDictionary::oops_do(&roots_closure);
break;
case class_loader_data:
{
PSScavengeKlassClosure klass_closure(pm);
ClassLoaderDataGraph::oops_do(&roots_closure, &klass_closure, false);
}
break;
case management:
Management::oops_do(&roots_closure);
break;
case jvmti:
JvmtiExport::oops_do(&roots_closure);
break;
case code_cache:
{
MarkingCodeBlobClosure each_scavengable_code_blob(&roots_to_old_closure, CodeBlobToOopClosure::FixRelocations);
CodeCache::scavenge_root_nmethods_do(&each_scavengable_code_blob);
}
break;
default:
fatal("Unknown root type");
}
// Do the real work
pm->drain_stacks(false);
}
Run Code Online (Sandbox Code Playgroud)
洞察:
源代码中的GC根源列表看起来比我在第一句中写的要大得多,所以我尝试在下面列出它们,并附上一些注释:
psMarkSweep.cpp使用CLDToOopClosure并使用Code blob执行某些操作,psScavange.cpp而不使用.Afaik,CLD代表类加载器数据,但我不知道为什么它在一种情况下使用,而在另一种情况下不使用.代码blob的相同帐户.psMarkSweep.cpp),有时使用CodeCache访问这些代码blob (如同psScavenge.cpp).问题:
虽然在源代码中可以找到很多东西,但我仍然很难理解这些GC根源,或者如何找到这些GC根.
CLDToOopClosure和a MarkingCodeBlobClosure组合Threads::oops_do(如所做的psMarkSweep.cpp)有什么区别.ClassLoaderDataGraph::oops_doCodeCache::scavenge_root_nmethods_dopsScavenge.cpp备注:
我知道这是一个很长的问题,有各种各样的子问题,但我认为很难将它分成多个.我感谢每一个发布的答案,即使它没有涵盖上述所有问题的答案,即使答案中的部分问题也会对我有所帮助.谢谢!
正如您自己已经发现的那样,<Subsystem>::oops_do()HotSpot JVM中的典型机制是访问GC的根源<Subsystem>.顺便说一下,好的分析.只需继续浏览VM源代码,您就会找到答案,因为代码中有很多有用的注释.
请注意,其目的oops_do不仅是标记可到达的对象,还要处理引用本身,特别是在压缩过程中重新定位它们.
CodeBlob是一段生成的代码.它不仅涵盖了JITted方法(又名nmethods),还涵盖了运行时生成的各种VM存根和例程.
// CodeBlob - superclass for all entries in the CodeCache.
//
// Suptypes are:
// nmethod : Compiled Java methods (include method that calls to native code)
// RuntimeStub : Call to VM runtime methods
// DeoptimizationBlob : Used for deoptimizatation
// ExceptionBlob : Used for stack unrolling
// SafepointBlob : Used to handle illegal instruction exceptions
Run Code Online (Sandbox Code Playgroud)
这些代码段可能包含对Heap对象的嵌入式引用,例如String/Class/MethodHandle文字和静态最终常量.
CLDToOopClosurein 的目的Threads::oops_do是标记通过方法指针引用的对象,否则不标记:
// The method pointer in the frame might be the only path to the method's
// klass, and the klass needs to be kept alive while executing. The GCs
// don't trace through method pointers, so typically in similar situations
// the mirror or the class loader of the klass are installed as a GC root.
// To minimze the overhead of doing that here, we ask the GC to pass down a
// closure that knows how to keep klasses alive given a ClassLoaderData.
cld_f->do_cld(m->method_holder()->class_loader_data());
Run Code Online (Sandbox Code Playgroud)
同样,MarkingCodeBlobClosure用于标记仅从活动 nmethods 引用的对象:
// In cases where perm gen is collected, GC will want to mark
// oops referenced from nmethods active on thread stacks so as to
// prevent them from being collected. However, this visit should be
// restricted to certain phases of the collection only. The
// closure decides how it wants nmethods to be traced.
if (cf != NULL)
cf->do_code_blob(_cb);
Run Code Online (Sandbox Code Playgroud)
请注意,CodeCache::scavenge_root_nmethods_do在标记阶段不会调用:
// Do not treat nmethods as strong roots for mark/sweep, since we can unload them.
//CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(&mark_and_push_closure));
Run Code Online (Sandbox Code Playgroud)
SystemDictionary主要负责解析类的符号名称.它不用作标记的GC根(Bootstrap和System类加载器除外).另一方面,ClassLoaderDataGraph维护类加载器实体的完整链接集.它确实充当GC根,负责类卸载.
// A ClassLoaderData identifies the full set of class types that a class
// loader's name resolution strategy produces for a given configuration of the
// class loader.
// Class types in the ClassLoaderData may be defined by from class file binaries
// provided by the class loader, or from other class loader it interacts with
// according to its name resolution strategy.
// ...
// ClassLoaderData carries information related to a linkset (e.g.,
// metaspace holding its klass definitions).
// The System Dictionary and related data structures (e.g., placeholder table,
// loader constraints table) as well as the runtime representation of classes
// only reference ClassLoaderData.
//
// Instances of java.lang.ClassLoader holds a pointer to a ClassLoaderData that
// that represent the loader's "linking domain" in the JVM.
Run Code Online (Sandbox Code Playgroud)
实习字符串不需要在GC中存活.它们不是GC的根源.可以卸载其他无法访问的实习字符串,而HotSpot实际上就是这样做的.
垃圾收集器本身不会引入新类型的根,但它可能会使用影响"可达性"含义的算法和数据结构.例如,并发收集器可以将在初始标记和最终备注之间修改的所有引用视为可达,即使它们不可达.
| 归档时间: |
|
| 查看次数: |
679 次 |
| 最近记录: |