目录
什么是垃圾
没有任何引用所指向的对象就是垃圾
如何定位垃圾
-
引用计数
-
根可达算法
常见的垃圾回收算法
- 标记清除:从根对象开始标记,之后将未标记的对象进行清理,适用于存活对象叫多的情况
缺点:扫描两次,效率偏低 - 拷贝算法:开辟一块内存,将存活对象顺序拷贝到新开辟的内存中,适用于存活对象较少的情况。
优点:只扫描一次,效率较高,不会产生碎片
缺点:空间浪费,需要移动对象。 - 标记压缩:标记的同时进行移动,使得对象在内存中顺序存储。
优点:不会产生碎片,方便对象分配,不会产生内存减半
缺点:扫描两次,并且需要移动对象,效率较低
分代模型
对象分配过程
1.new 对象,判断对象在stack(栈)中如果能放下,则方法stack中
2. 如果对象较大(可以通过参数配置),直接进入old区。
3. 第一次则进入Eden区,当没有被GC回收时,会进入s1。
4. 如果再被垃圾回收器清理,则s1跟s2轮流进入,直到达到年轻代次数(默认15次,可以通过参数指定),则进入老年代。
常见的垃圾回收算法
-
标记清除法(Mark-Sweep)
先全部扫描一遍,对存活对象进行标记,然后再进行一次清理
1.1. 算法相对简单
1.2. 存活对象较多情况下效率较高
1.3. 需要经过两边扫描,效率较低,容易产生碎片 -
拷贝算法(Copying)
先将存活对象进行标记,然后划分一块区域,将存活对象进行拷贝,然后清理整块内存
2.1. 适用于存活对象较少情况
2.2. 只扫描一次,效率较高没有碎片
2.3. 空间浪费,移动复制对象,需要调整对象引用 -
标记压缩法(MarkCompact)
先对存活对象进行标记,然后在清理过程中并进行压缩整理到开始部分
3.1 不会产生碎片,方便对象分配
3.2 不会产生内存减半
3.3 扫描两次,需要移动对象,效率偏低
jvm常见垃圾回收器
JDK诞生 Serial追随
Serial 年轻代 串行回收
Serial Old 老年代 串行回收
单线程回收
提高效率,诞生了PS
Parallel Scavenge年轻代 并行回收
Parallel Old老年代 并行回收
jdk8及之前默认,ps (年轻代,复制算法),po(老年代,标记-整理算法),多线程并行回收
因为无法忍受STW, 所以有了CMS(1.4后引入)
ParNew 为了配合CMS而诞生
CMS(ConcurrentMarkSweep) 老年代 与应用程序并发执行。但是CMS带来的问题就是碎片
cms 清理的4个阶段:
- 初始标记 ,只标记根对象。STW过程。 – initial mark
- 并发标记, 并发标记可以和应用程序同时运行。 – concurrent mark
- 重新标记,对并发标记过程中产生的垃圾进行标记,STW过程。 – remark
- 并发清理,进行垃圾清理。 – concurrent sweep
老年代分配不下了久会促发 Full GC(Serial GC来回收)
垃圾收集器跟内存大小的关系
Serial 几十兆
PS 上百兆 - 几个G
CMS - 20G
G1 - 上百G
ZGC - 4T - 16T(JDK13)
G1模型
- old:老年区
- survivor:存活对象(第一次垃圾回收之后,但是年龄并未到老年区)
- Eden:首次进入的对象
- Humongous:大对象区,可能会垮连续的Regin
回收算法:初始标记,并发标记,最终标记,筛选标记(与CMS不同,筛选垃圾占用最多的进行回收)。不会产生空间碎片,可以精确地控制停顿,使用三色标记算法(cms/G1都是使用三色标记算法,zgc使用颜色指针)
三色标记算法
漏标:黑色对象指向白色对象,灰色对象指向白色对象的引用取消,这时会产生漏标
如何解决漏标:
- 增量更新(CMS使用)
- SATB(G1使用)
为什么G1用SATB
灰色指向白色,引用消失时,如果没有黑色指向白色,引用会被push到堆栈,下次扫描时,拿到这个引用,由于有RSet的存在,不需要扫描整个堆去查找指向白色的引用,效率比较高,SATB配合RSet,浑然天成。
CMS收集器和G1收集器的区别:
CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
CMS收集器以最小的停顿时间为目标的收集器;
G1收集器可预测垃圾回收的停顿时间
CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
CMS三色标记解决漏标的方式为增量更新
G1三色标记解决漏标的方式为SATB
在开发中,是否推荐使用System.gc()
不推荐,因为System.gc()是单线程的回收(Serial),效率低
NUMA
UMA:多个cpu共享一个内存
缺点:不易扩展,CPU数量增多后引起内存访问地址冲突加剧,CPU的很多资源花在了争抢内存地址上面,4颗左右比较合适
NUMA:每个CPU都有就近的内存,ZGC可以感知到最近的内存,分配内存会优先分配该线程所在cpu最近的内存。