一. 垃圾收集算法
1. 标记-复制算法
一般应用于年轻代中,将内存分为相同大小的两块,每次使用其中一块;本次使用完成后,将存活对象复制到另一侧,并清空这块内存。
问题:该种方式浪费空间
2. 标记-清除算法
一般应用于老年代。
(1)标记阶段:标记存活的对象
(2)清除阶段:清除未被标记的对象
(3)问题:效率较低;标记清除后会产生大量不连续空间
3. 标记-整理算法
优点:避免内存碎片产生
二. 垃圾收集器
垃圾收集器使用图.png1. serial收集器(串行收集器)
(1)使用serial收集器配置:
-XX: +UseSerialGC
-XX: +UseSerialOldGC
(2)串行收集
串行收集示意图.png
注:STW(stop the world)暂停所有用户线程,并开始清理系统垃圾,这样做的原因是防止在垃圾清理过程中对象引用发生变化,产生新的未标记到的垃圾。
2. parallel收集器(JDK8默认)
(1)开启parallel收集器配置:
-XX: +UseParallelGC
-XX: +UseParallelOldGC
(2)并发收集
并发收集.png
3. parNew收集器
与parallel类型,主要区别为可与CMS配合使用
4. CMS垃圾收集器
基于标记-清除算法实现,底层使用写屏障模式
4.1 流程示意图
CMS收集器流程.png
(1)初始标记:执行STW(stop the world),此时用户线程均已停止,垃圾收集器通过引用链分析法找到gc根节点直接引用的对象,该阶段速度极快
User u = new User(); // 对象u为User类的直接引用
(2)并发标记:并发的含义是应用线程与回收线程并发运行,垃圾收集器将接替初始标记到的对象向下一直寻找堆中的引用,由于此时引用链变得复杂,这部分标记将使用并发标记;相较于STW标记方式,并发标记兼顾了用户线程的执行与垃圾收集,故此处垃圾收集时间将较长
class Person {
string p_name;
int p_id;
}
Person p = new Person("xiaoming", 10);
u.setPerson(p); // 该引用非直接引用,将在并发标记阶段执行标记
(3)重新标记:修正并发标记期间由于用户行为改变的标记对象,底层使用三色标记法
(4)并发清理:将未标记的对象进行清除
(5)并发重置:重置本次GC标记的数据
(6)CMS的缺点:
- 对CPU资源较为敏感,并发标记等阶段gc会与用户线程争夺资源
- 无法处理浮动垃圾,在并发标记和并发清理阶段又产生的垃圾,只能等到下次GC进行处理
- 底层使用回收算法“标记-清除”导致产生大量碎片,我们可以通过-XX: +UseCMSComputeAtFullCollection,当JVM执行完标记清除后再整理碎片
- 垃圾收集过程中可能触发concurrent mode failure(并发模式失败):
在并发标记与并发清理过程中,用户线程将产生新的对象存入老年代,而此时若老年代已满,将激发CMS算法触发full gc(serial old),执行串行STW
(7)三色标记法(CMS底层算法)
处理并发标记过程中,对象引用变化的问题;
会产生漏标的问题,例如以下情况:
class A {
B b = new B();
D d = null;
}
class B{
C c = new C();
D d = new D();
}
class C {}
public static void main() {
A a = new A();
D d = a.b.d; // 插入读屏障
a.b.d = null; // 写屏障
a.d = d; // 写屏障
}
三色标记法.png
注:最终不回收黑色/灰色对象,只回收白色对象
(8)对三色标记漏标问题的处理
-
增量更新方式
采用缓存记录并发标记过程中扫描的对象,重新标记过程中将从缓存中重新扫描这些对象。
增量更新方式.png - 原始快照方式
使用缓存方式作为快照集合,将被删除的引用放入集合中记录,并将对象节点标为黑色,成为浮动垃圾,在下轮清理的过程中会清理该种浮动垃圾。 - 实现增量更新/原始快照方式:写屏障
给某个对象的成员变量赋值时,在赋值前后进行操作
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field);
*field = new_value;
post_write_barrier(field);
}
(9)记忆集与卡表
解决跨代引用问题
5. G1收集器
G1收集器将java堆分为大小相等的多个独立区域(Region),种类为:
Eden区,survive区,old区及Humongous区
其中Humongous区存放占内存过大对象(一个对象超过region50%,默认被放入Humongous区域)
(1)G1收集流程
G1收集流程.png
- 筛选回收过程:对各个Region回收价值与成本进行排序,依据用户期望GC停顿回收时间制定回收计划(通过-XX:+MaxGCPauseMills修改用户预期停顿阈值),将STW限制在一定时间范围内,增强用户体验
- 底层采用复制算法回收
- 优先级列表:考虑gc时间,占用内存空间等
(2)G1收集算法分类: - young GC:计算当前Eden区回收时是否到达设定的时间阈值,不到达该阈值,则继续将对象放入Eden区;到达阈值,则进行一次Young GC
- mixed GC:回收所有年轻代,部分老年代及Humouongs区域垃圾,是否触发mixed GC由收集阈值确定,阈值的设置通过-XX:InitiatingHeapOccupancyPercent设定。
- full gc:STW,当老年代完全放满时开始执行,采用单线程进行标记,清理空间
(3)G1垃圾收集器使用场景: - 所用系统的堆中50%以上空间被存活对象占用
- 对象分配及晋升到老年代的速度变化较大时
- 垃圾收集时间较长时,比如超过1秒钟
- 堆内存较大(因为底层使用的是复制算法),超过8GB以上
- 停顿时间在500ms以内
注:不同内存推荐使用的算法版本: - 堆内存4G以下推荐使用parallel
- 堆内存4-8G使用CMS+parallel
- 堆内存8G以上使用G1
网友评论