JVM垃圾回收

一. JVM内存分配和回收

Java中内存自动管理主要是针对对象的内存分配和对象的内存回收,其中最主要的的是堆的管理。

1. 堆内存划分

在这里插入图片描述
从垃圾回收的角度,现在主流的垃圾收集器都将堆分为新生代老年代,再细分就分为Eden、From Survivor、To Survivor等空间。

  • 堆内常见的内存分配策略
    1. 对象优先在Eden区分配
    2. 大对象在老年代分配,避免频繁复制导致效率低下,如大数组、字符串等
    3. 长期存活的对象在老年代分配,通过年龄判断,一般默认为15

2. GC流程

大部分对象都会首先在Eden区域分配内存,当Eden空间不足时就会触发Minor GC。

  1. 执行一次新生代垃圾回收,如果对象还存活,就将对象加到s1(To Survivor),并且将对象的年龄加1(当对象的年龄到达阈值的时候就会加入老年代)。
  2. 经过一次GC后,Eden和s0(From Survivor)就被清空了,因为存活的对象都加入到s1了
  3. 然后将s0与s1调换,也就是说把上一次的"From"变为"To",这样就保证了To空间始终是空的,这也说明了"From"和"To"只是逻辑上的划分,当"To"空间被填满时,就将对象加入老年代中。

新生代 GC(Minor GC):发送在新生代内存区域,频繁,速度快

老年代 GC(Full GC/Major GC):发送在老年代内存区域,速度慢

二. 判断对象是否死亡

1. 引用计数法

给对象添加一个引用计数器,当有对象引用时就加1,引用失效时就减1,引用计数器为0时,则表示对象死亡。

这种方式简单、高效,但不能解决循环引用的问题。

循环引用:A对象拥有B对象的引用,B对象也拥有A对象的引用,此时A、B对象的引用计数器都不可能为0,所以它们不可能被回收。

2. 可达性分析算法

​ 这个算法通过一系列称为"GC Root"的对象作为起点(通过某些方法决定哪些对象作为"GC Root"对象),其他对象都直接或间接的引用了这些"GC Root"对象,从上往下搜索,搜索的路径叫做引用链,当一个对象到"GC Root"对象没有任何一条引用链的话,则表明这个对象是无用的,也就是说该对象与"GC Root"没有直接或间接的引用关系。

在这里插入图片描述

  • 不可达的对象不一定会被回收

对象被判定为不可达后,仍要经历一次或两次标记才会被回收。

第一次标记:判断对象是否覆盖finalize()方法,如果有覆盖则进入第二次标记,如果没有则对象被回收。

第二次标记:创建一个低优先级的线程,在线程中执行对象重写的finalize()方法(不一定执行完,避免无限循环等情况使线程无限等待),如果finalize()方法执行完后,对象仍然为不可达则对象被回收。

3. 引用种类

强引用:使用最普遍的引用,当内存空间不足时,抛出异常也不会回收该对象。

软引用:当内存充足时,不会回收该对象,当内存不足时,就会回收该对象,可以和引用队列联合使用

弱引用:不管内存空间是否充足,都会回收该对象,可以和引用队列联合使用

虚引用:形同虚设什么时候都会被回收,可以和引用队列联合使用

引用队列(ReferenceQueue):软引用、弱引用、虚引用使用时都可以关联一个引用队列,当对象被回收时JVM会将该引用加入到引用队列中,通过查看引用队列来判断对象是否已经被回收。

4. 判断一个类是无用类

  1. 类的所有实例都被回收,内存中不存在任何实例
  2. 加载类的ClassLoader已经被回收
  3. 该类的Class对象没有被任何地方引用,即为不可达状态

满足这三个条件虚拟机就可以堆无用类进行回收,但不是必然会被回收。

三. 垃圾收集算法

1. 标记-清除算法

最基础的收集算法,首先标记出所有可回收的对象,在标记完成后,再统一回收被标记的对象。

在这里插入图片描述

