为什么跨代引用是GC root

昨日,有人在一个JVM群里问了一个问题,为什么跨代引用是gc root。这虽然是一个很简单的问题,但是其实涉及到了分代垃圾回收算法的核心理念。

gc root的基本解释

首先我们要理解一下GC root究竟是什么东西。

gc root

堆是被我们垃圾回收所管理的内存空间。如图,存在两种引用,一种是堆外对象对堆内对象的引用,被标注为红色;另外一种是堆内对象之间的引用,被标注为灰色。通常我们说的gc root就可以被认为是红色的那种引用,比如说栈引用堆中对象。为什么我们不认为堆内对象之间的引用是gc root呢?因为我们的对象,最终是要被外部使用的,比如说被栈引用所访问。因此,如果一大堆的堆内对象之间互相引用,但是没有任何堆外部引用,那么这部分对象实际上也是不可达的。HotSpot就是如此的,所有的堆中的对象,最终都是被栈所使用的。因而,U和V就可以看做是不可达的对象了。

分代和跨代引用

解释了gc root的基本概念后,我们要来看看分代理论了。基本上,现代垃圾回收器都是分代垃圾回收器,它建立在两个分代理论之上:

  • 弱分代假说(weak generational hypothesis):大多数对象在年轻的时候死亡;
  • 强分代假说(strong generational hypothesis):越老的对象越难死亡;

这个分代假说引申出一种垃圾回收理念:将对象依据“年龄”分配到不同的区域,每次回收只回收其中的一个区域。这也就是分代回收的基础理念。因为很显然的,如果大部分对象都是朝生夕死的,那么将它们放在一起,每次回收都能够回收到很多的空间;剩下的不容易死亡的对象,放在一起,那么可以以一种极为低的频率来回收它们。这就兼顾了垃圾回收的时间开销和内存的空间利用率。

一般的垃圾回收算法至少会划分出两个年代,年轻代和老年代。但是单纯的分代理论在垃圾回收的时候存在一个巨大的缺陷:为了找到年轻代中的存活对象,却不得不遍历整个老年代,反过来也是一样的。

跨代引用引起老年代的遍历

如果我们从年轻代开始遍历,那么可以断定N, S, P, Q都是存活对象。但是,V却不会被认为是存活对象,其占据的内存会被回收了。这就是一个惊天的大漏洞!因为U本身是老年代对象,而且有外部引用指向它,也就是说U是存活对象,而U指向了V,也就是说V也应该是存活对象才是!而这都是因为我们只遍历年轻代对象!

所以,为了解决这种跨代引用的问题,最笨的办法就是遍历老年代的对象,找出这些跨代引用来。这种方案存在极大的性能浪费。因为从两个分代假说里面,其实隐含了一个推论:跨代引用是极少的。也就是为了找出那么一点点跨代引用,我们却得遍历整个老年代!从上图来说,很显然的是,我们根本不必遍历R。

因此,为了避免这种遍历老年代的性能开销,通常的分代垃圾回收器会引入一种称为记忆集的技术。简单来说,记忆集就是用来记录跨代引用的表。

记忆集记录跨代引用

如图,在拥有记忆集的情况下,我们就可以不用遍历老年代了,这是一个巨大的性能提升!

最终解释

现在,我们设想一下,要回收年轻代,首先我们要从引用年轻代对象的外部引用开始;其次,我们要从跨代引用开始。于是我们可以很自然的得出结果:跨代引用也是gc root。

整个模型可以抽象成:


gc root的最终解释

附录

在引入记忆集之后,其实会有一个很有意思的问题:即老年代对象即便已经事实上不可达了,但是因为记忆集的存在,会导致从该对象出发的跨代引用依旧会被当成gc root,直至该对象被回收引起记忆集中相关条目的擦除。

记忆集引出的问题

如图,U已经不存在外部引用了,所以它事实上已经不可达了。但是在这个时刻,因为老年代没有发生GC,所以它依旧存活着。

  • 如果我们采用遍历老年代的方法找出跨代引用,那么我们只能找到S->P这一条。于是U和V都会被当成是不可达对象,其内存空间就可以被回收掉了。
  • 如果我们使用记忆集,那么因为U没有被GC掉,所以记忆集里面的条目U->V依旧存在,所以在年轻代回收的时候,V会被当成存活对象。

这个问题就是因为使用记忆集带来的“滞后性”,它提高了时间效率,但是却降低了空间利用率。不过无论如何,它依然确保了垃圾回收所遵循的原则:垃圾回收确保回收的对象必然是不可达对象,但是不确保所有的不可达对象都会被回收

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容

  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供...
    简欲明心阅读 89,492评论 17 311
  • JVM架构 当一个程序启动之前,它的class会被类装载器装入方法区(Permanent区),执行引擎读取方法区的...
    cocohaifang阅读 1,664评论 0 7
  • 1.一些概念 1.1.数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始...
    落落落落大大方方阅读 4,540评论 4 86
  • 一. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对...
    Stan_Z阅读 1,931评论 0 25
  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 5,962评论 2 31