分代复制算法指针移动原理
数据发生了移动,为什么依然能够访问到。
JVM四大层面
java语法
java字节码
openjdk原理
openJdk源码
强软弱虚引用
前提:
未发生GC 所有的对象都是白色。
发生GC时新建的对象都是hi黑色。
- 标记阶段做的事情
初始化标记
只标记 GC Roots直接关联的对象对象直接充当GC Root
只标记A,E 标记成灰色
会STW,耗时很少。
并发标记阶段 (三色标记)
不需要STW
耗时很久
将GC Roots直接关联的对象所有引用链全部跑一遍
一层一层遍历,正常所有被标记的都会变成黑色。
- 三色标记
GC:
串行
用户线程会STW
一个Thread执行gc
并行
用户线程STW,多个GC线程运行
并发
不需要STW,用户线程,GC线程并发。(需要三色标记)
- 最终标记
并发标记会带来三个问题
多标
A->B-C A是黑色,B是灰色,C是白色,此时 A不引用B了。B还是灰色,没有变成黑色。 不会被回收。少标
标记程序在运行的过程中,用户线程依然创建对象。由于是新创建的所以是黑色的。
创建的对象会躲过这次GC,但是下次GC有可能会被回收。漏标
GC标记程序运行过程中,引用链发生改变。B(灰)对D引用取消了。
A(黑)引用了D.
这个会导致D被回收,执行就会报错。
解决方法:增量更新,原始快照。
多标和少标在下轮GC会被回收掉。
如何解决漏标:
当黑色对象直接引用了一个白色对象后,我们就将这个黑色对象记录下来(加入 oopMap),在扫描完成后,重新对这个黑色对象扫描,这个就是增量更新(Incremental Update)
当删除了灰色对象到白色对象的直接或间接引用后,就将这个灰色对象记录下来,再以此灰色对象为根,重新扫描一次。这个就是原始快照(Snapshot At TheBeginning,SATB)
- 读写屏障
原方法write
在执行前加上 pre_write,在执行之后执行 post_write。
核心垃圾回收期
现在的垃圾回收期发展趋势:模块化,支持并发。
默认 Parallel Scvenge,Parallel Old
CMS
分代
写屏障 + 增量更新
G1
基于region 默认2M,4M,8M,16,32M
2048个Regin
每个 region都可以是e,f,to老年代,但是一个region只能有一个角色。
控制耗时
Garbege First == G1
为什么耗内存?
20% - 30% 内存存一些数据结构。
优先队列维护一个 Region优先级数据。每次回收10ms。能回收几个region算几个。
空间换时间。
- 记忆集与卡表
首先跨代引用的问题?
存在跨代引用时,在进行YGC时,如果young generation的Y对象被old generation中O对象引用,那么称O对象存在跨代引用,而且Y对象应该在本次垃圾回收中存活下来,所以old generation的对象在YGC时也是Strong root的一部分,如果每次YGC都去扫描old generation中所有对象的话,肯定会非常耗时。
解决跨代引用
老年代引用新生代。
YGC时候对象被回收了。老年代会报错
新生代引用老年代。
YGC时候,也要扫描整个老年代,非常耗时。
记忆集:GenRemSet
卡表:CardTable
记忆集是一个理论,卡表是实现。
卡表的实现原理
每个Region 2M.
卡表
卡页卡表中有2048个卡页
一个卡页对应一个Region
一个卡页是512B卡页中的一个B标识Region中的4KB(2M/512)
00000001
标记这个region是否有对年轻代的引用。
如果没有记忆集:
需要遍历所有的老年代的region,遍历所有region中的所有对象。
说白了,记忆标记了某个region是否有引用年亲代引用,然后只遍历有年轻代引用的region。减少了遍历的region数量。
- savePoint
安全点:
150 + 90 + 275 - 40 =