GC:你要用什么方式把我丢掉?

前言

大家都知道Java程序的运行离不开java虚拟机,而Java虚拟机(HotSport VM)运行时的数据区大致可分为五个部分,分别为JAVA栈、本地方法栈、堆、方法区、程序计数器,其中JAVA栈、本地方法栈、程序计数器是线程私有的,方法区和堆是线程共享区。
堆中一般保存着所有引用类型的真实信息(即对象)、字符产常量池、静态变量,以方便执行器执行。
而堆的空间是有限的,JVM虚拟机又有何保证存储空间的充足的呢?这就需要用的垃圾回收机制
本文主要讲解GC清理垃圾常用的四种算法

垃圾回收机制(GC)

当对象被创建时,就会在Java虚拟机的堆区拥有一块内存,在Java虚拟机的生命周期中,Java程序会陆续地创建无数个对象,加入所有的对象永久的占用内存,那么内存有可能很快被消耗光,最后引发内存空间不足的错误。因此必须采取一种措施来及时回收那些无用对象的内存,以保证内存可以被重复利用。

JAVA GC 机制,是Java与C++/C的主要区别之一,作为java开发者,一般不需要战门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题。

那GC常用的算法有哪些呢?

GC常用算法

  • 标记-清除算法
  • 标记-压缩算法
  • 复制算法
  • 分代收集算法

标记清除算法

Step 1:标记(Marking):

GC通过遍历内存区辨别哪些内存在使用,哪些内容没有使用。并做好标记。
在这里插入图片描述

引用的对象以蓝色显卡。未引用的对象以金色显示。在标记阶段扫描所有对象以进行此确定。如果必须扫描系统中的所有对象,这可能是一个非常耗时的过程。

Step 2:清除(Normal Deletion):
清除阶段移除掉对象垃圾,并且用一个链表维护空闲的区域
在这里插入图片描述

内存分配器持有空闲内存区的引用,以便分配内存给新的对象

优点:
每个活着的对象的引用只需要找到即可,找到一个就可以判断它为活得。不用移动对象的位置。
缺点:

  • 效率比较低(递归与全堆对象遍历),每个活着的对象都要在标记阶段遍历一遍。
  • 所有对象都要在清除阶段扫描一遍,算法复杂度高。
  • 没有移动对象,导致可能出现需要碎小的空间无法利用(可能导致空间利用率低)。

标记-压缩算法

基于标记-清除法的缺点—无法利用碎小的空间,提出了我们的标记-压缩算法。不同之处在于在第二个阶段,该算法并没有直接堆死亡的对象进行清理,而是将所有存活的对象进行整理,放在同意区域的另一处空间,然后把剩下的所有对象全部清除,这样就达到了标记-整理的目的。
在这里插入图片描述

通过一起移动引用的对象,这使得新的内存分配更容易和更快

优点:
不会产生大量的碎片空间
缺点:
如果存活的对象过多,整理阶段就会执行较多复制操作,导致算法效率低。

复制算法

复制算法与标记-压缩算法的不同之处在于不是在同一个区域进行复制,而是将所有存活的对象复制到另一个区域

该算法将内存平均的分为两个部分,然后每次使用其中的一部分,当这部分内存存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只是用这部分内存,然后循环。

在这里插入图片描述

优点:
实现简单,不产生内存碎片
缺点:
每次运行,总有一半内存是空的,导致内存的利用率只有50%。

分代收集算法

标记和压缩算法存在的问题
标记和压缩算法对java虚拟机而言会比较耗时。当java虚拟机分配了越来越多的对象之后,GC就会花费更多的时间。然而,绝大多数的对象存活时间都比较短。这样我们可以把存活时间长的对象和短的对象隔离开。GC就会拥有更高的效率。
在这里插入图片描述
Y轴显示分配的字节数,X访问显示随时间分配的字节数。
随着时间的推移,分配的对象越来越少。事实上,大多数物体的寿命都很短。

现在的虚拟机GC大多数采用分代收集算法,它根据对象的生存周期,将堆分为新生代和老年代。

分代收集算法思想:

  • 在新生代中,由于对象生存期短,每次回收都会有大量的对象死去,那么这时就采用复制算法。
  • 老年代里的对象存活率高,没有额外的空间进行分配,所以可以使用标记-清除或者标记-压缩进行清除。

分代收集过程

  1. 新生代(Young)分为Eden区。From和To区

在这里插入图片描述
2.几乎所有新的对象都会在Eden空间分配内存,两个survivor空间一开始都是空的。
在这里插入图片描述
3.对着对象的不断创建,Eden区被填满
在这里插入图片描述
4. 当Eden区满的时候,就会触发一次Minor GC(Young GC),也就是新生代的垃圾回收,所采用的是复制算法。
在这里插入图片描述

Eden空间的存活对象将被复制到第一个空间,年龄+1,S0中的1代表年龄。

5.新创建的依然会被放在Eden区,当再次被填满时,会再一次触发Minor GC,此时会同时将Eden区和S0区的存活对象都复制到S1区。
在这里插入图片描述
6.同样再一次触发Minor GC的时候,又将共同的存活对象赋值到S0区。
在这里插入图片描述

7.随着Minor GC的不断发生,随着在幸村区的不断交换,有的存活对象的年龄在不断增加,当幸存对象的年龄到达指定的阈值时,会被移动到老年代。(阈值由JVM参数决定,此处为8)。
在这里插入图片描述

8.当老年代的内存被填满时,将会触发Major GC (Full GC)进行老年代的内存清理。Majoe GC在老年代用的时标记-清除算法。同时新生代的对象将被清除。
在这里插入图片描述

做一次Major GC 要比进行一次 Minor GC时间更长,一般为10倍以上。

对象被放置到老年代的条件

一个对象被放置到老年代除了它的年龄达到阈值外,以下几种情况也会使得该对象直接被放置到老年代:

  • 对象创建后,无法放置到eden区(比如eden区的大小为10m,新的对象大小为11m,eden区不够放,触发YGC。YGC后eden区被清空,但还是无法容下11m的“超大对象”,所以直接放置到老年代。当然如果老年代放置不下则会触发Full GC,Full GC后处理依然放不下则OOM);
  • YGC后,对象无法放置到survivor To代也会直接晋升到老年代;
  • 如果survivor区中相同年龄的所有对象大小大于survivor区空间的一半,年龄大于或等于这些对象年龄的对象可以直接进入老年代,无需等到年龄阈值。

Major GC触发条件

  • 调用System.gc时,系统建议执行Major GC,但时不是必然执行(看系统心情)。
  • 老年代空间不足
  • 方法区空间不足
  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存。
  • 由Eden区,From Space区向To Space复制时,对象的大小大于To Space的可用内存,则将该对象转存至老年代,且老年代的可用内存小于该对象大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vivien_o.O

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值