GC Root

GC Root全称是garbage collection root, 即垃圾回收的根. 回到我们的葡萄比喻上来, 也就是一串葡萄的柄. 实际上JVM中的GC Root不只一个, 也就是多个这样的 “柄”.

来看看hotspot网站的解释:

garbage collection root

A pointer into the Java object heap from outside the heap. These come up, e.g., from static fields of classes, local references in activation frames, etc.

官网的解释是由heap外部指向heap内的对象的指针, 比如class的静态字段, 活跃栈帧里的本地引用等. 这里有点小疑问, 从代码上来看, 静态字段也是存在于heap对象(Class对象)中的, 这样来看, 按照heap外指针的定义来解释好像不太妥当. 可以先不关注这个问题, 等会我们从代码上来直观感受一下.

这个说法可能听起来有点奇怪, 因为GC Root的存在是客观的, 并不是因为需要, 这里的意思更多的是为什么要单独提出这么一个概念. 前面介绍过, 对象之间的相互引用会形成一个图(graph), 而要遍历存活的对象, 必须从一些点出发, 这些点, 就是GC Root了.

这样做的目的避免有闭环的出现,闭环的出现讲师灾难性,大大影响性能。

最为GC Root四种对象:

  • 虚拟机栈中的引用对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用对象
  • 本地方法栈中JNI引用对象

GC Root 遍历的源码:

void GenCollectedHeap::process_roots(StrongRootsScope* scope,ScanningOption so,OopClosure* strong_roots,OopClosure* weak_roots,CLDClosure* strong_cld_closure,CLDClosure* weak_cld_closure,CodeBlobToOopClosure* code_roots) {// General roots.assert(Threads::thread_claim_parity() != 0, "must have called prologue code");assert(code_roots != NULL, "code root closure should always be set");// _n_termination for _process_strong_tasks should be set up stream// in a method not running in a GC worker.  Otherwise the GC worker// could be trying to change the termination condition while the task// is executing in another GC worker.if (!_process_strong_tasks->is_task_claimed(GCH_PS_ClassLoaderDataGraph_oops_do)) {ClassLoaderDataGraph::roots_cld_do(strong_cld_closure, weak_cld_closure);}// Only process code roots from thread stacks if we aren't visiting the entire CodeCache anywayCodeBlobToOopClosure* roots_from_code_p = (so & SO_AllCodeCache) ? NULL : code_roots;bool is_par = scope->n_threads() > 1;Threads::possibly_parallel_oops_do(is_par, strong_roots, roots_from_code_p);if (!_process_strong_tasks->is_task_claimed(GCH_PS_Universe_oops_do)) {Universe::oops_do(strong_roots);}// Global (strong) JNI handlesif (!_process_strong_tasks->is_task_claimed(GCH_PS_JNIHandles_oops_do)) {JNIHandles::oops_do(strong_roots);}if (!_process_strong_tasks->is_task_claimed(GCH_PS_ObjectSynchronizer_oops_do)) {ObjectSynchronizer::oops_do(strong_roots);}if (!_process_strong_tasks->is_task_claimed(GCH_PS_FlatProfiler_oops_do)) {FlatProfiler::oops_do(strong_roots);}if (!_process_strong_tasks->is_task_claimed(GCH_PS_Management_oops_do)) {Management::oops_do(strong_roots);}if (!_process_strong_tasks->is_task_claimed(GCH_PS_jvmti_oops_do)) {JvmtiExport::oops_do(strong_roots);}if (!_process_strong_tasks->is_task_claimed(GCH_PS_SystemDictionary_oops_do)) {SystemDictionary::roots_oops_do(strong_roots, weak_roots);}// All threads execute the following. A specific chunk of buckets// from the StringTable are the individual tasks.if (weak_roots != NULL) {if (is_par) {StringTable::possibly_parallel_oops_do(weak_roots);} else {StringTable::oops_do(weak_roots);}}if (!_process_strong_tasks->is_task_claimed(GCH_PS_CodeCache_oops_do)) {if (so & SO_ScavengeCodeCache) {assert(code_roots != NULL, "must supply closure for code cache");// We only visit parts of the CodeCache when scavenging.CodeCache::scavenge_root_nmethods_do(code_roots);}if (so & SO_AllCodeCache) {assert(code_roots != NULL, "must supply closure for code cache");// CMSCollector uses this to do intermediate-strength collections.// We scan the entire code cache, since CodeCache::do_unloading is not called.CodeCache::blobs_do(code_roots);}// Verify that the code cache contents are not subject to// movement by a scavenging collection.DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, !CodeBlobToOopClosure::FixRelocations));DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable));}
}

