文章目录
- 垃圾收集器介绍总结
- 各个垃圾收集器之间的关系
- 垃圾收集器使用命令及默认值
- 详解各个垃圾收集器
- Serial
- ParNew
- Parallel Scavenge
- Serial Old
- Parallel Old
- CMS(Concurrent Mark Sweep)
- G1(Garbage First)
- 适用场景及推荐
垃圾收集器介绍总结
垃圾收集器可以帮助我们进行具体的垃圾回收操作,在Java中,有几款非常经典的垃圾回收器,这些经典的垃圾收集器尽管已经算不上是最先进的技术,但是它们曾在实践中千锤百炼,足够成熟,基本上可以认为是可以在生产环境中放心使用的全部垃圾收集器了.
目前主要有7款经典的垃圾回收器,笔者对这几种进行了总结,如下图所示.
可以直观的理解各个垃圾回收器的区别.
这里对吞吐量额外做一下说明:
吞吐量就是处理器用于 运行用户代码的时间与处理器总消耗时间 的比值.
也就是:
如果虚拟机完成某个任务,用户代码加上垃圾收集器总共消耗的100分钟,其中运行垃圾收集花费1分钟,那么吞吐量就是99%.
停顿时间越短就越适合与用户发生交互或需要保证响应质量的程序,比如基于浏览器的B/S系统的服务端程序,响应时间越短,用户体验越好; 而吞吐量高则可以尽快的完成程序的运算任务,可以更高效的利用处理器资源, 主要适合在后台运算而不需要太多交互的任务.
各个垃圾收集器之间的关系
各个垃圾收集器并不是可以随意搭配使用的,而是存在一定的搭配关系,如下图所示.
如果不同收集器之间的存在连线,则表示它们可以搭配使用.图中垃圾收集器所处的区域,则表示它们属于新生代垃圾收集器亦或是老年代垃圾收集器.
垃圾收集器使用命令及默认值
那我们怎么去使用限定使用这些垃圾收集器呢,可以使用以下命令:
目前大多数生产环境都使用的是jdk8, 在server模式下,默认值是UseParallelGC,也就是新生代默认是Parallel Scavenge垃圾回收器,老年代默认Serial Old垃圾收集器.
jdk9以后server模式下默认是使用G1垃圾回收器.
查看jvm使用的垃圾回收器:
命令行: java -server -XX:+PrintCommandLineFlags -version
详解各个垃圾收集器
Serial
它是最基础,历史上最悠久的收集器, 是一个新生代的单线程收集器,标记和清理都是单线程,优点是简单高效;在工作的时候必须"stop the world".
虽然它是单线程的,但并非没有用处,它仍旧是HotSpot虚拟机运行在客户端模式下的新生代默认垃圾收集器.因为在内存资源首先的环境下,它是所有收集器里消耗额外内存最小的.
同时,对于并行能力较弱的计算机来说,串行回收器的专注性和独占性往往有更好的性能表现。
ParNew
新生代收并行集器,实际上是Serial收集器的多线程版本,只是简单的将Serial并行化, 在多核CPU环境下有着比Serial更好的表现;对于其他回收策略,收集算法等等均与Serial一致.在实现上这两种收集器也共用了相当多的代码.
该收集器也是激活CMS后,新生代的默认垃圾收集器.一般和CMS进行搭配使用.
Parallel Scavenge
也是一款新生代并行收集器,同样是基于标记整理算法, 表面上和ParNew非常相似,但最大的不同点就是,Parallel Scavenge的目标在于提升吞吐量.它也是jdk8在server模式下的新生代默认收集器.
由于与吞吐量密切相关,Parallel Scavenge有一个参数还是比较重要的,-XX:+UseAdaptiveSizePolicy.这是一个开关,当这个参数被激活后,就不需要人工指定新生代,老年代大小等细节参数了,虚拟机会根据当前系统的运行情况动态调整这些参数以获得最合适的停顿时间或最大的吞吐量.这种调节方式被称为垃圾收集器的自适应调节策略.
如果你对于收集器运作不太了解,手工优化存在困难的话,使用这个自适应调节策略,把内存管理优化交给虚拟机完成也许是一个很不错的选择.只需要把最基本的设置好(如最大堆).然后使用-XX:MaxGCPauseMillis参数(更关注停顿时间)或XX:GCTimeRatio参数(更关注吞吐量)给虚拟机设计一个优化目标,具体细节的参数调优就可以交给虚拟机完成了.
-XX:MaxGCPauseMillis:最大垃圾收集暂停时间,单位为毫秒,如:-XX:MaxGCPauseMillis=200,表示垃圾收集暂停时间最大为200毫秒。默认情况下,没有指定最大垃圾收集暂停时间。如果指定了暂停时间目标,则会调整堆大小与垃圾收集相关的其他参数,使垃圾收集的暂停时间短于指定值。这些调整可能导致降低应用的整体吞吐量,也有可能无法始终满足所指定的最大垃圾收集暂停时间目标。
-XX:GCTimeRatio:吞吐量大小,如:-XX:GCTimeRatio=19,表示将垃圾收集运行时间的目标设定为应用总运行时间(用户代码运行时间+垃圾收集运行时间)的1/(1+19),即5%。默认值为99,垃圾收集的目标时间占应用总运行时间的1/(1+99),即1%.
Serial Old
Serial Old收集器是Serial的老年代版本,采用标记-整理算法,是一个单线程收集器,这个收集器的主要意义也是提供客户端模式下的HotSpot虚拟机使用.
在server模式下可能也有两种应用: 一种是与Parallel Scavenge搭配使用,一种是作为CMS收集器发生失败后的后备预案.
Parallel Old
Parallel Scavenge收集器的老年代版本;采用标记-整理算法,支持多线程并发收集.同样追求高吞吐量.
该收集器在jdk6才出来,在之前Parallel Scavenge只能与Serial Old搭配使用,但由于单线程的老年代收集中无法充分利用服务多处理器并行处理的优势,在老年代内存空间很大的情况下这种组合的吞吐量甚至不一定比ParNew+CMS组合更加优秀.
直到Parallel Old出现后,"吞吐量优先"才算有了名副其实的搭配,在注重吞吐量或者处理器资源较为稀缺的情况下都可以优先考虑Parallel Scavenge+Parallel Old组合.
CMS(Concurrent Mark Sweep)
老年代并行收集器,采用标记清除法, 追求最短回收停顿时间,具有高并发、低停顿的特点.
目前很大一部分Java应用集中在网站上或基于浏览器的B/S架构,这类应用非常关注服务的响应速度,希望停顿时间更短,CMS就非常符合这类需求.
CMS并不是独占的回收器,在CMS回收过程中,应用程序仍然在不停的工作,又会有新的垃圾不断产生,在使用CMS的过程中应该确保应用程序的内存足够可用。CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阀值的时候开始回收,回收阀值可用指定的参数进行配置,-XX:CMSInitiatingOccupancyFraction来指定,默认为68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC的停顿时间可能较长,所以-XX:CMSInitiatingOccupancyFraction的设置要根据实际的情况.
CMS是HotSpot追求低停顿的第一次成功尝试,有自己的优点,但还是有一些缺点:
-
在处理器核心数量不足4个时,CMS对应用程序的影响可能变得很大,可能导致用户程序速度忽然大幅降低,因为其会占用一部分处理器资源.
-
当内存回收使用增长过快时,CMS回收失败时,就会Full GC,启用老年代串行回收器进行回收
-
其采用标记清除法,会产生空间碎片的问题,处理这些碎片又会花费较多时间
G1(Garbage First)
G1收集器是垃圾收集器技术发展史上里程碑式的成果.目前已经是jdk9及以上版本的server模式下的默认垃圾收集器了.
之前的收集器要么工作在新生代,要么工作在老年代,而G1跳出了这个樊笼,它可以面向堆内任何部分进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量更多,回收受益最大,这就是G1收集器的Mixed GC模式.
G1作为CMS未来的继承人,追求低延迟,但并非纯粹的追求低延迟,官方给其设定的目标是在延迟可控的情况下获得尽可能高的吞吐量.所以担当起了"全功能收集器"的重任与期望.
G1把连续的Java堆分成多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间,survivo空间,亦或是老年代空间,收集器能够对扮演不同角色的region采用不同的策略去进行回收,这样无论是刚创建的对象,还是已经存活了一段时间的对象等都能获得很好的收集结果.
G1相比CMS有很多的优点,比如说不会产生空间碎片、回收之后能够提供规整的可用内存、更不容易产生Full GC等等.但G1对对于CMS的碾压并不是全方位的,例如:G1为了垃圾回收而使用的内存和运行时的额外负载都比CMS要高.
适用场景及推荐
以上七款经典的垃圾回收器,并没有绝对的好坏之分,更不存在"万能"的垃圾收集器.所以我们只能根据具体应用,具体环境去选择最适合的一个垃圾收集器.
首先要明确一个观点,如果默认的收集器没有达到预期的性能,那么首先尝试调整堆和代的大小以满足预期的目标。如果性能仍然不够,再尝试不同的收集器:使用并发收集器来减少暂停时间,并使用并行收集器来提高多处理器硬件上的总体吞吐量。
- 如果应用程序就是一个小内存(最多大约100 MB),建议使用选项-XX:+UseSerialGC选择串行收集器。
- 如果应用程序将在单个处理器上运行,并且没有暂停时间要求,那么使用选项-XX:+UseSerialGC选择串行收集器。
- 如果应用程序性能、吞吐量是第一优先级,并且没有暂停时间要求,一秒钟或更长时间的暂停都是可以接受的,那么建议使用-XX:+UseParallelGC或-XX:+UseParallelOldGC 选择并行收集器。纯后台程序无交互,并且是多处理器的,推荐考虑-XX:+UseParallelOldGC
- 如果响应时间比总体吞吐量更重要,比如说与用户进行交互的,垃圾收集暂停必须很短,那么可以CMS或G1作为垃圾收集器,-XX:+UseG1GC或-XX:+UseConcMarkSweepGC
目前在小内存的应用上,CMS的表现大概率仍会优于G1,而在大内存应用上,G1则大多能发挥其优势,这个优劣势的堆容量平衡点在6-8G之间.
今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.