垃圾收集器
垃圾收集器
简介
如果说收集算法是内存回收的方法论《GC算法》,那么垃圾收集器就是内存回收的具体实现。java虚拟机规范中对应垃圾收集器如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的垃圾收集器可能会有很大差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合各个年代所用的收集器。
概括
JVM是一个进程,垃圾收集器就是一个线程,垃圾收集线程是一个守护线程,优先级极低,其在当前系统空闲或堆中老年代占用率较大时出发。
新生代收集器:Serial,ParNew,Parallel Scavenge
老年代收集器:Serial Old,Parallel Old,CMS

串行垃圾收集器与并行垃圾收集器很相似,只是它是用一个单线程做所有工作。这种单线程的方式意味着垃圾收集器实现的复杂度更低,以及需要非常少的外部运行时数据结构,其内存占用空间大小(footprint)也是所有HotSpot 垃圾收集器里***的。当然,串行垃圾收集器面对的挑战与并行收集器也非常相似,中断时间可能很长,同时随着堆的大小以及活跃数据的数量变化,中断时间会呈线性增加或减少。另外,串行垃圾收集器引发的长暂停会更加明显,因为所有垃圾收集工作都是在一个线程里完成。
因为很少占用内存,在Java HotSpot 客户端虚拟机中默认使用串行垃圾收集器,同时它还被用于大量嵌入式场景的需求。通过HotSpot 命令行选项-XX:+UseSerialGC,明确指定了使用串行垃圾收集器来做垃圾收集。
2.并发标记清除(CMS)垃圾收集器
2.1ParNew 收集器
它是 Serial 收集器的多线程版本。
是 Server 模式下的虚拟机首选新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合工作。
默认开启的线程数量与 CPU 数量相同,可以使用 -XX:ParallelGCThreads 参数来设置线程数。
2.2CMS收集器
CMS收集器是一款具有划时代意义的收集器,一款正真意义上的并发收集器,虽然现在已经有了理论意义上表现更好的G1收集器,但现在主流互联网企业选用的仍然是CMS。
CMS运作分四步:
1,初始标记:仅标记一下GC Roots能直接关联到的对象;速度很快;但需要"Stop The World"。
2,并发标记:并行GC Root Tracing的过程;刚才产生的集合中标记出存活对象;应用程序也在运行;并不保证可以标记出所有的存活对象。
3,重新标记:为了修正并发标记期间因用户程序继续运作致使标记变动的那一部分对象的标记记录;需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;采用多线程并行执行来提升效率。
4,并发清除:回收所有的垃圾对象。