http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/gc_implementation/

上面是hotspot1.7(openjdk)源代码

标记存活对象的处理主要在markSweep.cpp

/** Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.** This code is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 only, as* published by the Free Software Foundation.** This code is distributed in the hope that it will be useful, but WITHOUT* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License* version 2 for more details (a copy is included in the LICENSE file that* accompanied this code).** You should have received a copy of the GNU General Public License version* 2 along with this work; if not, write to the Free Software Foundation,* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.** Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA* or visit www.oracle.com if you need additional information or have any* questions.**/#include "precompiled.hpp"
#include "compiler/compileBroker.hpp"
#include "gc_implementation/shared/markSweep.inline.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "oops/methodDataOop.hpp"
#include "oops/objArrayKlass.inline.hpp"
#include "oops/oop.inline.hpp"Stack<oop>              MarkSweep::_marking_stack;
Stack<DataLayout*>      MarkSweep::_revisit_mdo_stack;
Stack<Klass*>           MarkSweep::_revisit_klass_stack;
Stack<ObjArrayTask>     MarkSweep::_objarray_stack;Stack<oop>              MarkSweep::_preserved_oop_stack;
Stack<markOop>          MarkSweep::_preserved_mark_stack;
size_t                  MarkSweep::_preserved_count = 0;
size_t                  MarkSweep::_preserved_count_max = 0;
PreservedMark*          MarkSweep::_preserved_marks = NULL;
ReferenceProcessor*     MarkSweep::_ref_processor   = NULL;#ifdef VALIDATE_MARK_SWEEP
GrowableArray<void*>*   MarkSweep::_root_refs_stack = NULL;
GrowableArray<oop> *    MarkSweep::_live_oops = NULL;
GrowableArray<oop> *    MarkSweep::_live_oops_moved_to = NULL;
GrowableArray<size_t>*  MarkSweep::_live_oops_size = NULL;
size_t                  MarkSweep::_live_oops_index = 0;
size_t                  MarkSweep::_live_oops_index_at_perm = 0;
GrowableArray<void*>*   MarkSweep::_other_refs_stack = NULL;
GrowableArray<void*>*   MarkSweep::_adjusted_pointers = NULL;
bool                         MarkSweep::_pointer_tracking = false;
bool                         MarkSweep::_root_tracking = true;GrowableArray<HeapWord*>* MarkSweep::_cur_gc_live_oops = NULL;
GrowableArray<HeapWord*>* MarkSweep::_cur_gc_live_oops_moved_to = NULL;
GrowableArray<size_t>   * MarkSweep::_cur_gc_live_oops_size = NULL;
GrowableArray<HeapWord*>* MarkSweep::_last_gc_live_oops = NULL;
GrowableArray<HeapWord*>* MarkSweep::_last_gc_live_oops_moved_to = NULL;
GrowableArray<size_t>   * MarkSweep::_last_gc_live_oops_size = NULL;
#endifvoid MarkSweep::revisit_weak_klass_link(Klass* k) {_revisit_klass_stack.push(k);
}void MarkSweep::follow_weak_klass_links() {// All klasses on the revisit stack are marked at this point.// Update and follow all subklass, sibling and implementor links.if (PrintRevisitStats) {gclog_or_tty->print_cr("#classes in system dictionary = %d",SystemDictionary::number_of_classes());gclog_or_tty->print_cr("Revisit klass stack size = " SIZE_FORMAT,_revisit_klass_stack.size());}while (!_revisit_klass_stack.is_empty()) {Klass* const k = _revisit_klass_stack.pop();k->follow_weak_klass_links(&is_alive, &keep_alive);}follow_stack();
}void MarkSweep::revisit_mdo(DataLayout* p) {_revisit_mdo_stack.push(p);
}void MarkSweep::follow_mdo_weak_refs() {// All strongly reachable oops have been marked at this point;// we can visit and clear any weak references from MDO's which// we memoized during the strong marking phase.assert(_marking_stack.is_empty(), "Marking stack should be empty");if (PrintRevisitStats) {gclog_or_tty->print_cr("#classes in system dictionary = %d",SystemDictionary::number_of_classes());gclog_or_tty->print_cr("Revisit MDO stack size = " SIZE_FORMAT,_revisit_mdo_stack.size());}while (!_revisit_mdo_stack.is_empty()) {_revisit_mdo_stack.pop()->follow_weak_refs(&is_alive);}follow_stack();
}MarkSweep::FollowRootClosure  MarkSweep::follow_root_closure;
CodeBlobToOopClosure MarkSweep::follow_code_root_closure(&MarkSweep::follow_root_closure, /*do_marking=*/ true);void MarkSweep::FollowRootClosure::do_oop(oop* p)       { follow_root(p); }
void MarkSweep::FollowRootClosure::do_oop(narrowOop* p) { follow_root(p); }MarkSweep::MarkAndPushClosure MarkSweep::mark_and_push_closure;void MarkSweep::MarkAndPushClosure::do_oop(oop* p)       { assert(*p == NULL || (*p)->is_oop(), ""); mark_and_push(p); }
void MarkSweep::MarkAndPushClosure::do_oop(narrowOop* p) { mark_and_push(p); }void MarkSweep::follow_stack() {do {while (!_marking_stack.is_empty()) {oop obj = _marking_stack.pop();assert (obj->is_gc_marked(), "p must be marked");obj->follow_contents();}// Process ObjArrays one at a time to avoid marking stack bloat.if (!_objarray_stack.is_empty()) {ObjArrayTask task = _objarray_stack.pop();objArrayKlass* const k = (objArrayKlass*)task.obj()->blueprint();k->oop_follow_contents(task.obj(), task.index());}} while (!_marking_stack.is_empty() || !_objarray_stack.is_empty());
}MarkSweep::FollowStackClosure MarkSweep::follow_stack_closure;void MarkSweep::FollowStackClosure::do_void() { follow_stack(); }// We preserve the mark which should be replaced at the end and the location
// that it will go.  Note that the object that this markOop belongs to isn't
// currently at that address but it will be after phase4
void MarkSweep::preserve_mark(oop obj, markOop mark) {// We try to store preserved marks in the to space of the new generation since// this is storage which should be available.  Most of the time this should be// sufficient space for the marks we need to preserve but if it isn't we fall// back to using Stacks to keep track of the overflow.if (_preserved_count < _preserved_count_max) {_preserved_marks[_preserved_count++].init(obj, mark);} else {_preserved_mark_stack.push(mark);_preserved_oop_stack.push(obj);}
}MarkSweep::AdjustPointerClosure MarkSweep::adjust_root_pointer_closure(true);
MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure(false);void MarkSweep::AdjustPointerClosure::do_oop(oop* p)       { adjust_pointer(p, _is_root); }
void MarkSweep::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); }void MarkSweep::adjust_marks() {assert( _preserved_oop_stack.size() == _preserved_mark_stack.size(),"inconsistent preserved oop stacks");// adjust the oops we saved earlierfor (size_t i = 0; i < _preserved_count; i++) {_preserved_marks[i].adjust_pointer();}// deal with the overflow stackStackIterator<oop> iter(_preserved_oop_stack);while (!iter.is_empty()) {oop* p = iter.next_addr();adjust_pointer(p);}
}void MarkSweep::restore_marks() {assert(_preserved_oop_stack.size() == _preserved_mark_stack.size(),"inconsistent preserved oop stacks");if (PrintGC && Verbose) {gclog_or_tty->print_cr("Restoring %d marks",_preserved_count + _preserved_oop_stack.size());}// restore the marks we saved earlierfor (size_t i = 0; i < _preserved_count; i++) {_preserved_marks[i].restore();}// deal with the overflowwhile (!_preserved_oop_stack.is_empty()) {oop obj       = _preserved_oop_stack.pop();markOop mark  = _preserved_mark_stack.pop();obj->set_mark(mark);}
}#ifdef VALIDATE_MARK_SWEEPvoid MarkSweep::track_adjusted_pointer(void* p, bool isroot) {if (!ValidateMarkSweep)return;if (!isroot) {if (_pointer_tracking) {guarantee(_adjusted_pointers->contains(p), "should have seen this pointer");_adjusted_pointers->remove(p);}} else {ptrdiff_t index = _root_refs_stack->find(p);if (index != -1) {int l = _root_refs_stack->length();if (l > 0 && l - 1 != index) {void* last = _root_refs_stack->pop();assert(last != p, "should be different");_root_refs_stack->at_put(index, last);} else {_root_refs_stack->remove(p);}}}
}void MarkSweep::check_adjust_pointer(void* p) {_adjusted_pointers->push(p);
}class AdjusterTracker: public OopClosure {public:AdjusterTracker() {}void do_oop(oop* o)       { MarkSweep::check_adjust_pointer(o); }void do_oop(narrowOop* o) { MarkSweep::check_adjust_pointer(o); }
};void MarkSweep::track_interior_pointers(oop obj) {if (ValidateMarkSweep) {_adjusted_pointers->clear();_pointer_tracking = true;AdjusterTracker checker;obj->oop_iterate(&checker);}
}void MarkSweep::check_interior_pointers() {if (ValidateMarkSweep) {_pointer_tracking = false;guarantee(_adjusted_pointers->length() == 0, "should have processed the same pointers");}
}void MarkSweep::reset_live_oop_tracking(bool at_perm) {if (ValidateMarkSweep) {guarantee((size_t)_live_oops->length() == _live_oops_index, "should be at end of live oops");_live_oops_index = at_perm ? _live_oops_index_at_perm : 0;}
}void MarkSweep::register_live_oop(oop p, size_t size) {if (ValidateMarkSweep) {_live_oops->push(p);_live_oops_size->push(size);_live_oops_index++;}
}

