垃圾收集器
垃圾收集器是收集算法的具体实现。不同的垃圾收集器之间使用的收集策略会有很大差别
垃圾收集器类型
在这里先解释两个名词:并发和并行
- 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
- 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上
Serial收集器和ParNew收集器
Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器,曾经是虚拟机新生代收集的唯一选择
这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。在用户不可见的情况下把用户正常工作的线程全部停掉,这对很多应用来说都是难以接受的。
Serial收集器收集时候使用的算法:新生代使用采用复制算法;老生代使用才用整理算法
虽然Serial收集器原始且存在各种问题但是它依然是虚拟机运行在Client模式下的默认新生代收集器。相较于其他收集器它:简单而高效。由于没有线程交互的开销,专心做垃圾收集使其获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版。控制参数)、收集算法、程序暂停、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。
ParNew收集器是除了Serial收集器外,目前能与CMS收集器配合工作收集器,CMS作为高效的收集器,却无法与JDK 1.4.0中已经存在的新生代收集器Parallel Scavenge配合工作,所以在JDK 1.5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个。
ParNew收集器在单CPU的环境中由于存在线程交互的开销,该收集器在实现的两个CPU的环境中都不能百分之百地保证可以超越Serial收集器。但是,随着可以使用的CPU的数量的增加,它对于GC时系统资源的有效利用还是很有好处的。
两者关键词
- Serial收集器
- 单线程收集器
- 主要针对新生代
- 新生代使用复制算法,老年代(假如使用)标记整理算法
- ParNew收集器
- 多线程收集器
- 同样针对新生代
- 新生代使用复制算法,老年代(假如使用)标记整理算法
- 主要配合CMS使用
Parallel Scavenge收集器
Parallel Scavenge收集器是一个并行的多线程新生代收集器,使用复制算法的收集器,非常类似ParNew。
既然已经存在类似的收集器,那么他和其他收集器到底有什么不同的呢
吞吐量
Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值。由于与吞吐量关系密切,ParallelScavenge收集器也经常称为“吞吐量优先”收集器。
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
关键词
- 并行的多线程收集器
- 主要针对新生代
- 使用复制算法
- 关注可控的吞吐量
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。
此收集器在Server模式有两大用途:
- 一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用
- 另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用
新生代的示意图
老年代的示意图
关键词
- 单线程收集器
- 主要针对老年代
- 使用标记整理算法
- 主要和Parallel Scavenge收集器实现配合
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
这个收集器是在JDK 1.6中才开始提供的,在此之前,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器外别无选择效果。但是因为Serial Old的单线程导致整体效果并不是很好。
直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较好的销量。在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
工作过程
关键词
- 多线程收集器
- 主要针对老年代
- Parallel Scavenge的老年代版本,实现和Parallel Scavenge的配合
- 使用标记整理算法
- 关注可控的吞吐量
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
CMS使用“标记清除”算法实现。处理过程
- 初始标记
- 并发标记
- 并发预清理
- 最终标记
- 并发清除
- 并发重置
初始标记
初始标记仅仅只是标记一下GC Roots能之间关联到的对象。
并发标记
并发标记就是进行GC RootsTracing(可达性分析)的过程。
并发预清理
查找在执行并发标记阶段新进入老年代的对象,通过重新扫描,减少下一个阶段"重新标记"的工作。
重新标记
进行标记修正,对因为用户程序持续运行而导致对象引用产生变动的那一部分对象进行标记。此阶段会暂停其他线程。
并发清除
执行对象的清理工作。
并发重置
重置垃圾回收器状态。
Sun公司的一些官方文档中也称之为并发低停顿收集器。
CMS收集器存在的问题
- 对CPU资源非常敏感,因为会因为占用了一部分线程而导致应用程序变慢
- CMS收集器无法处理浮动垃圾由于CMS并发清理阶段用户线程还在运行着,伴随程序运行垃圾的产生不会停止,在标记过程之后产生的垃圾,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。
- 垃圾收集阶段用户线程还要运行,需要预留足够的内存空间给用户。因而CMS收集器不能像其他收集器那样等到老年代填满后再进行收集。
- 因为基于“标记-清除”算法,意味着收集结束时会有大量碎片。因此会在进行FullGC时开启内存碎片的合并整理,因为内存整理过程无法并发,虽然解决空间碎片问题,但是停顿时间变长了。
G1收集器
G1是一款面向服务端应用的垃圾收集器。
和其他收集器相比G1收集器的好处
- 并行与并发:充分利用多CPU来缩短top-The-World停顿的时。G1收集器可以通过并发的方式保证JAVA程序的运行。
- 分代收集:它能够采用不同的方式去处理新创建的对象和已经存活经历多次GC的旧对象。
- 和CMS不同,G1是基于“标记整理”算法。这使得G1运作期间不会产生空间碎片。有利于程序长时间运行,
- G1提供一个使用者一个功能。使用者可以指定一个长度为M毫秒的时间内,消耗在GC上时间不得超过N秒。
- G1之前收集器进行收集范围都是整个新生代或老生代。而G1将整个JAVA堆分为多个大小相等的区域(region),G1跟踪各个区域里的垃圾堆积的价值大小,后台维护一个优先列表,每次根据允许收集的时间,优先回收价值最大的区域。
G1收集器的运作过程
- 初始标记(Initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacuation)
初始标记
初始标记仅仅只是标记一下GC Roots能之间关联到的对象。
并发标记
并发标记就是进行GC RootsTracing(可达性分析)的过程,
最终标记
进行标记修正,对因为用户程序持续运行而导致标记产生变动的那一部分对象进行标记。
筛选回收
最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。