2垃圾回收GC
1.基础理论
1.1对象回收的条件
- 引用计数算法
- 为对象添加一个引用计数器,当有一个地方引用它时就加一,当一个引用失效时就减一。
- 但没法解决循环引用问题:A引用B,B引用1。此时引用计数器永远不可为0。
- 可达性分析算法
- 从GC ROOTS开始寻找,当对象不存在于引用链中时,便为可回收对象。
- JVM采用的就是这种方式。
- GC ROOTS:
- 虚拟机栈的本地变量表中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- 引用的类型
- 强引用:永远不会回收。
- 软引用:内存够就不会回收。常用于缓存。
- 弱引用:存活到下一次回收之前。
- 虚引用:任何时候都可以被回收。回收时得到一个系统通知。
1.2垃圾回收算法
- 标记清除
- 先对所有应该被回收的对象进行标记,标记之后统一进行回收。
- 完全标记与清除效率不高,且产生大量内存碎片,内存不连续不利于大对象的分配。
- 复制
- 将内存空间分为两部分,每次只是用一部分,当那部分的内存用完时将存活的复制到另一部分上,然后对整个部分进行回收。
- 内存变为原来的一般,内存代价较高。对象存活率较高时需要复制的太多,效率变低。
- 解决了内存不连续的问题。
- 标记整理
- 标记整理同标记清除类似,先对所有被回收的对象进行标记,之后将存活的对象向一端移动,并直接清理掉剩余的部分。
- 解决了标记整理算法在内存存活率高时的效率问题,且内存效率为100%。
- 常用于老年代。
- 分代收集
- 在JVM中一般将堆分为新生代和老年代,新生代中总有大量对象死去因此采用复制算法,而老年代对象存活率高一般采用标记清除和标记整理。
1.3安全点安全区
- 在进行GC的标记时需要暂停所有线程的工作以防止引用发生变化,因此必须设置安全点/区来防止问题的发生。
- OopMap
- 存储的是GC ROOTS,回收时无需为了寻找GC ROOTS扫描所有位置。
- 类加载和编译时就到堆中的引用添加到OopMap中。
- 安全点
- 并非为每一条导致OopMap改变的指令都添加OopMap操作指令。
- 因此这些添加了OopMap的位置就是安全点。
- 抢先式中断
- 将所有线程停止,并让不在安全点的线程执行到安全点再停止。
- 主动式中断
- 设置一个中断标志,线程轮询此标志,当所有线程都停止时进行GC标记。
- 一般采用此种方式。
- 安全区
- 若有线程当前不处于运行状态时,若此线程暂停在非安全位置,则可能导致问题。
- 因此只有它在没有进行引用关系变化的安全区内时才可以,并在离开安全区时检测GC标记是否结束。
2.JVM中的垃圾回收
2.1回收区域
- 堆
- 新生代(Eden、Survivor),老年代
- 方法区
- 废弃常量,无用的类(并不是一定有回收功能)
- 废弃常量
- 该类的所有实例已被回收
- 该类的ClassLoader已经被回收
- 该类对应的Class对象不被引用,且无法在任何地方通过反射访问该类。
2.2内存分配与回收策略
- 内存分配
- 指针碰撞
- 堆是规整的,指针指向保存对象的最后地址
- 空闲列表方式
- 堆可以是不规整的,列表保存所有的对象。
- 选择方式
- 堆是否规整-垃圾收集器是否含有压缩整理功能决定。
- Serial ParNew等带整理功能的垃圾收集器使用指针碰撞
- CMS这种Mark-Sweep算法的使用空闲列表
- 堆是否规整-垃圾收集器是否含有压缩整理功能决定。
- 指针碰撞
- 优先Eden区
- 内存不够时进行minor GC
- 大对象直接分配到老年区
- 长期存活的放入老年区
- 在eden的对象在第一次Minor GC后分配到survivor区
- 在survivor区的熬过15次Minor GC进入老年代
2.3垃圾回收器
-
新生代
-
Serial 复制算法 单线程 非并发 可配合CMS Clinent默认 ParNew 复制算法 多线程 非并发 可配合CMS Server默认 Parallel Scanvenge 复制算法 多线程 非并发 不可配合CMS 可控吞吐量
-
-
老年代
-
Serial Old 标记整理 单线程 非并发 可配合Parallel Scanvenge Parallel Old 标记整理 多线程 非并发 可配合Parallel Scanvenge CMS 标记清除 并发 不可配合Parallel Scanvenge -
CMS
-
CMS垃圾收集步骤:
-
1.初始标记 stop world 单线程/可调 非并行 2.并发标记 未知 与用户进程同时并行 3.重新标记 stop world 多线程 非并行 4.并发清除 多线程 与用户进程同时并行
-
-
初始标记
- 标记一下GC Roots能直接关联到的对象,速度很快。
-
并发标记
- GC Roots Tracing
-
重新标记
- 修正并发标记期间,标记变动的对象的标记记录,停顿时间略长于初始标记
-
-
G1
- 可以收集整个堆的垃圾
- 分为内存多个region