这个类中关键的数据是:

GrowableArray<oop> *    MarkSweep::_live_oops = NULL;
GrowableArray<oop> *    MarkSweep::_live_oops_moved_to = NULL;

看下GrowableArray 源码:

/** Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.** This code is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 only, as* published by the Free Software Foundation.** This code is distributed in the hope that it will be useful, but WITHOUT* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License* version 2 for more details (a copy is included in the LICENSE file that* accompanied this code).** You should have received a copy of the GNU General Public License version* 2 along with this work; if not, write to the Free Software Foundation,* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.** Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA* or visit www.oracle.com if you need additional information or have any* questions.**/#include "precompiled.hpp"
#include "memory/resourceArea.hpp"
#include "utilities/growableArray.hpp"
#ifdef TARGET_OS_FAMILY_linux
# include "thread_linux.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_solaris
# include "thread_solaris.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_windows
# include "thread_windows.inline.hpp"
#endif
#ifdef ASSERT
void GenericGrowableArray::set_nesting() {if (on_stack()) {_nesting = Thread::current()->resource_area()->nesting();}
}void GenericGrowableArray::check_nesting() {// Check for insidious allocation bug: if a GrowableArray overflows, the// grown array must be allocated under the same ResourceMark as the original.// Otherwise, the _data array will be deallocated too early.if (on_stack() &&_nesting != Thread::current()->resource_area()->nesting()) {fatal("allocation bug: GrowableArray could grow within nested ResourceMark");}
}
#endifvoid* GenericGrowableArray::raw_allocate(int elementSize) {assert(_max >= 0, "integer overflow");size_t byte_size = elementSize * (size_t) _max;if (on_stack()) {return (void*)resource_allocate_bytes(byte_size);} else if (on_C_heap()) {return (void*)AllocateHeap(byte_size, "GrET in " __FILE__);} else {return _arena->Amalloc(byte_size);}
}

