1. 垃圾回收器算法
目前主流垃圾回收器都采用的是可达性分析算法来判断对象是否已经存活,不使用引用计数算法判断对象存活的原因在于该算法很难解决相互引用的问题。
1.1 标记-清除算法(Mark-Sweep)
标记-清除算法由标记阶段和清除阶段构成。标记阶段是把所有活着的对象都做上标记的阶段;清除阶段是把那些没有标记的对象,也就是非活动对象回收的阶段。通过这两个阶段,就可以令不能利用的内存空间重新得到利用。
从标记-清除算法我们可以看出,该算法不涉及对象移动,但是可能会产生内存碎片化问题。空间碎片太高可能会导致程序运行时需要分配较大内存时候,无法找到足够的连续内存,需要其他垃圾回收帮助回收内存。
1.2 复制算法(Copying)
复制算法内存空间分为两块区域:From、to,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
上面那种复制算法有一半的空间是浪费的。所以在Java新生代把内存区域分为Eden空间、from、to空间3个部分,from和to空间也称为survivor
空间,用于存放未被回收的对象。对象开始都是Eden生成;当回收时,将Eden和from中存活的对象移动到to区域中。
复制算法存在空间浪费的情况,始终都要保持一个Survivor是空闲的,并且在GC的时候要是存活对象大小超过了Survivor中的大小,就需要另外的策略存储存活对象。
目前open JDK新生代回收策略就是采用的复制算法,其中Eden和Survivor的默认配置为8:1
1.3 标记-压缩算法(Mark-Compact)
标记-压缩算法由标记阶段和压缩阶段构成。标记阶段标记-清除算法中的标记阶段完全一样,压缩阶段是让所有存活的对象向一端移动。这样空闲内存都在另外一端,属于连续空间,不存在内存碎片化问题,但是会产生对象移动。
1.4 分代算法(Generational GC)
根据对象的不同生命周期分别管理, JVM 中将对象分为我们熟悉的新生代、老年代和永久代分别管理。这样做的好处就是可以根据不同类型对象进行不同策略的管理,例如新生代中对象更新速度快,就会使用效率较高的复制算法。老年代中内存空间相对分配较大,而且时效性不如新生代强,就会常常使用Mark-Sweep-Compact (标记-清除-压缩)算法。
2. 各个算法的性能比较
标记-清除 | 复制算法 | 标记压缩 | |
---|---|---|---|
时间开销 | 中等 | 最快 | 最慢 |
空间浪费 | 不存在 | 存在 | 不存在 |
内存碎片化 | 存在 | 不存在 | 不存在 |
是否移动对象 | 否 | 是 | 是 |
从表格中我们可以看出,复制算法效率最高,也不存在内存碎片化,但有空间浪费的现象,一般用来处理新生代中的对象
而标记-清除算法和标记压缩算法则主要处理老年代中对象内存分配比较大的,并且时效性不如新生代的
3. 常见的垃圾回收器
3.1 按照处理过程分类:
- 串行垃圾回收器(Serial Garbage Collector)
- 并行垃圾回收器(Parallel Garbage Collector)
- 并发标记扫描垃圾回收器(CMS Garbage Collector)
3.1.1 串行垃圾回收器
回收器名称 | 使用的算法 | 作用区域 | 单/多线程 | 备注 |
---|---|---|---|---|
Serial | 复制算法 | 新生代 | 单线程 | 简单高效,不建议使用,Client默认的 |
Serial Old | 标记-压缩 | 老年代 | 单线程 | 能和所有的Young GC搭配使用 |
3.1.2 并行垃圾回收器
回收器名称 | 使用的算法 | 作用区域 | 单多线程 | 备注 |
---|---|---|---|---|
ParNew | 复制算法 | 新生代 | 多线程 | 唯一和CMS搭配的新生代垃圾回收器 |
Parallel Scavenge | 复制算法 | 新生代 | 多线程 | 更关注于吞吐量 |
ParNew Old | 标记整理 | 老年代 | 多线程 | 搭配Parallel Scavenge垃圾回收器 |
3.1.3 并发标记扫描垃圾回收器
回收器名称 | 使用的算法 | 作用区域 | 单多线程 | 备注 |
---|---|---|---|---|
CMS | 标记-清除 | 老年代 | 多线程 | 追求最短的暂时时间 |
3.2 按照处理的区域分类:
3.2.1 新生代(Young GC)
4. 垃圾回收器的选择策略
- 客户端程序:Serial + Serial Old;
- 吞吐率优先的服务端程序(比如:计算密集型):Parallel Scavenge + Parallel Old;
- 响应时间优先的服务端程序:ParNew + CMS。
目前很大一部分的Java应用都集中在互联网的服务器端,这类应用尤其关系服务的响应时间,希望应用暂停时间更短,所以基本上使用的都是 ParNew + CMS
在启动JVM参数加上 -XX:+UseConcMarkSweepGC
这个参数表示对于老年代的回收采用 CMS。
4.1 CMS执行过程
CMS 的回收过程主要分为下面的几个步骤:
- 初始标记(Initial Mark)
- 并发标记(Concurrent marking)
- 并发预清理(Concurrent pre-preclean)
- 重新标记(Final Remark)
- 并发清理(Concurrent sweep)
- 并发重置(Concurrent reset)