HotSpot虚拟机使用可达性分析算法肯定对象是否能够被GC。java
可达性分析算法从一系列GCRoot对象开始,向下搜索引用链,若是一个对象没有与任何GCRoot对象关联,这个对象就会被断定为可回收对象。算法
GCRoot包括如下对象:安全
虚拟机栈上的本地变量表引用的对象多线程
方法区中类的静态属性引用的对象函数
方法区中常量引用的对象spa
本地方法栈中JNI引用的对象线程
这一过程称为根节点枚举,也就是垃圾回收中的标记过程,当前全部的垃圾收集器,在标记阶段都必须中止全部java执行线程(STW),以保证对象引用状态不会发生变化。对象
HotSpot虚拟机做为准确式虚拟机,维护了一个专门的映射表(OopMap)记录哪些位置存放着对象引用,来快速完成根节点枚举过程。内存
为每个操做记录OopMap不现实,HotSpot虚拟机引入了SafePoint。字符串
SafePoint是程序中的某些位置,线程执行到这些位置时,线程中的某些状态是肯定的,在safePoint能够记录OopMap信息,线程在safePoint停顿,虚拟机进行GC。
线程停顿方式有两种,抢先式中断和主动式中断:
抢先式中断:虚拟机须要GC时,中断全部线程,让没有到达SafePoint的线程继续执行至SafePoint并中断
主动式中断:虚拟机不直接中断线程,而是在内存中设置标志位,线程检查到标志位被设置,运行至SafePoint时主动中断
SafePoint通常出如今如下位置:
循环体的结尾
方法返回前
调用方法的call以后
抛出异常的位置
这些位置保证线程不会长时间运行而没法到达SafePoint,避免其余线程都停顿等待本线程。
咱们知道代码是在线程里执行的, GC的代码也是在线程里执行, 若是执行GC的时候其余线程也同时执行的话, heap的状态将是难以追踪的. 以上面的代码为例, 假设GC线程经过扫描线程的stack(线程stack是一种GC Root), 扫描到demoObject, 而后根据这时候, main函数执行到[3], 但还未执行, 扫描的结果只发现demoObject是存活的, 接下来, main函数的线程执行[3], demoObject.val1引用了一个字符串对象, 这个对象的扫描就漏掉了, 除非以某种方式记录下这个变化, 而后从新扫描demoObject. 即使有办法记录这个赋值致使的变化而后再次扫描, 若是其余线程这时候又来捣乱, 那么从新扫描的时候有可能又发生了变化, 陷入循环…
再往下一点, 咱们知道CPU执行运算时的数据, 须要从内存里载入寄存器中, 运算完再从寄存器存入内存, 对象的地址也要通过这么个过程. 假如一个java线程分配了一个对象A, 该对象的地址存在某个寄存器中, 而后线程的cpu时间片到期被切换出去, 同时GC的线程开始扫描存活对象, 因为没有路径到这个地址还在寄存器中的对象, 这个对象被认为是garbage, 回收了. 而后睡眠的java线程醒来了, 把寄存器中的对象地址赋值给了存活对象的某个字段, over…
GC的目的在于帮助咱们收集再也不使用的内存, 可是把正在是使用的内存当成垃圾回收显然是不能接受的. 同时经过分析也看到, 因为多线程运行环境的存在, GC的工做会变的异常复杂, 要安全的回收垃圾, 须要具有两个条件:
heap的变化是受限的, 固然了, 全部线程都停下来最好, 这样heap 在GC过程当中是稳定的,这是最简单的状况.
heap的状态是已知的, 不会有活着的对象找不到或者很难找的状况. 想一想对象地址在寄存器中的状况, 虽然能够有办法能够扫描线程的寄存器, 即便这样, 也必须知道哪一个寄存器在某个时刻存的是地址, 要作到扫描不漏是很复杂的事情.
SafePoint没法解决线程未达到SafePoint并处于休眠或等待状态的状况,此时引入SafeRegion的概念。
SafeRegion是代码中的一块区域或线程的状态,在SafeRegion中,线程执行与否不会影响对象引用的状态。线程进入SafeRegion会给本身加标记,告诉虚拟机能够进行GC;线程准备离开SafeRegion前会询问虚拟机GC是否完成。