可以见得,标记存活的对象最后会放到一个二维数组里面,也就是指向存活的对象的指针放在一张连续的二维数组的地址块内。

这块地址区域就是极为重要的地址!

但是是如何一步步标记的呢?

Hotspot GC Root 对应调用链相关推荐

  1. Java垃圾回收(GC)、找垃圾的方式、GC Root、GC停顿、引用、垃圾收集算法、收集器、GC日志、安全点、安全区域

    1.垃圾回收 1.1概念 在Java语言中,垃圾回收(Garbage Collection,GC)是一个非常重要的概念. 它的主要作用是回收程序中不再被使用的内存,Java提供的GC功能可以自动监测对 ...

  2. 【Java 虚拟机原理】垃圾回收算法 ( 可达性分析算法 | GC Root 示例 | GC 回收前的两次标记 | finalize 方法示例 )

    文章目录 一.可达性分析算法 二.GC Root 示例 三.GC 回收前的两次标记 四.finalize 方法示例 一.可达性分析算法 在 堆内存 中 , 存在一个 根对象 GC Root , GC ...

  3. JVM中GC Root对象有哪些?

    众所周知,我们目前最常用的虚拟机hotspot使用可达性分析来进行垃圾回收,而可达性分析需要依赖GC Root.下面我就来介绍下可以作为GC Root的对象. (一)虚拟机栈中引用的对象 虚拟机栈中的 ...

  4. 使用 docker 构建分布式调用链跟踪框架skywalking

    一旦你的程序docker化之后,你会遇到各种问题,比如原来采用的本地记日志的方式就不再方便了,虽然你可以挂载到宿主机,但你使用 --scale 的话,会导致记录日志异常,所以最好的方式还是要做日志中心 ...

  5. 13、Nepxion Discovery 之 全链路调用链监控

    在进行微服务调用的时候,为了系统的高可用性,不仅需要进行灰度发布验证服务的可用性.同时对于服务健康的监控也是很重要的一环.Nepxion Discovery 在这方面也有监控方面的集成,包含以下几个方 ...

  6. vivo 调用链 Agent 原理及实践

    一.项目背景 2017年,vivo互联网研发团队认为调用链系统对实际业务具有较大的价值,于是开始了研发工作.3年的时间,调用链系统整体框架不断演进--本文将介绍vivo调用链系统 Agent 技术原理 ...

  7. [Java JVM] Hotspot GC研究- 64位引用指针压缩技术

    为什么需要指针压缩 在上一篇文章 [Java JVM] Hotspot GC研究- 开篇&对象内存布局 中介绍对象内存布局时, 曾提到过, 由于在64位CPU下, 指针的宽度是64位的, 而实 ...

  8. 什么是GC root ,GC root原理

    1.GC root原理 GC root原理:通过对枚举GCroot对象做引用可达性分析,即从GC root对象开始,向下搜索,形成的路径称之为 引用链.如果一个对象到GC roots对象没有任何引用, ...

  9. 小程聊微服务-自己动手扩展分布式调用链

    一.说在前面 微服务是当下最火的词语,现在很多公司都在推广微服务,当服务越来越多的时候,我们是否会纠结以下几个问题: 面对一笔超时的订单,究竟是哪一步处理时间超长呢? 数据由于并发莫名篡改,到底都谁有 ...

