- 首先要知道oop是做什么的,其实就是记录新的对象引用,能够让系统快速找到gc root
- 如果每次修改引用都要重新生成oop,那么是不是就需要占据大量的cpu时间,所以需要每隔一段时间去更新
- 也就是说这就是为什么需要STW的原因
- 安全点就是能够保证在oop差别不大的情况下修改,而且能够让大多数线程执行到这个时间间隔STW
- 但是很多线程不可能同时一起中断,有的指令需要执行很长时间这个时候就需要协调这个位置,这个位置就是安全点(中断的时间约定)
相当于就是给一个标志,线程去轮询如果发现需要中断,那么就跑到安全点的时候自我中断。
抢占式中断系统直接中断所有线程,如果发现还有线程在中断的时候还在跑,那就让他继续跑
那么为什么需要安全区域?因为还有一些阻塞的线程,那么计算安全点不可能去等这些线程,线程在阻塞的时候应该通知jvm这里是我的阻塞需要的时间,那么jvm就不管那些阻塞线程来计算安全点,stw所有线程来进行oop更新。
总结:其实就是为了oop更新不那么频繁,但是要保证更新间隔不长。还要考虑到阻塞线程等问题
卡表和记忆集的定义?- 卡表是记忆集的实现
- 卡表可以说是一个数组存在每个新生代,然后每个数组的元素对应着一内存区域,如果对应的内存区域指向现在这个新生代区域,那么对应的数组元素就是1,如果没有就是0。
- 卡表存在可以快速找到老年代,从老年代那里的那里得知自己已经被引用不需要被删除,就不需从头开始遍历了,在清理新生代的时候看看有没有被老年代引用,如果有那么就能够幸存
- 大大缩减了搜索gc root范围,不需要搜索全部老年代
- 其实就是其它区域有对象指向自己的时候,那么卡表就要修改。那么什么时候修改?肯定就是其他区域对象赋值地址 *** 作的时候
- 赋值 *** 作指令加上一个后继处理,修改卡表相当于就是一个aop *** 作
- 但是可能会出现并发问题,卡表指向的内存区域,刚好多个线程正在处理这些区域对象的引用改变,指向了卡表所在的区域,那么卡表所在的区域就要修改卡表,问题就是如果线程1先取了卡表出来改了,还没改完线程2执行了该表,那么就会导致表改失败了。解决办法就是检查卡表的标记,如果没有标记那么才变脏那么才能够进行修改。修改完之后才恢复标记,其它线程才能修改。(这里存疑)
- 黑色块后面插入白色块,因为黑色块已经遍历过,导致白色块被忽略
- 灰色块后面删除白色块导致白色块没有回收
解决方案
- 增量更新记录插入白色块,结束回收再以这个插入白色块的根(黑色块)来遍历一次
- 原始快照记录删除的白色块,结束后以灰色块为根来再次遍历
会不会凭空多出白色对象?很明显,不会,那么只能就是线程修改里面的各种引用关系了。要么就是多引用了几个,要么就是就是断开连接了。
黑色插入的同时肯定会有一个灰色删除了,灰色删除的同时,黑色插入那个删除的。由于黑色无法再去遍历导致的白色也被删掉。
说说CMS工作流程?- 初始标记:标记gc root第一个直接连接的节点(stw)
- 并发标记:遍历标记所有gc root的链
- 重新标记:增量更新之前新插入的节点状态
- 最后就是并发消除
- 浮动垃圾指的是用户线程与垃圾回收线程并发的时候用户线程产生的垃圾无法被标记。
- 所以老年代需要预留空间,如果空间不足就会发生并发失败的问题。
- 所以需要后备方案serial old,好处就是serial是单线程不会导致浮动垃圾
- 初始标记
- 并发标记,使用SATB来进行记录
- 重新标记,重新遍历SATB获取的记录
- 筛选回收
- 因为G1有维护region的垃圾优先级表,优先选择回收价值大的保证响应时间
- 通过STAB来记录那些被删除的节点,然后恢复
- 卡表占用大量内存,修改卡表消耗大
- G1还需要写前屏障保证SATB算法执行
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)