JVM 垃圾回收机制

新生代和老年代

堆内存被划分为新生代和老年代,其中新生代用于临时对象的分配和垃圾回收,而老年代用于长时间存活的对象。

  1. 新生代(Young Generation):
    • 新生代是对象被创建和短暂存在的区域。它被进一步划分为三个部分:Eden空间和两个Survivor空间(通常称为S0和S1)。
    • 当对象被创建时,它们首先被分配到Eden空间。在新生代回收后,如果对象还存活,则进入s0或s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。
  2. 老年代(Old Generation):
  • 老年代是存放长时间存活的对象的区域。它通常比新生代更大。

判断对象是否可以被回收

   1. 引用计数法

在引用计数法中,每个对象都维护一个计数器,用于记录当前有多少个引用指向该对象。

当一个对象被引用时,它的计数器加一;当一个引用指向对象被销毁或不再指向该对象时,它的计数器减一。当对象的计数器变为零时,表示没有任何引用指向该对象,即该对象不再被使用,可以被回收。由于循环引用问题的存在,引用计数法通常不是主流的垃圾回收判断方法。

   2. 可达性分析算法

可达性分析算法(Reachability Analysis Algorithm)是一种主流的垃圾回收算法,用于确定哪些对象是可达的(被程序中的其他对象引用)并且不会被回收,以及哪些对象是不可达的(没有被引用)并且可以被回收。

可达性分析算法的基本思想是从一组称为"根(Roots)"的起始对象开始,通过遍历对象之间的引用关系,标记所有可达的对象。标记过程中,所有被访问到的对象都被标记为可达,而未被访问到的对象则被标记为不可达。

在标记完成后,垃圾回收器将遍历整个堆内存,回收所有未被标记为可达的对象,释放它们占用的内存空间。这些不可达的对象被认为是不再被程序使用的垃圾对象。

垃圾回收算法

1. 标记清除算法(适用于老年代)

标记清除算法(Mark-Sweep)是一种常见的垃圾回收算法,用于回收不再使用的对象。它由两个主要阶段组成:标记阶段(Mark)和清除阶段(Sweep)。

  1. 标记阶段(Marking phase):

    • 从根对象开始,通过对象之间的引用关系进行遍历,标记所有可达的对象。
    • 在遍历过程中,被访问到的对象都被标记为可达。标记可以通过不同的方式实现,常见的方法是在对象头部添加一个标记位或使用一个独立的位图来表示对象是否被标记。
  2. 清除阶段(Sweeping phase):

    • 在清除阶段,垃圾回收器遍历整个堆内存,回收所有未被标记为可达的对象,即不可达对象。
    • 对于每个未被标记的对象,垃圾回收器会将其内存空间标记为可用的,并将其加入到空闲列表中,以便后续的内存分配使用。

标记清除算法的优点包括:

  • 能够回收不连续分布的内存碎片:由于标记清除算法只关注对象的可达性,而不需要移动对象,因此可以处理非连续的内存分配,避免了内存碎片的产生。
  • 相对简单:相对于其他高级的垃圾回收算法,标记清除算法实现相对简单。

然而,标记清除算法也存在一些缺点:

  • 内存碎片问题:由于标记清除算法不会对内存空间进行整理,可能导致内存碎片的增加。这可能会影响到后续的内存分配操作,导致效率下降。
  • 垃圾回收过程中的停顿:回收时,应用需要挂起,也就是stop the world。扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)


 

2.复制算法(适用于新生代)

复制算法的基本思想是将堆内存划分为两个大小相等的区域,通常称为"From"空间和"To"空间。在初始状态下,所有的存活对象都位于From空间中。

执行垃圾回收时,复制算法会遍历From空间中的存活对象,并将它们复制到To空间中。复制过程中,对象的布局会保持原样,但对象之间的相对顺序可能会改变。

复制完成后,From空间中的存活对象已经被复制到To空间中,而From空间中未被复制的对象则被认为是不可达的垃圾对象。此时,整个From空间可以被视为一片连续的可用内存,无需进行复杂的内存碎片整理。

复制算法的优点包括:

  • 消除内存碎片:由于复制算法在回收过程中将存活对象复制到新的内存空间,它可以有效地消除内存碎片问题。
  • 简单高效:复制算法的实现相对简单且高效,适用于管理小到中等大小的堆内存

标记-复制算法,在这个基础之上对其进行了优化,IBM曾有过一项针对新生代的研究,结论是绝大多数情况下,新生代区域里的对象有98%都熬不过第一次回收。

所以不需要按照 1 : 1 的比例来实现复制算法,而是可以按照 8 : 1 : 1 的比例来分配内存空间,也就是一个80%的Eden空间和两个10%的Survivor空间。

每次分配内存,只使用Eden和其中一块Survivor空间,发生GC回收时,把Eden和其中一块Survivor空间中存活的对象,复制到另一块空闲的Survivor空间,然后直接把Eden和使用过的那块Survivor空间清理掉。

3. 标记-整理算法(适用于老年代)

标记-整理算法的基本流程如下:

  1. 标记阶段(Marking phase):

    • 从根对象出发,通过对象之间的引用关系进行遍历,标记所有可达的对象,将其标记为存活对象。
  2. 整理阶段(Compacting phase):

    • 在整理阶段,垃圾回收器会重新组织存活对象,将它们紧凑地排列在一起,以便形成一块连续的内存空间。
    • 整理的过程中,存活对象会被依次移动到内存空间的一端,同时保持它们之间的相对顺序不变。这样,所有存活对象都会被紧凑地排列在一起,形成连续的内存块。
  3. 更新引用关系:

    • 在整理过程中,由于存活对象的移动,原本指向存活对象的引用需要更新为新的地址。这确保了对象之间的引用关系依然有效。
  4. 清理阶段(Sweeping phase):

    • 在整理完成后,垃圾回收器会清理整理阶段末尾的未被覆盖的内存空间,这些空间中的对象被认为是不可达的垃圾对象。
    • 清理操作可以简单地将整理阶段剩余的内存空间标记为空闲,供后续的内存分配使用。

4. 分代算法

根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

Minor GC和Full GC区别

新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。 老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。

  • Minor GC触发机制:
    • 当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC
  • Full GC触发机制:
    • 当老年代满时会引发Full GC,Full GC将会同时回收年轻代、老年代。
  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值