【gc】垃圾收集器以及垃圾收集算法

垃圾收集器

可以根据分代不同,设定不同的收集器。有时候需要分别为新生代或老年代选用合适的收集器。

一般来说,新生代收集器的收集频率较高,应选用性能高效的收集器;而老年代收集器收集次数相对较少,对空间较为敏感,应当避免选择基于复制算法的收集器

1 Serial收集器

  1. 【新生代】 收集器
  2. 采用复制算法
  3. 单个GC线程执行垃圾回收
  4. 垃圾回收时,用户线程暂停

2 CMS收集器

  1. 并发标记清楚收集器(CMS的GC线程可以和用户线程一起工作),因此可以获得最短回收停顿时间。

2.【老年代】收集器
3. 基于标记-清除实现的。

CMS的步骤:
  • 初始标记:暂停【用户程序】,标记GC Roots能直接关联到的对象,速度很快,需要“Stop The World”。
  • 并发标记:GC RootsTracing 【GC和用户】线程并发,闭包结构记录【可达对象】
  • 重新标记: 需要“Stop The World”。【用户程序】在【并发标记】可能更新【引用域】,所以【暂停用户程序】修正对象【可达性】的变化。
  • 并发清除:【GC】线程和【用户程序】并发,GC线程【清除可回收对象】

和串行的一样,将堆结构化分成三个部分:年轻代、老年代、固定内存大小的永久代。

stop the world以及相关原因:收集的时候,需要暂停用户程序。

CMS优点(为什么可以实现并发)

使用标记-清除的方法,对收集过程进行了细粒度的分解,然后以流水线方式拆分了收集周期。将耗时长的操作单元保持与应用线程并发执行。只将那些必需STW才能执行的操作单元单独拎出来,控制这些单元在恰当的时机运行,并能保证仅需短暂的时间就可以完成。

CMS缺点
  1. 【cpu资源敏感】 : 并发期间,GC程序占用CPU资源,导致用户程序吞吐量低。
  2. 【无法处理浮动垃圾】 : 【并发清除】阶段,【GC和用户】程序并发,在该阶段仍会产生垃圾。这些垃圾仅能在下一次GC中回收。
  3. 【标记-清除】算法,造成 大量【不连续】空间碎片(怎么并发清除的?)

3 G1收集器

在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不是。

G1引入了region概念,每个region都是连续的虚拟内存范围。region中又分成了eden, suvivor, old。
region的大小可以在JVM启动的时候选择。大约两千个region。

这样收集的时候,不需要在全堆范围内执行。

G1优点
  1. 并发 : 利用多核处理器,既实现用户程序停顿时间短,又一定程度保障CPU吞吐量。
  2. 分代收集 : G1收集器【无需其他收集器配合】,独立管理堆。但存在分代收集概念
  3. 空间整合 : G1收集器 【整体】采用 【标记-整理】,【局部】采用 【复制算法】。不产生空间碎片
  4. 可预测停顿: G1建立预测模型,预测停顿时长

可预测的停顿时间模型: 可以【划分region】收集,因此可避免在【整个Java堆】中进行全区域的垃圾收集。该模型能算出每个Region的收集成本并量化,因此【收集器给定停顿时间后】,可以选择恰当的region作为收集目标。

G1流程
  1. 初始标记 : 【暂停用户程序】,标记和root相连的对象
  2. 并发标记 : 【GC和用户】程序并发,GC程序 标记 可达对象
  3. 最终标记 : 修正 【并发标记】阶段,【用户程序更新引用域】所 引起的【可达性】变化
  4. 筛选回收 : 对各个Region的【回收价值和成本进行排序】,根据用户所期望的GC停顿时间来制定回收计划(与用户程序并发)。只回收一部分Region,时间是用户可控制的。

相比于CMS:消除了碎片,简化收集器的各个部分。但是无法像CMS一样让回收线程和用户线程并发进行。回收时候需要暂停用户线程

GC时为什么要暂停用户线程?首先,如果不暂停用户线程,就意味着期间会不断有垃圾产生,永远也清理不干净。其次,用户线程的运行必然会导致对象的引用关系发生改变,这就会导致两种情况:漏标和错标。

每个region区域进行单独垃圾回收。记录每个region垃圾回收的时间以及回收获得的空间,维护一个优先列表。每次优先回收价值最大的Region

执行流程:

对象一开始优先进入Eden中。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Young GC

经过一次垃圾回收后,存活下来的被放到suvivor0, suvivor1中,并且年龄+1
年龄达到一定的预置(默认为15)。

另外,大对象先进入老年区(因为大对象的复制会消耗很多)

Old GC
如果统计信息说,young gen待晋升(要移动到old gen)的数据比old gen空间大,则会出发full GC

垃圾收集的算法(其中两个)

标记-清除算法: 当内存区域满了的时候,将依旧存活的对象做标记,删除所有没有标记的对象(将产生大量的内存碎片,因此对新生代是很难接受的)

标记-复制: 将内存分成大小相同的两块。每次只使用其中一块。当其中的一块满了的时候,将存活的变量复制到另一块内存中,删除之前的整个内存。

标记-整理:和标记清除一样,但是后续操作不是直接对可回收的对象进行清理。而是让所有存活的对象都向一端移动。然后直接清除掉端边界以外的内存。

对于新生代,每次收集会死亡大量的对象,所以适合用标记-复制,对于老生代,变量占内存大,复制代价高,并且存活率高。所以适合用标记-清除

safe point

程序在safe point的时候才能stop下来,进行GC。

安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java虚拟机的堆栈不会发生变化。

gc发生时,会设置一个标志。线程每到安全点的时候,回去轮训终端标志,如果是true, 就会中断。

安全区域:

指在一段代码片段中,引用关系不会发生变化。在这个区域中任意地方开始GC都是安全的。也可以把Safe Region看作是被扩展了的Safepoint。

参考

1. 有道云笔记
2. 掘金文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值