JVM垃圾回收机制(GC)

JVM垃圾回收机制(GC)

引入:我们都知道,栈内存中方法运行完毕后会有弹栈的操作,不会产生垃圾,而堆内存中却没有这种操作,当堆内存中很多无用的成员变量、对象等等积压到一定程度时,就会发生堆内存溢出的一个错误OutOfMemoryError (Java heap space)堆内存溢出 ,虽然说堆内存的大小是可以调节的,但是它还是解决不了根本问题。那么为了避免这种情况的发生,出现了垃圾回收机制,也就是我们所说的GC。

堆内存的结构

堆内存逻辑上是分为三个部分:新生代、养老代、永久代(jdk1.7以后我们称永久代为元空间)。
在这里插入图片描述
但是实际上负责存储的只有:新生代、养老代。
在这里插入图片描述

回收机制介绍

对象在伊甸区(Eden)被new出来,当伊甸区满了以后还需要创建对象,这时候JVM会对伊甸区进行垃圾回收(YGC也叫轻GC),将伊甸区中的没有被其他对象所引用的对象进行销毁(finalize()方法用于销毁对象的)。然后将伊甸区中存活的对象移动到幸存0区,并且该对象年龄为1,当伊甸区再满了之后会对伊甸区和幸存0区的对象进行GC,然后会将伊甸区和幸存0区存活的对象移动到幸存1区,如果当前存活的对象GC前是幸存区的,那么他们的年龄+1,就这样GC一次交换一次年龄增长一次,如果有对象经过了15次GC依然存活(15岁),会被转移到养老区(Old),当养老区满了之后也会进行垃圾回收(Full GC也叫重GC),对养老区进行垃圾清理,当最后没有可清理的垃圾时且新生代、养老代都满了之后,会报一个异常:OutOfMemoryError (Java heap space)

异常原因:
代码中创建了大量的对象,且长时间不能被回收,导致创建新对象出现堆内存溢出。

关于新生代GC之后有交换,谁空谁是To的图解

在这里插入图片描述
文字解释:第一次GC会把存活的对象存入幸存0区(当前的Form区),第二次GC会把所有存活的对象存入幸存1区(当前From区),第三次GC会把所有存活的对象存入幸存0区(当前From区),你会发现每次有对象的哪个区就是From区,没有对象的那个区就是To区。
这样就验证了上面那句:GC之后有交换,谁空谁是To

解读GC日志

从百度上扒拉了两个GC图,希望可以帮到你。
简称Young GC 为 YGC
简称Full GC 为 FGC
Young GC
在这里插入图片描述
Full GC在这里插入图片描述

GC算法

  • 引用计数法(已经不用了)
    缺点:老算法,比较消耗内存,比较难处理循环引用。

  • 复制回收算法:(GC 90%用的都是这个,这个算法一般用在YGC就是我们的新生代)
    优点:不会产生内存碎片,效率高。
    缺点:浪费内存空间。
    最经典的体现在这句话:GC之后有交换,谁空谁是To。 里面所提到的交换其实就是在一次GC之后,把剩余存活的对象复制到下一个幸存区,从而就导致了必须要有两个幸存区的概念 (幸存0区和幸存1区) 所以就是比较消耗内存空间。因为是直接复制的所以比较省时,不会产生内存碎片。

  • 标记清除算法:(用于FGC,一般用在老年代)
    优点:节约空间
    缺点:扫描标记耗时,并产生内存碎片,效率不高。
    先标记那些对象是需要回收的,标记完成后,对带有标记的对象进行统一的回收,因为分为了标记和回收两步,所以效率较低,同时对象与对象之间会有内存碎片产生。

  • 标记清除压缩算法:(用于老年代)
    优点:更加节约空间,不会产生内存碎片
    缺点:与标记清除算法相比多了压缩的这一步,所以耗时更长,效率不高。
    先标记那些对象是需要回收的,标记完成后,对带有标记的对象进行统一的回收,然后在对存活的对象进行压缩规整,分为了标记、清除、压缩三步,多以效率更低一些,但是由于已经压缩规整了所以不会产生内存碎片。

总结: 那个算法合适我们就用那个算法
一般新生代用:复制算法(效率高,耗内存空间)
一般老年代用:标记清除与压缩的结合(省内存空间,但是效率低)

如果这篇文章对您有帮助,那么请留下您的足迹吧…!

发布了41 篇原创文章 · 获赞 48 · 访问量 4925

猜你喜欢

转载自blog.csdn.net/weixin_45216092/article/details/105196959