JVM垃圾回收总结

一、JVM垃圾回收

GC (Garbage Collection)的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停

(1)对新生代的对象的收集称为minor GC;

(2)对旧生代的对象的收集称为Full GC(差不多==Major GC);

(3)程序中主动调用System.gc()强制执行的GC为Full GC。

不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:

(1)强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)

(2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)

(3)弱引用:在GC时一定会被GC回收

(4)虚引用:由于虚引用只是用来得知对象是否被GC

垃圾回收涉及到的内容有:1、判断对象是否已死;2、选择垃圾收集算法;3、选择垃圾收集的时间;4、选择适当的垃圾收集器清理垃圾(已死的对象)。判断对象是否已死有引用计数算法和可达性分析算法。

二、常用垃圾回收算法

常用的垃圾回收算法有三种:标记-清除算法、复制算法、标记-整理算法。

  • 标记-清除算法。标记垃圾对象,然后统一清除。这个算法一个明显的缺点是会造成内存空间不连续。一般用在老年代区。
  • 标记-整理算法。标记垃圾对象,然后整理内存空间,整理出连续的内存空间。此算法为标记-清除算法的优化。一般用在老年代区。
  • 复制算法。将内存中存活对象复制到To survivor区,然后清空该空间,这样就获得了一块连续的内存空间。鉴于此算法的特点,它不适用于存活对象较多的内存空间,所以一般用在新生代区中。

三、分代收集算法

当前商业虚拟机的GC都是采用分代收集算法,这种算法并没有什么新的思想,而是老年代中的对象因为对象存活率高、没有额外空间进行分配担保,就使用标记-清除或标记-整理算法。新产生的对象优先进去Eden根据对象存活周期的不同将堆分为:新生代和老年代,方法区称为永久代(在新的版本中已经将永久代废弃,引入了元空间的概念,永久代使用的是JVM内存而元空间直接使用物理内存)。这样就可以根据各个年代的特点采用不同的收集算法。

新生代中的对象“朝生夕死”,每次GC时都会有大量对象死去,少量存活,使用复制算法。新生代又分为Eden区和Survivor区(Survivor from、Survivor to),大小比例默认为8:1:1。区,当Eden区满了之后再使用Survivor from,当Survivor from 也满了之后就进行Minor GC(新生代GC),将Eden和Survivor from中存活的对象copy进入Survivor to,然后清空Eden和Survivor from,这个时候原来的Survivor from成了新的Survivor to,原来的Survivor to成了新的Survivor from。复制的时候,如果Survivor to 无法容纳全部存活的对象,则根据老年代的分配担保(类似于银行的贷款担保)将对象copy进去老年代,如果老年代也无法容纳,则进行Full GC(老年代GC)。大对象直接进入老年代:JVM中有个参数配置-XX:PretenureSizeThreshold,令大于这个设置值的对象直接进入老年代,目的是为了避免在Eden和Survivor区之间发生大量的内存复制。长期存活的对象进入老年代:JVM给每个对象定义一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳,将被移入Survivor并且年龄设定为1。没熬过一次Minor GC,年龄就加1,当他的年龄到一定程度(默认为15岁,可以通过XX:MaxTenuringThreshold来设定),就会移入老年代。但是JVM并不是永远要求年龄必须达到最大年龄才会晋升老年代,如果Survivor 空间中相同年龄(如年龄为x)所有对象大小的总和大于Survivor的一半,年龄大于等于x的所有对象直接进入老年代,无需等到最大年龄要求。

四、垃圾收集的时间

当程序运行时,各种数据、对象、线程、内存等都时刻在发生变化,当下达垃圾收集命令后就立刻进行收集吗?肯定不是。这里来了解两个概念:安全点(safepoint)和安全区(safe region)。 
1)安全点:从线程角度看,安全点可以理解为是在代码执行过程中的一些特殊位置,当线程执行到安全点的时候,说明虚拟机当前的状态是安全的,如果有需要,可以在这里暂停用户线程。当垃圾收集时,如果需要暂停当前的用户线程,但用户线程当时没在安全点上,则应该等待这些线程执行到安全点再暂停。举个例子,妈妈在扫地,儿子在吃西瓜(瓜皮会扔到地上),妈妈扫到儿子跟前时,儿子说:“妈妈等一下,让我吃完这块再扫。”儿子吃完这块西瓜把瓜皮扔到地上后就是一个安全点,妈妈可以继续扫地(垃圾收集器可以继续收集垃圾)。理论上,解释器的每条字节码的边界上都可以放一个安全点,实际上,安全点基本上以“是否具有让程序长时间执行的特征”为标准进行选定。 
2)安全区:安全点是相对于运行中的线程来说的,对于如sleep或blocked等状态的线程,收集器不会等待这些线程被分配CPU时间,这时候只要线程处于安全区中,就可以算是安全的。安全区就是在一段代码片段中,引用关系不会发生变化,可以看作是被扩展、拉长了的安全点。还以上面的例子说明,妈妈在扫地,儿子在吃西瓜(瓜皮会扔到地上),妈妈扫到儿子跟前时,儿子说:“妈妈你继续扫地吧,我还得吃10分钟呢!”儿子吃瓜的这段时间就是安全区,妈妈可以继续扫地(垃圾收集器可以继续收集垃圾)。

 

参考文章:https://mp.weixin.qq.com/s/ZZSC2YDmgncah7VHnsvyxA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值