JVM之垃圾回收算法

一、java是如何实现垃圾回收的呢?

简单来说,垃圾回收要做的有两件事情:

1、找到内存中存活的对象

2、释放不再存活对象的内存,使得程序能再次利用这部分空间

二、垃圾回收算法的历史:

  • 1960年John McCarthy发布了第一个GC算法:标记-清除算法。
  • 1963年Marvin L.Minsky发布了复制算法。

本质上后续所有的垃圾回收算法,都是在上述两种算法的基础上优化而来

三、垃圾回收算法分类:

1、标记-清楚算法

2、复制算法

3、标记-整理算法

4、分代GC(分代收集算法)

垃圾回收算法的评价标准:

java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为Stop The World简称STW,如果STW时间过长则会影响用户的使用。

所以判断GC算法是否优秀,可以从三个方面来考虑:

1、吞吐量:

吞吐量指的是CPU用于执行用户代码的时间与CPU总执行时间的比值,即吞吐量=执行用户代码时间/(执行用户代码时间+GC时间)。吞吐量数值越高,垃圾回收的效率就越高。

比如:虚拟机总共运行了100分钟,其中GC花掉1分钟,那么吞吐量就是99%.

2、最大暂停时间:

最大暂停时间指的是所有在垃圾回收过程中的STW时间的最大值,比如下图中,黄色部分的STW就是最大暂停时间,显而易见上面的图比下面的图拥有更少的最大暂停时间。最大暂停时间越短,用户使用系统时受到的影响就越短。

很明显可以看出,在最大暂停时间上,上面的这种情况是优于下面的这种情况。

3、堆使用效率:

不同垃圾回收算法,对堆内存的使用方式是不同的。比如标记清除算法,可以使用完整的堆内存。而复制算法会将堆内存一分为二,每次只能使用一半内存。从堆使用效率上来说,标记清除算法要优于复制算法。

上述三种评价标准:堆使用效率、吞吐量,以及最大暂停时间不可兼得。

一般来说,堆内存越大,最大暂停时间就越长。想要减少最大暂停时间,就会降低吞吐量。

不同的垃圾回收算法,适用于不同的场景。

垃圾回收算法—标记清除算法

标记清除算法的核心思想分为两个阶段:

1、标记阶段,将所有存活的对象进行一个标记。java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象。

2、清除阶段、从内存中删除没有被标记也就是非存活对象。

优缺点:

优点:实现简单,只需要在第一阶段给每个对象维护标志位,第二阶段删除对象即可。

缺点:

1.碎片化问题

由于内存是连续的,所以在对象被删除之后,内存中会出现很多细小的可用内存单元。如果我们需要的是一个比较大的空间,很有这些内存单元的大小无法进行分配

总共回收了9个字节,但是无法为5个字节对象分配出合适的内存

2、分配速度慢:

由于内存碎片的存在,需要维护一个空闲链表,极有可能发生每次需要遍历到链表的最后才能获得合适的内存空间

垃圾回收算法—复制算法

复制算法的核心思想是:

1、准备两块空间From空间和To空间,每次在对象分配阶段,只能使用其中一块空间(From空间)。

2、在垃圾回收GC阶段,将From中存活对象复制到To空间。

3、将两块空间的From和To名字互换。

一个完整复制算法的例子:

1、将堆内存分割成两块From空间To空间,对象分配阶段,创建对象

2、GC阶段开始,将GC Root搬运到To空间

3、将GC Root关联的对象,搬运到To空间

4、清理From空间,并把名称互换

复制算法的优缺点:

优点:

1、吞吐量高:复制算法只需要遍历一次存活对象复制到To空间即可,比标记-整理算法少了一次遍历的过程,因而性能较好,但是不如标记-清除算法,因为标记清除算法不需要进行对象的移动

2、不会发生碎片化:复制算法在复制之后就会将对象按顺序放入To空间中,所以对象以外的区域都是可用空间,不存在碎片化内存空间。

缺点:

内存使用效率低:每次只能让一半的内存空间来为创建对象使用

垃圾回收算法—标记整理算法

标记整理算法也叫标记压缩算法,是对标记清理算法中容易产生内存碎片问题的一种解决方案。

核心思想分为两个阶段:

1、标记阶段,将所有存活的对象进行标记。java中使用可达性分析算法,从GC Root开始通过引用链遍历出所有存活对象。

2、整理阶段,将存活对象移动到堆的一段。清理掉存活对象的内存空间

标记整理算法的优缺点

优点:

1、内存使用效率高:整个堆内存都可以使用,不会像复制算法只能使用半个堆内存

2、不会发生碎片化:在整理阶段可以将对象往内存的一侧进行移动,剩下的空间都是可以分配对象的有效空间

缺点:

整理算法有很多种,比如Lisp2整理算法需要对整个堆中的对象搜索3次,整体性能不佳。可以通过Two-Finger、表格算法、ImmixGC等高效的整理算法优化此阶段的性能

垃圾回收算法—分代垃圾回收算法

现代优秀的垃圾回收算法,会将上述描述的垃圾回收算法组合进行使用,其中应用最广的就是分代垃圾回收算法(Generational GC)

分代垃圾回收将整个内存区域划分为年轻代(Eden(伊甸园),S0,S1)和老年代,年轻代存放存活时间比较短的对象,老年代存放存活时间比较长的对象

arthas查看分代之后的内存情况

  • 在JDK8中,添加-XX:+UseSerialGC参数使用分代回收的垃圾回收期,运行程序。
  • 在arthas中使用memory命令查看内存,显示出三个区域的内存情况。

调整内存区域的大小

根据以下虚拟机参数,调整堆的大小并观察结果。注意加上-XX:+UseSerialGC

分代回收时,创建出来的对象,首先会被放入Eden伊甸园区。

随着对象在Eden区越来越多,如果Eden区满,新创建的对象已经无法放入,就会触发年轻代的GC,成为Minor GC或者Young GC。

Minor GC会把需要Eden中和From需要回收的对象回收,把没有回收的对象放入To区。

接下来,S0会变成To区,S1变成From区。当Eden区满时再往里放入对象,依然会发生Minor GC。

此时会回收Eden区和S1(From)中对象,并把Eden和From区中剩余的对象放入S0.

注意:每次Minor GC中都会为对象记录他的年龄,初始值为0,每次GC完加1

如果Minor GC后对象的年龄达到阈值(最大15,默认值和垃圾回收器有关),对象就会被晋升至老年代。

当老年代中空间不足,无法放入新的对象时,先尝试Minor GC如果还是不足,就会触发Full GC,Full GC会对整个堆进行垃圾回收

如果Full GC依然无法回收掉老年代的对象,那么当对象继续放入老年代,就会抛出Out Of Memory异常。

到这里就结束啦,感谢各位小伙伴的观看!!

这里给大家留一个疑问:

为什么分代GC算法要把堆分为年轻代和老年代?

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值