垃圾收集器与内存分配策略(二)

垃圾收集器与内存分配策略(二)

垃圾收集算法

标记清除

这个很好理解,整个过程分为标记和清除两个阶段。标记上一章已经说过了,在标记结束后统一进行清除。


标记清除

缺点:
(1) 效率不高
(2) 内存中存在大量的内存碎片,如果后续有大的对象生成,可能无法找到足够的连续空间,从而提前触发垃圾回收。

复制算法

把内存分为平均的两部分,平时只使用其中一部分A,回收时,将还存活的对象全部复制到另一个部分B,然后A整个回收掉就可以了。


复制算法

注意的问题

  • 50%的内存是不用的,太浪费了
    商业上不会使用1比1的比例。HotSpot将内存分为一块较大的Eden和两块较小的Survivor空间。每次使用Eden和其中一块Survivor空间。回收时讲Eden和Survivor还存活的对象复制到另一块Survivor空间。HotSpot默认Eden和Survivor的比例是8:1.
  • 如果回收时发现Survivor不够放存活的对象怎么办
    需要老年代进行分配担保,即当Survivor不够放的话,剩下的对象直接放入老年代。
标记整理

和标记清除类似,在回收时,将还存活的对象向一端移动。


标记整理
分代收集算法

根据对象的存活周期,分为年轻代和老年代。不同年代的对象采用不同的垃圾收集算法。

  • 显然复制算法适合年轻代。因为年轻代对象的存活率很低,复制的对象的个数很少。
  • 标记清除和标记整理适合老年代。老年代对象的存活率很高,复制算法会复制的次数太高,效率不高,而使用标记清除和标记整理算法。

Hotspot的算法实现

枚举根节点

这一节主要是针对如何找到GC Root对象?

  • 现有的问题
    (1) 我们知道可作为GC Root对象的有全局性引用(static/常量)和执行上下文(栈中局部变量引用)中,但是这块地方有时可达几百M,依次遍历其中每个数据是否是引用,效率太低。
    (2) 且我们知道在进行GC Root枚举时,必须stop the world来保证引用关系不再改变。所以这也要求GC Root的枚举不能太耗时。
  • HotSpot的解决办法
    使用一组叫做OopMap的数据结构。
    (1) 记录每个类的每个数据位置(通过偏移量来确定)的类型(即是否是引用)。这一步在类加载后即可确定。
    (2) 在特定的位置上记录栈和寄存器中哪些位置是引用。
    这样我们在GC Root枚举时,可以快速找到所有引用。
安全点
  • 程序执行时并非在任何地方都能停顿下来GC。
    只有在安全点的位置上才能停顿下来,原因是因为只有安全点上才有全部的OopMap信息(就是上面说的“特定位置”),即只有安全点才能安全地进行GC Root枚举。
    安全点的选取由程序本身结构决定。不能太多也不能太少。一般在方法调用、循环跳转、异常跳转等。
  • 还有一个问题:如何在发生GC时,让所有的线程都跑到最近的安全点停下来?
    两种方式:抢占式和主动式
    抢占式:在GC发生时,中断所有线程,如果发现有的线程不在安全点,则重新唤醒知道跑到最近的安全点。--这种方式几乎没人使用
    主动式:线程主动发起。在GC发生时,在安全点位置上设置标记位,线程执行到安全点时轮询这个标志位,如果发现为true,则中断。

安全点很好地解决了如何进入GC的问题。

安全区
  • 安全点无法解决的问题
    当一个线程没有处于执行状态:就绪(等待分配CPU时间)。此时如果要发生GC,如何让这个线程进入安全点?总不能一直等,知道CPU分配给它时间吧。
  • 解决办法
    引入安全区的概念,即一段代码内无引用关系改变。线程进入安全区代码后,标记自己“安全区”标记,GC发生时就不用管标记“安全区”的线程了。当线程想要离开安全区时,判断GC是否结束,如果没结束要等GC结束后才能继续执行。

上面这段文章讲的是虚拟机如何发起内存回收,下面讲具体如何回收。

垃圾收集器

垃圾回收器
Serial

单线程收集器。即只有一个线程去垃圾回收工作。必须先进行stop the world,然后进行回收工作。
算法:由于是新生代,采用复制算法。
特点:单线程简单高效,多线程下效率不高。且必须stop the world。


Serial收集器
ParNew

和Serial唯一的区别就是使用多线程去垃圾回收工作。其他和Serial完全一样。
特点:有一个不和性能有关的优点:能和CMS配合的最好新生代收集器。


