前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GC算法[通俗易懂]

GC算法[通俗易懂]

作者头像
全栈程序员站长
发布2022-09-07 15:16:44
3410
发布2022-09-07 15:16:44
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

JVM(Java Virtual Machine)

GC是什么?

  • 频繁收集 Young 区
  • 较少收集 Old 区
  • 基本不动 Perm 区
GC算法[通俗易懂]
GC算法[通俗易懂]

JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代,因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)

  • 普通GC(minor GC):只针对新生代区域的GC
  • 全局GC(major GC or Full GC):针对年老代的GC,偶尔伴随对新生代的GC以及对永久代的GC。

GC的三大算法

其实还有一种:引用计数法,但是不用了。

GC算法[通俗易懂]
GC算法[通俗易懂]

复制算法:MinorGC(普通GC)

年轻代中使用的是 Minor GC,这种GC算法采用的是复制算法(Copying)

Minor GC会把Eden中的所有活的对象都移到 Survivor 区域中,如果 Survivor区中放不下,那么剩下的活的对象就被移到Oldgeneration中,也即一旦收集后,Eden是就变成空的了。

当对象在Eden(包括一个 Survivor区域,这里假设是from区域)出生后,在经过一次 Minor GC后,如果对象还存活,并且能够被另外一块 Survivor区域所容纳(上面已经假设为from区域,这里应为to区域,即to区域有足够的内存空间来存储Eden和from区域中存活的对象),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor区域(即to区域)中,然后清理所使用过的Eden以及 Survivor区域(即fom区域),并且将这些对象的年龄设置为1,以后对象在 Survivor区每熬过一次 Minor GC,就将对象的年龄+1,当对象的年龄达到某个值时(默认是15岁,通过 -XX:MaxTenuringThreshold 来设定参数),这些对象就会成为老年代。

解释: 年轻代中的GC,主要是复制算法(Copying) Hotspot JVM把年轻代分为了三部分:1个Eden区和2个 Survivor区(分别叫from和to)。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次 Minor GC 后,如果仍然存活,将会被移到Survivor区。对象在 Survivor区中每熬过一次 Minor GC ,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。 在GC开始的时候,对象只会存在于Eden区和名为“From”的 Survivor区,Survivor区”To“是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过 -XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到 “To”区域。经过这次GC后,Eden区和“From”区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为“To”的 Survivor区域是空的。Minor GC 会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

GC算法[通俗易懂]
GC算法[通俗易懂]

因为Eden区对象一般存活率较低,一般的,使用两块10%的内存作为空闲和活动区间,而另外80%的内存,则是用来给新建对象分配内存的。一旦发生GC,将10%(From)的活动区间与另外80%中存活的对象转移到10%(To)的空闲区间,接下来,将之前90%的内存全部释放,以此类推 。

口诀:

谁空谁是 to,复制要交换。

总结:

  1. 优点:不会产生内存碎片,完整度极高。
  2. 缺点:浪费了这10%(To)的内存空间。

复制算法弥补了标记/清除算法中,内存布局混乱的缺点。不过与此同时,它的缺点也是相当明显的

  1. 它浪费了一半(1:1的这个一半,就是那10%)的内存,这太要命了。
  2. 如果对象的存活率很高,我们可以极端一点,假设是100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍。复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变的不可忽视。所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%内存的浪费。

标记清除/标记整理算法:FullGC又叫 MajorGC(全局GC)

老年代一般是由标记清除或者是标记清除标记整理的混合实现

标记清除(Mark-Sweep)

GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]

缺点:

  1. 首先,它的缺点就是效率比较低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。
  2. 其次,主要的缺点则是这种方式清理出来的空闲内存是不连续的,这点不难理解,我们的死亡对象都是随机的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。而为了应付这一点,JVM 就不得不维持一个内存的空闲列表,这又是一种开销。而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

标记整理(Mark-Compact)

GC算法[通俗易懂]
GC算法[通俗易懂]

注释:上面标记的是活的。

GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]
GC算法[通俗易懂]

缺点:

  1. 标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记/整理算法要低于复制算法。

小结:

  • 内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)
  • 内存整齐度:复制算法=标记整理算法>标记清除算法
  • 内存利用率:标记整理算法=标记清除算法>复制算法

可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面所提到的三个指标,标记/整理算法相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/清除多了一个整理内存的过程。

难道就没有一种最优算法吗?

回答:无,没有最好的算法,只有最合适的算法。==========>分代收集算法(根据 JVM 不同的(新生、养老、永久)带,采用各自不同的算法)


面试题:

1. JVM内存模型以及分区,需要详细到每个区放什么。

2. 堆里面的分区:Eden,survival from to,老年代,各自的特点。

3. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别阐述。

4. Minor GC 与 Full GC 分别在什么时候发生。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/148274.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GC是什么?
  • GC的三大算法
    • 复制算法:MinorGC(普通GC)
      • 口诀:
      • 总结:
    • 标记清除/标记整理算法:FullGC又叫 MajorGC(全局GC)
      • 标记清除(Mark-Sweep)
      • 标记整理(Mark-Compact)
  • 小结:
  • 面试题:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档