【Java基础知识】垃圾回收机制总结

垃圾回收机制

概念:

自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制。所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象;而未使用中的对象(未引用对象),则没有被任何指针给指向,因此占用的内存也可以被回收掉。

在用 C 之类的编程语言时,程序员需要自己手动分配和释放内存。而 Java 不一样,它有垃圾回收器,释放内存由回收器负责。本文接下来将介绍垃圾回收机制的基本过程。

如何判断可以垃圾回收:

引用计数法:

根据引用该变量的个数来判断是否需要被回收,当引用个数为零即被回收。弊端:循环引用无法解决,AB对象引用计数都为1,无法被判定垃圾回收。内存泄漏。
【Java基础知识】垃圾回收机制总结

可达性分析算法

扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以被回收。

那些对象可以作为GC Root?

**启动加载类
操作系统引用的Java对象
加锁对象
线程栈帧内的对象
**

四种引用
  1. 强引用,例如赋值,没有强引用,就可以被垃圾回收。
  2. 软引用,通过软引用对象引用,如果某对象只被软引用时,并且经过一次垃圾回收,内存依然不足,此时就会被该对象就会被垃圾回收。对于软引用对象,会进入引用队列,无强引用引用时,会被释放掉。
  3. 弱引用:只要发生垃圾回收,就会被垃圾回收。对于弱引用对象,会进入引用队列,无强引用引用时,会被释放掉。
  4. 虚引用:配合引用队列,使用在直接内存上,保证不会内存泄漏。
  5. 终结器引用:配合引用队列,使用在Object对象上,调用finallize()方法,回收对象。
垃圾回收算法

1. 标记清除:
【Java基础知识】垃圾回收机制总结

  • 根据GC Root位置寻找,无法找到的位置就清除。
  • 速度快。
  • 容易产生内存碎片,如图白色部分。

2. 标记整理:
【Java基础知识】垃圾回收机制总结

  • 无内存碎片。
  • 需要移动,牵扯到引用地址,速度慢。

3. 复制:
【Java基础知识】垃圾回收机制总结

  • 将存活的复制到另一块区域,然后在交换。
  • 无内存碎片。
  • 占用双倍内存空间。
分代垃圾回收机制:

【Java基础知识】垃圾回收机制总结

  1. 新创建一个对象都在伊甸园区,当伊甸园满了的时候,触发Minor GC。
    【Java基础知识】垃圾回收机制总结
    【Java基础知识】垃圾回收机制总结

  2. 将存活的对象,“年龄”+1并复制到幸存区To中,然后交换From区和To区,引用交换。

  3. 当伊甸园区又满了的时候,会对伊甸园区和From区进行Minor GC,将幸存的对象存到To区中。
    【Java基础知识】垃圾回收机制总结

  4. 某些对象经过很多次垃圾回收之后,会由幸存区转移到老年代。
    【Java基础知识】垃圾回收机制总结

  5. 当老年代空间不足时,先尝试出发Minor GC,还是空间不足,就会触发Full GC。

Minor GC和Full GC都会触发STW,一个短一个长。

垃圾回收器
  • 串行
    单线程
    堆内存较小,适合个人电脑(CPU核数较小)
    【Java基础知识】垃圾回收机制总结
  • 吞吐量优先
    多线程
    堆内存较大场景,多核CPU
    单位时间内,STW时间最短
    【Java基础知识】垃圾回收机制总结
  • 响应时间优先 CMS
    多线程
    堆内存较大场景,多核CPU
    尽可能STW时间变短
    【Java基础知识】垃圾回收机制总结
Garbage First G1

JDK9默认使用

  • 同时注重吞吐量和低延迟,默认的暂停目标是200ms
  • 超大堆内存,会将堆分为多个大小相等的Region
  • 整体上市标记+整理算法,两个区域之间时复制算法
G1垃圾回收阶段

三个阶段循环收集
【Java基础知识】垃圾回收机制总结

第一个阶段-新生代垃圾回收

刚开始对象放在E区,即伊甸园区
【Java基础知识】垃圾回收机制总结
伊甸园区满了,即用拷贝算法复制到幸存区
【Java基础知识】垃圾回收机制总结
幸存区存储较多,或者对象年龄较大,就放入老年代
【Java基础知识】垃圾回收机制总结

第二阶段-新生代回收和并发标记阶段
  • 在新生代时会进行GC Root的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会STW)

【Java基础知识】垃圾回收机制总结

混合收集阶段

【Java基础知识】垃圾回收机制总结

新生代跨老年代的问题

有一些新生代被老年代引用,但发生垃圾回收,就要对老年代进行遍历,查找是否引用新生代,这就降低标记速度。于是引用脏卡,引用新生代的就作为脏卡。标记时只标记脏卡就可以了。
【Java基础知识】垃圾回收机制总结

Remark

在并发标记阶段引用发生变化时,如图方块C,这时候就出现写保护,将C放入队列,在最终标记阶段在对C进行再标记
【Java基础知识】垃圾回收机制总结

参考资料:
B站JVM黑马视频
https://www.cnblogs.com/zhengcheng-java/p/11391601.html