JVM内存管理之垃圾搜集器精解(让你在垃圾搜集器的世界里耍的游刃有余)
引言
在上一章我们已经探讨过hotspot上垃圾搜集器的实现,一共有六种实现六种组合。本次LZ与各位一起探讨下这六种搜集器各自的威力以及组合的威力如何。
为了方便各位的观看与对比,LZ决定采用当初写设计模式时使用的方式,针对某些搜集器,分几个维度去解释这些搜集器。
client模式与server模式
在介绍本章内容之前,要说一下JVM的两种模式,一种是client模式,一种是server模式。我们平时开发使用的模式默认是client模式,也可以使用命令行参数-server强制开启server模式,两者最大的区别在于在server模式下JVM做了很多优化。
server模式下的JAVA应用程序启动较慢,不过由于server模式下JVM所做的优化,在程序长时间运行下,运行速度将会越来越快。相反,client模式下的JAVA应用程序虽然启动快,但不适合长时间运行,若是运行时间较长的话,则会在性能上明显低于server模式。
搜集器详解
以下我们先探讨一下单个垃圾搜集器的相关内容,最后我们再简单的谈一下组合之后,各个组合的特点。
Serial Garbage Collector
算法:采用复制算法
内存区域:针对新生代设计
执行方式:单线程、串行
执行过程:当新生代内存不够用时,先暂停全部用户程序,然后开启一条GC线程使用复制算法对垃圾进行回收,这一过程中可能会有一些对象提升到年老代
特点:由于单线程运行,且整个GC阶段都要暂停用户程序,因此会造成应用程序停顿时间较长,但对于小规模的程序来说,却非常适合。
适用场景:平时的开发与调试程序使用,以及桌面应用交互程序。
开启参数:-XX:+UseSerialGC(client模式默认值)
Serial Old Garbage Collector
这里针对serial old搜集器不再列举各个维度的特点,因为它与serial搜集器是一样的,区别是它是针对年老代而设计的,因此采用标记/整理算法。对于其余的维度特点,serial old与serial搜集器一模一样。
ParNew Garbage Collector
算法:采用复制算法
内存区域:针对新生代设计
执行方式:多线程、并行
执行过程:当新生代内存不够用时,先暂停全部用户程序,然后开启若干条GC线程使用复制算法并行进行垃圾回收,这一过程中可能会有一些对象提升到年老代
特点:采用多线程并行运行,因此会对系统的内核处理器数目比较敏感,至少需要多于一个的处理器,有几个处理器就会开几个线程(不过线程数是可以使用参数-XX:ParallelGCThreads=<N>控制的),因此只适合于多核多处理器的系统。尽管整个GC阶段还是要暂停用户程序,但多线程并行处理并不会造成太长的停顿时间。因此就吞吐量来说,ParNew要大于serial,在处理器越多的时候,效果越明显。但是这并非绝对,对于单个处理器来说,由于并行执行的开销(比如同步),ParNew的性能将会低于serial搜集器。不仅是单个处理器的时候,如果在容量较小的堆上,甚至在两个处理器的情况下,ParNew的性能都并非一定可以高过serial。
适用场景:在中到大型的堆上,且系统处理器至少多于一个的情况
开启参数:-XX:+UseParNewGC
Parallel Scavenge Garbage Collector
这个搜集器与ParNew几乎一模一样,都是针对新生代设计,采用复制算法的并行搜集器。它与ParNew最大的不同就是可设置的参数不一样,它可以让我们更精确的控制GC停顿时间以及吞吐量。
parallel scavenge搜集器提供参数主要包括控制最大的停顿时间(使用-XX:MaxGCPauseMillis=<N>),以及控制吞吐量(使用-XX:GCTimeRatio=<N>)。由此可以看出,parallel scavenge就是为了提供吞吐量控制的搜集器。
不过千万不要以为把最大停顿时间调的越小越好,或者吞吐量越大越好,在使用parallel scavenge搜集器时,主要有三个性能指标,最大停顿时间、吞吐量以及新生代区域的最小值。
parallel scavenge搜集器具有相应的调节策略,它将会优先满足最大停顿时间的目标,次之是吞吐量,最后才是新生代区域的最小值。
因此,如果将最大停顿时间调的过小,将会牺牲整体的吞吐量以及新生代大小来满足你的私欲。手心手背都是肉,我们最好还是不要这么干。不过parallel scavenge有一个参数可以让parallel scavenge搜集器全权接手内存区域大小的调节,这其中还包括了晋升为年老代(可使用-XX:MaxTenuringThreshold=n调节)的年龄,也就是使用-XX:UseAdaptiveSizePolicy打开内存区域大小自适应策略。
parallel scavenge搜集器可使用参数-XX:+UseParallelGC开启,同时它也是server模式下默认的新生代搜集器。
Parallel Old Garbage Collector
Parallel Old与ParNew或者Parallel Scavenge的关系就好似serial与serial old一样,相互之间的区别并不大,只不过parallel old是针对年老代设计的并行搜集器而已,因此它采用标记/整理算法。
Parallel Old搜集器还有一个重要的意义就是,它是除了serial old以外唯一一个可以与parallel scavenge搭配工作的年老代搜集器,因此为了避免serial old影响parallel scavenge可控制吞吐量的名声,parallel old就作为了parallel scavenge真正意义上的搭档。
它可以使用参数-XX:-UseParallelOldGC开启,不过在JDK6以后,它也是在开启parallel scavenge之后默认的年老代搜集器。
Concurrent Mark Sweep Garbage Collector
concurrent mark sweep(以下简称CMS)搜集器是唯一一个真正意义上实现了应用程序与GC线程一起工作(一起是针对客户而言,而并不一定是真正的一起,有可能是快速交替)的搜集器。
CMS是针对年老代设计的搜集器,并采用标记/清除算法,它也是唯一一个在年老代采用标记/清除算法的搜集器。
采用标记/清除算法是因为它特殊的处理方式造成的,它的处理分为四个阶段。
1、初始标记:需要暂停应用程序,快速标记存活对象。
2、并发标记:恢复应用程序,并发跟踪GC Roots。
3、重新标记:需要暂停应用程序,重新标记跟踪遗漏的对象。
4、并发清除:恢复应用程序,并发清除未标记的垃圾对象。
它比原来的标记/清除算法复杂了点,主要表现在并发标记和并发清除这两个阶段,而这两个阶段也是整个GC阶段中耗时最长的阶段,不过由于这两个阶段皆是与应用程序并发执行的,因此CMS搜集器造成的停顿时间是非常短暂的。这点还是比较好理解的。
不过它的缺点也是要简单提一下的,主要有以下几点。
1、由于GC线程与应用程序并发执行时会抢占CPU资源,因此会造成整体的吞吐量下降。也就是说,从吞吐量的指标上来说,CMS搜集器是要弱于parallel scavenge搜集器的。LZ这里从oracle官网上摘录下一段关于CMS的描述,里面提到CMS性能与CPU个数的关系。
Since at least one processor is utilized for garbage collection during the concurrent phases, the concurrent collector does not normally provide any benefit on a uniprocessor (single-core) machine. However, there is a separate mode available that can achieve low pauses on systems with only one or two processors; see incremental mode below for details.
LZ的英文很一般(四级都没过,惭愧,0.0),不过在借助工具的情况下也能大致翻译出来这段话的意思,如下。
中文大意:由于在并发阶段垃圾搜集至少使用了一个处理器,因此在单处理器的情况下使用并发搜集器,将得不到任何好处。不过,在单个或两个处理器的系统上,有一种独立的方式可以有效的达到低停顿的目的,详情见下方的增量模式(incremental mode)。
很明显,oracle的文档指出,在单处理器的情况下,并发搜集器会因为抢占处理器,而造成性能降低。最后给出了一种增量模式的处理方式,不过在《深入理解JAVA虚拟机》一书中指出,增量模式已经被定义为不推荐使用。由于LZ摘录的这段官方介绍是基于JDK5.0的介绍,而《深入理解JAVA虚拟机》一书中则是指的JDK6.0的版本,因此LZ暂且猜测,增量模式是在JDK6.0发布的时候被废弃了,不过这个废弃的时间或者说版本其实已经不重要了。
2、标记/清除很大的一个缺点,那就是内存碎片的存在。因此JVM提供了-XX:+UseCMSCompactAtFullCollection参数用于在全局GC(full GC)后进行一次碎片整理的工作,由于每次全局GC后都进行碎片整理会较大的影响停顿时间,JVM又提供了参数-XX:CMSFullGCsBeforeCompaction去控制在几次全局GC后会进行碎片整理。
3、CMS最后一个缺点涉及到一个术语---并发模式失败(Concurrent Mode Failure)。对于这个术语,官方是这样解释的。
if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfied with the available free space blocks in the tenured generation, then the application is paused and the collection is completed with all the application threads stopped.The inability to complete a collection concurrently is referred to as concurrent mode failure and indicates the need to adjust the concurrent collector parameters.
中文大意:如果并发搜集器不能在年老代填满之前完成不可达(unreachable)对象的回收,或者年老代中有效的空闲内存空间不能满足某一个内存的分配请求,此时应用会被暂停,并在此暂停期间开始垃圾回收,直到回收完成才会恢复应用程序。这种无法并发完成搜集的情况就成为并发模式失败(concurrent mode failure),而且这种情况的发生也意味着我们需要调节并发搜集器的参数了。
上面两个情况感觉有点重复,不能满足内存的分配请求不就是在年老代填满之前,没有完成对象回收造成的吗?
这里LZ个人的理解是,年老代填满之前无法完成对象回收是指年老代在并发清除阶段清除不及时,因此造成的空闲内存不足。而不能满足内存的分配请求,则主要指的是新生代在提升到年老代时,由于年老代的内存碎片过多,导致一些分配由于没有连续的内存无法满足。
实际上,在并发模式失败的情况下,serial old会作为备选搜集器,进行一次全局GC(Full GC),因此serial old也算是CMS的“替补”。显然,由于serial old的介入,会造成较大的停顿时间。
为了尽量避免并发模式失败发生,我们可以调节-XX:CMSInitiatingOccupancyFraction=<N>参数,去控制当年老代的内存占用达到多少的时候(N%),便开启并发搜集器开始回收年老代。
组合的威力
上面我们已经简单的介绍了各个搜集器的特点,下面LZ与各位分享三个典型的组合,其余三种组合一般不常用。
serial & serial old
这个组合是我们最常见的组合之一,也是client模式下的默认垃圾搜集器组合,也可以使用参数-XX:+UseSerialGC强制开启。
由于它实现相对简单,没有线程相关的额外开销(主要指线程切换与同步),因此非常适合运行于客户端PC的小型应用程序,或者桌面应用程序(比如swing编写的用户界面程序),以及我们平时的开发、调试、测试等。
上面三种情况都有共同的特点。
1、由于都是在PC上运行,因此配置一般不会太高,或者说处理器个数不会太多。
2、上面几种情况的应用程序都不会运行太久。
3、规模不会太大,也就是说,堆相对较小,搜集起来也比较快,停顿时间会比较短。
Parallel Scavenge & Parallel Old
这个组合我们并不常见,毕竟它不会出现在我们平时的开发当中,但是它却是很多对吞吐量(throughout)要求较高或者对停顿时间(pause time)要求不高的应用程序的首选,并且这个组合是server模式下的默认组合(JDK6或JDK6之后)。当然,它也可以使用-XX:+UseParallelGC参数强制开启。
该组合无论是新生代还是年老代都采用并行搜集,因此停顿时间较短,系统的整体吞吐量较高。它适用于一些需要长期运行且对吞吐量有一定要求的后台程序。
这些运行于后台的程序都有以下特点。
1、系统配置较高,通常情况下至少四核(以目前的硬件水平为准)。
2、对吞吐量要求较高,或需要达到一定的量。
3、应用程序运行时间较长。
4、应用程序规模较大,一般是中到大型的堆。
ParNew & CMS(Serial Old作为替补)
这个组合与上面的并行组合一样,在平时的开发当中都不常见,而它则是对相应时间(response time)要求较高的应用程序的首选。该组合需要使用参数-XX:+UseConcMarkSweepGC开启。
该组合在新生代采用并行搜集器,因此新生代的GC速度会非常快,停顿时间很短。而年老代的GC采用并发搜集,大部分垃圾搜集的时间里,GC线程都是与应用程序并发执行的,因此造成的停顿时间依然很短。它适用于一些需要长期运行且对相应时间有一定要求的后台程序。
这些运行于后台的程序的特点与并行模式下的后台程序十分类似,不同的是第二点,采用ParNew & CMS组合的后台应用程序,一般都对相应时间有一定要求,最典型的就是我们的WEB应用程序。
结束语
本次LZ整理了各个搜集器的特点与各个组合的特点,此外,还有剩下的三种组合LZ这里没有提到,原因是这三种组合都不是特别常用,或者可以说几乎不用,因为这三个组合都给人一种四不像的感觉,而且效果也确实不好。
希望本文能给各位带来一些帮助,感谢各位的收看。
转载自一位前辈,地址:http://www.cnblogs.com/zuoxiaolong
JVM内存管理之垃圾搜集器精解(让你在垃圾搜集器的世界里耍的游刃有余)的更多相关文章
- JVM内存管理------垃圾搜集器参数精解
本文是GC相关的最后一篇,这次LZ只是罗列一下hotspot JVM中垃圾搜集器相关的重点参数,以及各个参数的解释.废话不多说,这就开始. 垃圾搜集器文章传送门 JVM内存管理------JAVA语言 ...
- JVM内存管理之垃圾搜集器参数精解
本文是GC相关的最后一篇,这次LZ只是罗列一下hotspot JVM中垃圾搜集器相关的重点参数,以及各个参数的解释.废话不多说,这就开始. 垃圾搜集器文章传送门 JVM内存管理------JAVA语言 ...
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收
很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...
- JVM内存管理及垃圾回收
一.JVM内存的构 Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法.总体分为下面几个部分: 程序计数器(Progra ...
- JVM内存管理及垃圾回收【转】
很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...
- JVM内存管理机制和垃圾回收机制
JVM内存管理机制和垃圾回收机制 JVM结构 图片描述: java源码编译成class文件 class文件通过类加载器加载到内存 其中方法区存放的是运行时的常量.静态变量.类信息等,被所有线程共享 堆 ...
- JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)
转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...
- JVM内存管理及垃圾回收机制
一.JVM内存组成结构 JVM栈由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 二.JVM内存回收 Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:把对 ...
- JVM内存管理和垃圾回收机制介绍
http://backend.blog.163.com/blog/static/20229412620128233285220/ 内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实 ...
随机推荐
- [转载]各种java生成word解决方案的优缺点对比
解决方案 优点 缺点 Jacob 功能强大 直接调用VBA接口,程序异常复杂:服务器必须是:windows系统+安装Office:服务器端自动化com接口容易产生死进程造成服务器宕机 Apache P ...
- HDU 2669 Romantic (扩展欧几里得定理)
Romantic Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- elasticsearch实现网站搜索
使用elasticsearch 实现网站搜索,可以支持商品搜索,筛选项过滤搜索 ,价格排序, 打分 筛选项聚合,还有其他综合排序 后续推出搜索人工干预排序,根据销量,好评率,售卖率 进行全方位的搜索实 ...
- UI-基本控件的简单使用
1.IBAction: //====================== 1> 能保证方法可以连线 2> 相当于void 2.IBOutlet: 1> 能保证属性可以连线 3.常 ...
- Code Cache相关知识总结
codecache代码缓存区,主要存放JIT所编译的代码,同时还有Java所使用的本地方法代码也会存储在codecache中.不同的jvm.不同的启动方式codecache的默认值大小也不尽相同. J ...
- 状态保持: cookier及session简介
状态保持 因为 http 是一种无状态协议,浏览器请求服务器是无状态的. 无状态:指一次用户请求时,浏览器.服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求. 无状态原因:浏览器与服务器是 ...
- java中a++和++a的区别详解
java中的++操作无论在前还是在后,都是在变量自身的值加1,接下来将具体描述两者的区别 int a =5; int b =a++; System.out.println(b); 1.通俗易懂的理解是 ...
- vue.js 源代码学习笔记 ----- 工具方法 debug
import config from '../config' import { noop } from 'shared/util' let warn = noop let tip = noop let ...
- hdu 1098
http://acm.hdu.edu.cn/showproblem.php?pid=1098 假设x=m时,65|f(m),即65|5*m^13+13*m^5+k*a*m 计算f(m+1)=(5*m^ ...
- iOS通讯录相关知识-浅析
本文来自于:贞娃儿的博客 http://blog.sina.com.cn/zhenwawaer 在开发一些应用中,我们如果需要iPhone设备中的通讯录信息.或者,需要开发通讯录相关的一些功能.那 ...