ParNew收集器
Parallel Scavenge

也是多线程去垃圾回收工作。算法也是复制算法。
它和别的收集器主要有两点不同:

  • 关注点不同
    其他收集器关注:如何让用户的线程停顿时间越短越好。
    它关注:如何让CPU的吞吐量最高。
    吞吐量=(CPU执行代码的时间)/(CPU执行代码的时间+CPU GC的时间)
    也被称为:吞吐量优先收集器
  • 自适应
    它无需用户设置Eden与Survivor的比例,进入老年代年龄参数,它可以动态调整,以达到最高吞吐量。
    也被称为:自适应调节策略。
Serial Old

是Serial的老年代版本。单线程+标记整理算法。

Parallel Old

是Parallel Old的老年代版本。多线程+标记整理算法。

CMS
  • 说明
    一个真正意义上的并行收集器:用户线程和GC线程可以同时运行。
    目标是尽可能缩短GC停顿时间的回收器。多用于B/S模型中,用户感知的停顿时间很小。
    算法:使用标记-清除算法。
  • 分为四个步骤:三标记一清除
    (1) 初始标记:标记GC Root直接关联的对象。
    (2) 并发标记:进行GC Root的trace过程,进行可达性分析。
    (3) 重新标记:修正(2)时,由于用户线程也在运行导致的引用关系变化。
    (4) 并发清除:就是清除的过程。

    (1)和(3)需要stop the world,但(1)和(3)的消耗时间非常短。
    (2)和(4)为并发,即可以和用户线程同时运行。消耗时间相较于1和3较长。但由于是并行,所以整个收集器看来就是和用户线程并行运行。
    CMS过程
  • CMS的三大缺点
    • 由于2和4需要CPU进行GC,所以用户线程的可用CPU变少了。
    • 并发清除阶段引起:提前Full GC和引发Concurrent Mode Failure问题。
      • 提前Full GC
        由于用户线程也在运行,所以清除时,必须留着内存给正在运行的用户线程使用。所以无法像其他收集器一样,老年代用满了才进行GC,CMS在老年代在65%就要开始GC了。
      • Concurrent Mode Failure
        由于用户线程也在运行且用户线程需要的内存未知,如果此时内存无法满足用户线程的需要,则就会引起CMF。进而引发虚拟机启动备选预案:使用Serial Old来进行GC,这就要花很长时间了。
    • 标记-清除算法自身的缺点:存在空间碎片,容易导致大对象无法找到连续的内存分配,而导致提前Full GC。
G1

jdk1.7中最具先进的收集器。

  • 有几个特点
    • 并行
      即整体看来,可以和用户线程并行执行
    • 分代收集
      前面说的收集器只针对某个年轻代或老年代,它G1可以管理整个堆。
    • 空间整合
      整体来看是标记-整理算法。从局部(Region)看是复制算法。总而言之,不会有空间碎片。
    • 可预测的停顿
      这是G1一个很大的特点:即用户可以指定在一个M长的时间片内,GC的停顿时间不超过N秒。
  • G1是基于Region来进行内存管理的。
    G1收集器的堆的分布已经和其他收集器不一样了。G1将堆分为大小相等的Region。仍然有年轻代和老年代的概念,但两者没有物理界限,都是一连串的Region集合。
  • 可预测的停顿实现方法
    G1会跟踪每个Region的回收价值(回收得到的空间以及GC时间),维护一个优先列表。每个GC时,在用户指定的时间内,找到回收价值最高的进行回收。
  • 如何解决跨Region引用来引起的全局堆引用扫描问题
    我们知道引用可能发生在跨Region之间,这个问题不仅仅是G1的问题,在其他收集器中也存在年轻代引用老年代的问题。而虚拟机的解决办法是Remembered Set,即每次赋值引用时,记录这个引用否引用其他Region(或老年代),如果是则在Remembered Set记录其他Region(或老年代)。保证不会圈堆扫描。
  • G1的具体步骤
    • 初始标记
    • 并发标记
    • 最终标记
    • 筛选回收
      前三个阶段几乎和CMS一样。最后的筛选回收:在运行的GC停顿时间内,选择回收价值最高的Region回收。这个阶段虽然可以和用户线程并行,但实际上由于所花费时间短,且停顿用户线程这个时间会更短,所以采用停顿用户线程的方式。


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

推荐阅读更多精彩内容