此算法会导致两个明显的问题。

  • 效率问题:回收时需要遍历全部对象
  • 空间问题:回收完后会导致大量不连续的空间碎片

2. 复制算法

提高了效率,此算法将内存分为两块,每次使用其中一块,当这一块内存使用完后,就将其中还存活的对象复制到第二块内存中,然后再将第一块内存全部清除。

在这里插入图片描述
次算法的消耗是复制存活的对象,所以此算法适用于对象比较小的情况,常用于新生代内存区域。

3. 标记-整理算法

标记-清除算法一样,先对可回收的对象进行标记,然后再将仍然存活的对象向一端移动,即将可回收的对象覆盖掉,最后再清除边界外的内存。

在这里插入图片描述

4. 分代收集算法

这种算法会将堆内存分为几份,根据对象的生存周期而使用不同的收集算法。一般会将堆内存分为新生代和老年代,由于新生代会有大量的对象死亡,所以适合用复制算法只需要消耗少量的复制成本,而老年代中的对象一般不易死亡并且数据量较大,所以适合用标记-整理算法或者标记-清除算法

四. 垃圾收集器

垃圾收集器常见的有五种,目前没有最好的垃圾收集器,不同的场景适用不同的垃圾收集器。

  • Serial收集器
  • ParNew收集器
  • Parallel Scavenge收集器
  • CMS收集器
  • G1收集器

1. Serial收集器

最基本、最悠久的收集器。

  • Serial收集器是单线程的,并且在垃圾收集的时候会暂停其他线程,直到收集结束。
  • 新生代使用的是复制算法,老年代使用的是标记-整理算法

在这里插入图片描述
目前还么有产生能够避免回收过程所产生的停顿,只是不同的收集器产生的停顿时间长短不同。

2. CMS收集器

CMS收集器是HotSpot虚拟机真正意义上的并发收集器,第一次实现了垃圾收集与用户线程(基本上)同时工作。

CMS是基于标记-清除算法的垃圾收集器,主要分为四个步骤。

  1. **初始标记:**这个过程会暂停所有其他线程,并标记出与Root直接相连的对象,即找出引用链的起点。
  2. **并发标记:**这个过程会与用户线程并发执行,通过初始标记出的对象去标记所有可达的对象,即标记出所有在引用链中的对象。
  3. **重新标记:**因为并发标记是与用户线程并发执行的,所以在用户线程执行的过程中可能会产生新的对象,即产出新的引用链,所以需要暂停所有线程对新产生的对象进行重新标记,避免回收了不必要回收的对象。
  4. **并发清除:**与用户线程并发执行,对可回收对象进行回收。

在这里插入图片描述

CMS收集器优点:低停顿、并发收集。

CMS收集器缺点:1. 因为使用标记-清除算法所以会产生大量空间碎片

​ 2. 因为与用户线程并发执行,所以会占用CPU资源,导致用户线程卡顿

​ 3. 无法处理浮动垃圾,即当CMS标记对象后,用户线程又不需要该对象了,导致该对象只能在下一次 GC中回收

3. G1收集器

G1是一款面向服务器的垃圾收集器,主要针对多颗处理器以及大容量内存的机器,极大的概率满足低停顿要求,还具备高吞吐的性能。

特点:

  • 并发与并行:充分利用CPU缩短短停顿。
  • 分代收集:保留了分代的概念,虽然保留了分代的概念,但是新生代和老年代不再是物理划分,也就是说不再是连续的
  • 空间整合:使用标记-整理的算法,避免了空间碎片化
  • 可预测停顿:让垃圾收集消耗的时间小于指定的时间

G1收集的过程和CMS类似

  • 初始标记(需要停顿)
  • 并发标记
  • 重新标记(需要停顿)
  • 筛选回收

G1收集器维护了一个优先列表,在允许的回收时间内,选择价值最大的Region(区域),使用这种方法保证了G1能够在有限的时间内回收效率尽可能的高。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值