【Java基础知识】垃圾回收机制总结
垃圾回收机制
概念:
自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制。所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象;而未使用中的对象(未引用对象),则没有被任何指针给指向,因此占用的内存也可以被回收掉。
在用 C 之类的编程语言时,程序员需要自己手动分配和释放内存。而 Java 不一样,它有垃圾回收器,释放内存由回收器负责。本文接下来将介绍垃圾回收机制的基本过程。
如何判断可以垃圾回收:
引用计数法:
根据引用该变量的个数来判断是否需要被回收,当引用个数为零即被回收。弊端:循环引用无法解决,AB对象引用计数都为1,无法被判定垃圾回收。内存泄漏。
可达性分析算法
扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以被回收。
那些对象可以作为GC Root?
**启动加载类
操作系统引用的Java对象
加锁对象
线程栈帧内的对象
**
四种引用
- 强引用,例如赋值,没有强引用,就可以被垃圾回收。
- 软引用,通过软引用对象引用,如果某对象只被软引用时,并且经过一次垃圾回收,内存依然不足,此时就会被该对象就会被垃圾回收。对于软引用对象,会进入引用队列,无强引用引用时,会被释放掉。
- 弱引用:只要发生垃圾回收,就会被垃圾回收。对于弱引用对象,会进入引用队列,无强引用引用时,会被释放掉。
- 虚引用:配合引用队列,使用在直接内存上,保证不会内存泄漏。
- 终结器引用:配合引用队列,使用在Object对象上,调用finallize()方法,回收对象。
垃圾回收算法
1. 标记清除:
- 根据GC Root位置寻找,无法找到的位置就清除。
- 速度快。
- 容易产生内存碎片,如图白色部分。
2. 标记整理:
- 无内存碎片。
- 需要移动,牵扯到引用地址,速度慢。
3. 复制:
- 将存活的复制到另一块区域,然后在交换。
- 无内存碎片。
- 占用双倍内存空间。
分代垃圾回收机制:
-
新创建一个对象都在伊甸园区,当伊甸园满了的时候,触发Minor GC。
-
将存活的对象,“年龄”+1并复制到幸存区To中,然后交换From区和To区,引用交换。
-
当伊甸园区又满了的时候,会对伊甸园区和From区进行Minor GC,将幸存的对象存到To区中。
-
某些对象经过很多次垃圾回收之后,会由幸存区转移到老年代。
-
当老年代空间不足时,先尝试出发Minor GC,还是空间不足,就会触发Full GC。
Minor GC和Full GC都会触发STW,一个短一个长。
垃圾回收器
- 串行
单线程
堆内存较小,适合个人电脑(CPU核数较小)
- 吞吐量优先
多线程
堆内存较大场景,多核CPU
单位时间内,STW时间最短
- 响应时间优先 CMS
多线程
堆内存较大场景,多核CPU
尽可能STW时间变短
Garbage First G1
JDK9默认使用
- 同时注重吞吐量和低延迟,默认的暂停目标是200ms
- 超大堆内存,会将堆分为多个大小相等的Region
- 整体上市标记+整理算法,两个区域之间时复制算法
G1垃圾回收阶段
三个阶段循环收集
第一个阶段-新生代垃圾回收
刚开始对象放在E区,即伊甸园区
伊甸园区满了,即用拷贝算法复制到幸存区
幸存区存储较多,或者对象年龄较大,就放入老年代
第二阶段-新生代回收和并发标记阶段
- 在新生代时会进行GC Root的初始标记
- 老年代占用堆空间比例达到阈值时,进行并发标记(不会STW)
混合收集阶段
新生代跨老年代的问题
有一些新生代被老年代引用,但发生垃圾回收,就要对老年代进行遍历,查找是否引用新生代,这就降低标记速度。于是引用脏卡,引用新生代的就作为脏卡。标记时只标记脏卡就可以了。
Remark
在并发标记阶段引用发生变化时,如图方块C,这时候就出现写保护,将C放入队列,在最终标记阶段在对C进行再标记
参考资料:
B站JVM黑马视频
https://www.cnblogs.com/zhengcheng-java/p/11391601.html