最新文章

  1. 利用指针的指针,修改被调函数的局部值【传引用】
  2. hiho一下 第四周 Hihocoder #1036 : Trie图
  3. 华为P50真机谍照曝光:璀璨粉色机身 牢牢锁定女性用户
  4. linux 0.11 返回用户侧,iPhone 11 11 Pro 用户必备教程:重启手机,恢复模式,DFU模式...
  5. Tomcat如果默认8080被占用修改端口号和查询端口号地址
  6. Eclipse调试时Application XXX is waiting for the debugger to attach的提示
  7. php数组的概述及分类与声明
  8. 循环数组最大子段和--51nod1050
  9. CWMP 开发漫谈
  10. 医学图像分类_深度学习与医学图像分析
  11. APP - 腾讯微信上线“一证通查”功能,还没查的速度看看
  12. python爬虫——利用超级鹰识别验证码并进行古诗网进行模拟登录
  13. 福大计算机课程表,福州大学研究生院-通知公告-福州大学课程表(非全日制工程硕士研究生2017年周末班公共课3-5月份 )...
  14. 刷新-读书笔记2(4-7章)
  15. python连乘函数_python 连乘
  16. 整理下使用yum localinstall做离线安装的点
  17. 欢聚时代(YY)面试
  18. 马士兵mysql_MYSQL相关总结(马士兵教育)
  19. MIT6.824-lab4B-Sharded Key/Value Server(基于Raft的Shard KV数据库-分片KV存储器)
  20. vs无法提示sourcetree的变基修改代码

热门文章

  1. 启动hiveserver2时报错Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient
  2. 数据结构:线性数据结构(3)-deques(栈,队列,deques, 列表)
  3. 斯坦福大学机器学习第一课“引言(Introduction)”
  4. ebs r12多少钱 实施oracle_甘肃室外儿童游乐设施报价,室外儿童游乐设施多少钱
  5. 根据文法画出语法树_编译工程5:语法分析(3)
  6. linux 字符串加入中括号,方括号及其在命令行中的不同用法介绍
  7. 微信小程序获取用户的 OpenId (附前端代码)
  8. Gartner:2020年企业中无“云”战略将极为罕见
  9. pip安装lxml报错
  10. iframe URI钓鱼