目录
jvm收集器(旧)
Serial收集器
Serial收集器是最基础、历史最悠久的收集器,这个收集器是一个单线程工作的收集器,
但它的
“
单线程
”
的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强
调在它进行垃圾收集时,必须暂停其他所有工作线程(
Stop The World
),直到它收集结束

优于其他收集器的地方,那就是简单而高效(与其他收集器的单线程相比),对于内
存资源受限的环境,它是所有收集器里额外内存消耗
最小的;对于单核处理
器或处理器核心数较少的环境来说,
Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
Serial
收集器对于运行在客户端模式下的虚拟机来说是一个很好的选择。
总结: 新生代单线程收集器,使用标记复制算法工作时必须stop the world(木大木大木大)优点: 简单而高效(与其他收集器的单线程相比),额外内存消耗小缺点:stop the world时间过长(我的天,我开个qq卡我五分钟)
Serial Old收集器
Serial Old
是
Serial
收集器的老年代版本,它同样是一个单线程收集器,使用标记
-整理算法
这个收
集器的主要意义也是供客户端模式下的
HotSpot
虚拟机使用。如果在服务端模式下,它也可能有两种用途:一种是在
JDK 5
以及之前的版本中与
Parallel Scavenge
收集器搭配使用
,另外一种就是作为
CMS收集器发生失败时的后备预案

总结:老年代单线程收集器,使用标记整理算法
Serial收集器的老年代版本,用于搭配其他新生代收集器
ParNew收集器
ParNew
收集器实质上是
Serial收集器的多线程并行版本

ParNew收集器除了支持多线程并行收集之外,其他与Serial收集器相比并没有太多创新之处
但它
却是不少运行在服务端模式下的
HotSpot
虚拟机
其中有一个与功能、性能无关但其实很重要的原因是:除了
Serial
收集器外,目前只有它能与
CMS
收集器配合工作。
ParNew
收集器在单核心处理器的环境中绝对不会有比
Serial
收集器更好的效果,甚至由于存在线程
交互的开销,该收集器在通过超线程(
Hyper-Threading
)技术实现的伪双核处理器环境中都不能百分
之百保证超越
Serial
收集器
当然,随着可以被使用的处理器核心数量的增加,
ParNew
对于垃圾收集时
系统资源的高效利用还是很有好处的。它默认开启的收集线程数与处理器核心数量相同,在处理器
核心非常多(譬如
32
个,现在
CPU
都是多核加超线程设计,服务器达到或超过
32
个逻辑核心的情况非常普遍)的环境中,可以使用
-XX
:
ParallelGCThreads
参数来限制垃圾收集的线程数。
从
ParNew
收集器开始,后面还将会接触到若干款涉及
“
并发
”
和
“
并行
”
概念的收集器。
在大家可能产生疑惑之前,有必要先解释清楚这两个名词。并行和并发都是并发编程中的专业名词,
在谈论垃圾收集器的上下文语境中,它们可以理解为:
·并行(Parallel):并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线
程在协同工作,通常默认此时用户线程是处于等待状态。
·并发(Concurrent):并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾
收集器线程与用户线程都在运行。由于用户线程并未被冻结,所以程序仍然能响应服务请求,但由于垃圾收集器线程占用了一部分系统资源,此时应用程序的处理的吞吐量将受到一定影响。
总结:新生代收集器,使用标记复制算法Serial收集器的多线程并行版本(CMS收集器的工具人)
Parallel Scavenge收集器
Parallel Scavenge
收集器也是一款新生代收集器,它同样是基于标记
-
复制算法实现的收集器
Parallel Scavenge
收集器的特点是它的关注点与其他收集器不同,
CMS
等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而
Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。
所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值

