多线程gc问题

GC是如何工作的
GC的工作流程主要分为如下几个步骤:
1、标记(Mark)
2、计划(Plan)
3、清理(Sweep)
4、引用更新(Relocate)
5、压缩(Compact)

多线程gc问题_第1张图片
(一)、标记
目标:找出所有引用不为0(live)的实例
方法:找到所有的GC的根结点(GC Root), 将他们放到队列里,然后依次递归地遍历所有的根结点以及引用的所有子节点和子子节点,将所有被遍历到的结点标记成live。弱引用不会被考虑在内
(二)、计划和清理
1、计划
目标:判断是否需要压缩
方法:遍历当前所有的generation上所有的标记(Live),根据特定算法作出决策
2、清理
目标:回收所有的free空间
方法:遍历当前所有的generation上所有的标记(Live or Dead),把所有处在Live实例中间的内存块加入到可用内存链表中去
(三)、引用更新和压缩
1、引用更新
目标: 将所有引用的地址进行更新
方法:计算出压缩后每个实例对应的新地址,找到所有的GC的根结点(GC Root), 将他们放到队列里,然后依次递归地遍历所有的根结点以及引用的所有子节点和子子节点,将所有被遍历到的结点中引用的地址进行更新,包括弱引用。
2、压缩
目标:减少内存碎片
方法:根据计算出来的新地址,把实例移动到相应的位置。

自己在项目中的体会与总结:
多线程gc也要分阶段:标记-整理、标记-清除;
其中 标记阶段可以多线程并行执行(Parallel), 整理和清除都是内存写入操作,所以必须只能单线程处理( 也就是说 其实还是为了安全所以只能单线程),
清除时会有短暂STW(Stop the World)进程内存冻结;
我们的3D云设计客户端 卡顿/未响应现象(如上两个问题主要来自于:一个是多线程GC同步问题,一个是对象的fields问题)
其中卡顿就是由于STW引发的,进程内存进入冻结!
解决方法:现在的解决办法基本上本质就是让SWT时间变短,但是也是有缺点的就是:SWT会发生的更加的频繁!
减少SWT的时间会引发一个工作量的问题:标记整理清除过程中如果有大量内存可以回收,那么回收的工作量会变得繁重,也就是STW时间会很长
卡顿现象会灰常明显,明显到人肉眼可以感知到!所以现在的新GC做法就是维持大堆管理,但是让STW发生更频繁,每次都只是处理一点点内存回收,
这种对CPU要求其实会高一点,说白了就是多线程上下文切换更加频繁要么就是学习新兴语言那样,gc逻辑简单,没有java那么复杂,但是限制适用场景,例如只能用在小堆规模的场景
不会有几十G,或者上百G的大堆规模场景,所以你会发现Python NodeJS Go 都会非常强调小快美,唯独Java还是大慢丑

你可能感兴趣的:(多线程,垃圾回收,GC,多线程GC问题,linux,操作系统原理,多线程)