1. 垃圾判别方法
引用计数算法
判断对象的引用数量来决定对象是否可以被回收
每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1
优点:执行效率高,程序执行受影响小
缺点:无法检测出循环引用的情况,导致内存泄露
可达性分析算法
Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活对象
扫描堆中的对象,看是否能沿着GC Root对象为起点的引用链找到该对象,找不到则可以回收
哪些对象可以作为GC Root
通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
虚拟机栈中的引用的对象
本地方法栈中JNI(natice方法)的引用的对象
方法区中的常量引用的对象
方法区中的类静态属性引用的对象
处于激活状态的线程
正在被用于同步的各种锁对象
GC保留的对象,比如系统类加载器等。
2. 垃圾回收算法
标记清除法
- 标记没有被GC Root引用的对象
- 清除被标记位置的内存
- 优点:处理速度快
- 缺点:造成空间不连续,产生内存碎片
标记整理法
- 标记没有被GC Root引用的对象
- 整理被引用的对象
- 优点:空间连续,没有内存碎片
- 缺点:整理导致效率较低
复制算法
- 分配同等大小的内存空间
- 标记被GC Root引用的对象
- 将引用的对象连续的复制到新的内存空间
- 清除原来的内存空间
- 交换FROM空间和TO空间
- 优点:空间连续,没有内存碎片
- 缺点:占用双倍的内存空间
![在这里插入图片描述](https://uploadfiles.nowcoder.com/files/20200522/247775678_1590133187448_20200522081246348.png) ![在这里插入图片描述](https://uploadfiles.nowcoder.com/files/20200522/247775678_1590133187314_20200522081254168.png)
3. 分代垃圾回收机制
分代垃圾回收流程
- 对象首先分配在伊甸园区域
- 新生代空间不足时,触发Minor GC,伊甸园和from存活的对象使用【复制算法】复制到to中,存活的对象年龄加一,并且交换from区和to区
- Minor GC会引发Stop the world(STW)现象,暂停其他用户的线程。垃圾回收结束后,用户线程才恢复运行
- 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4位二进制)
- 当老年代空间不足,会先尝试触发Minor GC,如果之后空间仍不足,会触发Full GC(STW时间更长,老年代可能使用标签清除或标记整理算法)
- 当存放大对象新生代放不下而老年代可以放下,大文件会直接晋升到老年代
- 当存放大对象新生代和老年代都放不下时,抛出OOM异常
默认堆内存分配
- 新生代占1/3,老年代占2/3
- -XX:NewRatio:老年代和年轻代内存大小的比例
- 新生代中按8 1 1进行分配,两个幸存区大小需要保持一致
- -XX:SurvivorRatio: Eden和Survivor的比值,默认是8(8:1)
GC相关VM参数
4. 垃圾回收器
安全点(SafePoint)
分析过程中对象引用关系不会发生改变的点
产生安全点的地方:
- 方法调用
- 循环跳转
- 异常跳转
安全点的数量应该设置适中
串行(SerialGC)
- 单线程的垃圾回收器
- 堆内存较小,CPU核数少,适合个人电脑
- SerialGC收集器 (-XX:+UseSerialGC 复制算法) Client模式下默认的年轻代收集器
- SerialGC Old收集器 (-XX:+UseSerialOldGC 标记-整理算法)Client模式下默认的老年代收集器
吞吐量优先(ParallelGC)
多线程的垃圾回收器
堆内存较大,多核CPU,适合服务器
尽可能让单位时间内STW暂停时间最短(吞吐量=运行代码时间/(运行代码时间+垃圾回收时间))
并行的执行
ParallelGC收集器(-XX:+UseParallelGC 复制算法) Server模式下默认的年轻代垃圾回收器
ParallelGC Old收集器(-XX:+UseParallelOldGC 复制算法)
响应时间优先(CMS -XX:+UseConcMarkSweepGC 标记清除算法)
多线程的垃圾回收器
堆内存较大,多核CPU,Server模式下默认的老年代垃圾回收器
尽可能让单次STW暂停时间最短
部分时期内可以并发执行
执行流程
- 初始标记:stop-the-world
- 并发标记:并发追溯标记,程序不会停顿
- 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象
- 重新标记:暂停虚拟机,扫描CMS堆中的剩余对象
- 并发清理:清理垃圾对象,程序不会停顿
- 并发重置:重置CMS收集器的数据结构
G1(-XX:+UseG1GC 复制+标记清除算法)
- G1l垃圾回收器简介
- 定义:Garbage First (2017 jdk9 默认)
- 特点
- 并发和并行
- 分代收集
- 空间整合
- 可预测的停顿
- 使用场景
- 同时注重吞吐量和低延迟,默认暂停目标是200ms
- 超大堆内存,会将整个堆划分为多个大小相等的Region(新生代和老年代不再物理隔离了)
- 整体上是标记整理算法,两个区域之间是复制算法
垃圾回收阶段
新生代垃圾收集
- 会发生STW
新生代垃圾收集+并发标记
- 在Young GC时会进行GC Root的初始标记
- 老年代占用堆内存空间比例达到阈值时,进行并发标记(不会STW)
混合收集,对新生代,幸存区和老年代都进行收集
- 最终标记,会STW
- 拷贝存活,会STW
- 三种阶段循环交替
Full GC
SerialGC
- 新生代内存不足发生的垃圾收集:minor GC
- 老年代内存不足发生的垃圾收集:full GC
ParallelGC
- 新生代内存不足发生的垃圾收集:minor GC
- 老年代内存不足发生的垃圾收集:full GC
CMS
新生代内存不足发生的垃圾收集:minor GC
老年代内存不足
- 并发收集成功:并发的垃圾收集
- 并发收集失败:串行的full GC
G1
新生代内存不足发生的垃圾收集:minor GC
老年代内存不足,达到阈值时进入并发标记和混合收集阶段
- 如果回收速度>新产生垃圾的速度 :并发垃圾收集
- 如果回收速度<新产生垃圾的速度:串行的full GC
5. 四种引用
- 强引用
最常见的对象:通过new关键字创建,通过GC Root能找到的对象。
当所有的GC Root都不通过【强引用】引用该对象时,对象才能被垃圾回收
软引用
仅有【软引用】引用该对象时,在垃圾回收后,内存仍不足时会再次发起垃圾回收,回收软引用对象
可以配合引用队列来释放软引用自身
创建一个软引用:SoftReference<object> ref = new SoftReference<>(new Object());</object>
软引用被回收后,仍然还保留一个null,如将软引用加入集合,回收后遍历集合仍然还存在一个null
- 解决:使用引用队列,软引用关联的对象被回收时,软引用自身会被加入到引用队列中,通过queue.poll()取得对象进行删除
- 创建一个而引用队列:ReferenceQueue<object> queue = new ReferenceQueue<>();</object>
- 创建加入了引用队列的软引用:SoftReference<object> ref = new SoftReference<>(new Object(),queue);</object>
弱引用
仅有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
可以配合引用队列来释放弱引用自身
创建一个弱引用:WeakReference<object> ref = new WeakReference<>(new Object());</object>
引用队列使用同软引用
- 虚引用
必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将【虚引用】入队,由Reference Hanler线程调用虚引用相关方法释放【直接内存】(unsafe类中方法)
- 终结器引用
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用队列入队(引用对象暂未回收),再由Finalizer线程通过终结器引用找到被引用对象并调用他的finalize方法,第二次gc时回收被引用对象