如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了
100
分钟,其中垃圾收集花掉
1
分
钟,那吞吐量就是
99%
。停顿时间越短就越适合需要与用户交互或需要保证服务响应质量的程序,良
好的响应速度能提升用户体验
而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算
任务,主要适合在后台运算而不需要太多交互的分析任务。
Parallel Scavenge
收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间
的-XX:MaxGCPauseMillis
参数以及直接设置吞吐量大小的
-XX:GCTimeRatio参数。
由于与吞吐量关系密切,
Parallel Scavenge
收集器也经常被称作
“
吞吐量优先收集器“
Parallel Scavenge
收集器还有一个参数
-XX
:
+UseAdaptiveSizePolicy
值得我们关注
当这个参数被激活之后,就不需要人工指定新生代的大小(
-Xmn
)、
Eden
与
Survivor
区的比例(
-XX
:
SurvivorRatio
)、晋升老年代对象大小(
-XX
:
PretenureSizeThreshold
)等细节参数
了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量
这种调节方式称为垃圾收集的自适应的调节策略(
GC Ergonomics
)
总结:新生代多线程收集器,使用标记复制算法,相比其他收集器想缩短stop the world的时间,它选择了另一种道路,控制吞吐量可设置自动模式
Parallel Old收集器
Parallel Old
是
Parallel Scavenge
收集器的老年代版本,支持多线程并发收集,基于标记
-
整理算法实现
这个收集器是直到
JDK 6
时才开始提供的,在此之前,新生代的
Parallel Scavenge
收集器一直处于相当尴尬的状态,原因是如果新生代选择了
Parallel Scavenge
收集器,老年代除了
Serial Old
(
PSMarkSweep
)收集器以外别无选择,其他表现良好的老年代收集器,如
CMS
无法与它配合工作。由于
老年代
Serial Old
收集器在服务端应用性能上的
“
拖累
”
,使用
Parallel Scavenge
收集器也未必能在整体上
获得吞吐量最大化的效果。同样,由于单线程的老年代收集中无法充分利用服务器多处理器的并行处理能力,在老年代内存空间很大而且硬件规格比较高级的运行环境中,这种组合的总吞吐量甚至不一
定比
ParNew
加
CMS
的组合来得优秀。
在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑
Parallel Scavenge
加
Parallel Old
收集器

Parallel Scavenge收集器的老年代版本,多线程收集器,标记整理算法
与Parallel Scavenge收集器搭配,解决了Parallel Scavenge收集器的尴尬地位(老年代只有单线程收集器)
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
CMS收集器是基于标记-清除算法实现的
它的运作
过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:
1
)初始标记(
CMS initial mark
)
2
)并发标记(
CMS concurrent mark
)
3
)重新标记(
CMS remark
)
4
)并发清除(
CMS concurrent sweep
)
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC
Roots能直接关联到的对象,速度很快
并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对
象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行
而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
,这个阶段的停顿时间通常会比初始标记阶段稍长一
些,但也远比并发标记阶段的时间短
并发清除阶段,清理删除掉标记阶段判断的已经死亡的
对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一
起工作,所以从总体上来说,
CMS
收集器的内存回收过程是与用户线程一起并发执行的。通过图
可以比较清楚地看到
CMS
收集器的运作步骤中并发和需要停顿的阶段

CMS
是一款优秀的收集器,它最主要的优点在名字上已经体现出来:并发收集、低停顿,一些官
方公开文档里面也称之为
“
并发低停顿收集器
”
缺点:
首先,
CMS
收集器对处理器资源非常敏感。事实上,面向并发设计的程序都对处理器资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程(或者说处理器计
算能力)而导致应用程序变慢,降低总吞吐量
CMS
默认启动的回收线程数是(处理器核心数量+3
)
/4
,也就是说,如果处理器核心数在四个或以上,并发回收时垃圾收集线程只占用不超过
25%
的
处理器运算资源,并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时,
CMS
对用户程序的影响就可能变得很大
由于CMS收集器无法处理“浮动垃圾”(Floating Garbage),有可能出现“Con-current Mode
Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。在CMS的并发标记和并发清理阶
段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分
垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留待下一次垃圾收集时再清理掉。这一部分垃圾就称为“浮动垃圾”。
同样也是由于在垃圾收集阶段用户线程还需要持续运
行,那就还需要预留足够内存空间提供给用户线程使用,因此
CMS
收集器不能像其他收集器那样等待到老年代几乎完全被填满了再进行收集,必须预留一部分空间供并发收集时的程序运作使用
CMS
收集器当老年代使用了
68%
的空间后就会被激活,这是一个偏保守的设置,如果
在实际应用中老年代增长并不是太快,可以适当调高参数
-XX
:
CMSInitiatingOccu-pancyFraction
的值来提高
CMS
的触发百分比,降低内存回收频率,获取更好的性能。到了
JDK 6
时,
CMS
收集器的启动
阈值就已经默认提升至
92%
但这又会更容易面临另一种风险:要是
CMS运行期间预留的内存无法满
足程序分配新对象的需要,就会出现一次“并发失败”(Concurrent Mode Failure),这时候虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾集,
但这样停顿时间就很长了。所以参数
-XX
:
CMSInitiatingOccupancyFraction
设置得太高将会很容易导致
大量的并发失败产生,性能反而降低
还有最后一个缺点,
CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。
多线程老年代收集器,标记清除算法实现优点:并发收集,停顿低缺点:并发阶段占用线程无法处理浮动垃圾预留空间需要自己把握(预留空间多了,降低了内存回收频率,少了容易并发失败)标记清除算法的空间碎片