CMS收集器运行
老年代收集,采用"标记-清除"算法,与用户线程并发运行
应用场景:与用户交互较多的场景;希望系统停顿时间较短,注重服务的响应速度;以给用户带来较好的体验。
由于CMS采用"标记-清除"算法,可能产生大量内存碎片,内存碎片过多可能导致无法分配大对象而提前触发Full GC,因此CMS提供了
-XX:+UseCMSCompactAtFullCollection开关参数,用于在Full GC后再执行一次碎片整理过程,但内存整理是无法并发的,内存碎片问题虽然没有了,但停顿时间也因此变长了,因此CMS还提供了另外一个参数
-XX:CMSFullGCBeforeCompaction用于设置在执行N次不进行内存整理的Full GC后,跟着一次带整理的(默认为0:每次进入Full GC时都进行碎片整理
3.G1 垃圾收集器(Garbage Collector)
G1 垃圾收集器
G1(Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征. 在 OracleJDK7update4 及以上版本中得到完全支持, 专为以下应用程序设计:
-
可以像CMS收集器一样,GC操作与应用的线程一起并发执行
-
紧凑的空闲内存区间且没有很长的GC停顿时间.
-
需要可预测的GC暂停耗时.
-
不想牺牲太多吞吐量性能.
-
启动后不需要请求更大的Java堆.
G1的长期目标是取代CMS( Concurrent``Mark-SweepCollector, 并发标记-清除). 因为特性的不同使 G1 成为比 CMS 更好的解决方案. 一个区别是, G1 是一款压缩型的收集器. G1 通过有效的压缩完全避免了对细微空闲内存空间的分配,不用依赖于 regions,这不仅大大简化了收集器,而且还消除了潜在的内存碎片问题。除压缩以外, G1 的垃圾收集停顿也比 CMS容易估计,也允许用户自定义所希望的停顿参数( pause targets)
G1 操作概述
上一代的垃圾收集器(串行 serial, 并行 parallel, 以及 CMS )都把堆内存划分为固定大小的三个部分: 年轻代( young generation), 年老代( old generation), 以及持久代( permanent generation).

内存中的每个对象都存放在这三个区域中的一个.
而 G1 收集器采用一种不同的方式来管理堆内存.

堆内存被划分为多个大小相等的 heap 区,每个 heap 区都是逻辑上连续的一段内存( virtual memory). 其中一部分区域被当成老一代收集器相同的角色( eden, survivor, old), 但每个角色的区域个数都不是固定的。这在内存使用上提供了更多的灵活性。
G1执行垃圾回收的处理方式与 CMS 相似. G1 在全局标记阶段( global marking phase)并发执行, 以确定堆内存中哪些对象是存活的。标记阶段完成后,G1就可以知道哪些heap区的empty空间最大。
它会首先回收这些区,通常会得到大量的自由空间. 这也是为什么这种垃圾收集方法叫做 Garbage-First(垃圾优先)的原因。顾名思义, G1 将精力集中放在可能布满可收回对象的区域, 可回收对象( reclaimable objects)也就是所谓的垃圾. G1 使用暂停预测模型( pause prediction model)来达到用户定义的目标暂停时间。
并根据目标暂停时间来选择此次进行垃圾回收的 heap 区域数量被 G1 标记为适合回收的 heap 区将使用转移( evacuation)的方式进行垃圾回收. G1 将一个或多个 heap 区域中的对象拷贝到其他的单个区域中,并在此过程中压缩和释放内存. 在多核 CPU 上转移是并行执行的( parallel on multi-processors), 这样能减少停顿时间并增加吞吐量。
因此,每次垃圾收集时, G1 都会持续不断地减少碎片, 并且在用户给定的暂停时间内执行. 这比以前的方法强大了很多. CMS 垃圾收集器( Concurrent Mark Sweep,并发标记清理)不进行压缩. ParallelOld 垃圾收集只对整个堆执行压缩,从而导致相当长的暂停时间。
需要强调的是, G1 并不是一款实时垃圾收集器( real-time collector). 能以极高的概率在设定的目标暂停时间内完成,但不保证绝对在这个时间内完成。基于以前收集的各种监控数据, G1 会根据用户指定的目标时间来预估能回收多少个 heap 区. 因此,收集器有一个相当精确的 heap 区耗时计算模型,并根据该模型来确定在给定时间内去回收哪些 heap 区.
注意 G1 分为两个阶段: 并发阶段( concurrent, 与应用线程一起运行, 如: 细化 refinement、标记 marking、清理 cleanup) 和 并行阶段( parallel, 多线程执行, 如: 停止所有 JVM 线程, stop the world). 而 FullGC (完整垃圾收集)仍然是单线程的, 但如果进行适当的调优,则应用程序应该能够避免 full GC。
G1 的内存占用(Footprint)
如果从 ParallelOldGC 或者 CMS 收集器迁移到 G1, 您可能会看到JVM进程占用更多的内存( a larger JVM process size). 这在很大程度上与 “ accounting” 数据结构有关, 如 Remembered Sets 和 Collection Sets.
Remembered Sets 简称 RSets, 跟踪指向某个 heap 区内的对象引用. 堆内存中的每个区都有一个 RSet. RSet 使 heap 区能并行独立地进行垃圾集合. RSets 的总体影响小于5%.
Collection Sets 简称 CSets, 收集集合, 在一次 GC 中将执行垃圾回收的 heap 区. GC 时在 CSet 中的所有存活数据( live data)都会被转移(复制/移动). 集合中的 heap 区可以是 Eden, survivor, 和/或 old generation. CSets 所占用的 JVM 内存小于 1%.


浙公网安备 33010602011771号