标签:
本文是基于周志明的《深入理解Java虚拟机》
如果说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器都可能会有很大的差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。
图展示了7种作用于不同分代的收集器(包括JDK 1.6_Update14后引入的Early Access版G1收集器),如果两个收集器之间存在连线,就说明它们可以搭配使用。
在介绍这些收集器各自的特性之前,我们先来明确一个观点:虽然我们是在对各个收集器进行比较,但并非为了挑选一个最好的收集器出来。因为直到现在为止还没有最好的收集器出现,更加没有万能的收集器,所以我们选择的只是对具体应用最合适的收集器。这点不需要多加解释就能证明:如果有一种放之四海皆准、任何场景下都适用的完美收集器存在,那HotSpot虚拟机就没必要实现那么多不同的收集器了。
--------------------------新生代收集器------------------------
适用:看上去没什么用,但实际上到现在为止,它依然是虚拟机运行在Client模式下的默认新生代收集器
特点:
1).单线程的收集器,说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作
2).在它进行垃圾收集时,必须暂停其他所有的工作线程(Sun将这件事情称之为“Stop The World”),直到它收集结束。这项工作实际上是由虚拟机在后台自动发起和自动完成的,在用户不可见的情况下把用户的正常工作的线程全部停掉,这对很多应用来说都是难以接受的。
收集算法:采用复制算法
优点:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。
缺点:GC时暂停线程带给用户不良体验
搭配:CMS 或Serial Old(MSC)
-----------------------------------------------------------------------------------------------------------
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为都与Serial收集器完全一样,实现上这两种收集器也共用了相当多的代码。
适用:运行在Server模式下的虚拟机中的新生代
特点:
1).多线程GC(并行):ParNew是Serial的多线程版本,两者共用了许多代码。
2).在GC时暂停所有用户线程
算法:采用复制算法
优点:高效
缺点:GC时暂停线程带给用户不良体验,单线程下效果不一定优于Serial
搭配:CMS 或Serial Old(MSC)
ParNew收集器有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。在JDK 1.5以后使用CMS来收集老年代的时候,新生代只能选择ParNew或Serial收集器中的一个。
---------------------------------------------------------------------------------------------------------
适用:新生代收集器,在后台运算而不需要太多交互的任务。
特点: 1.多线程GC(并行)
2.在GC时暂停所有用户线程
与其他收集器的不同:
1).ParNew,CMS等收集器的关注点在于尽可能缩短垃圾收集时用户线程的停顿时间;而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。
吞吐量:运行代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
2).Parallel Scavenge可采用GC自适应的调节策略(这是与另外两一个重要的区别)
参数:用于精确控制吞吐量
-XX:MaxGCPauseMillis 最大垃圾收集停顿时间
-XX:GCTimeRatio 垃圾收集时间与运行用户代码时间的比例=垃圾收集时间/运行用户代码时间,相当于是吞吐量的倒数。
实现:降低GC停顿时间:牺牲吞吐量和新生代空间(减小新生代空间,GC频率变大,吞吐量降低)
GC自适应的调节策略
-XX:+UseAdaptiveSizePolicy 使用自适应的调节策略 即不需要指定新生代的大小,Eden与Surivior的比例,晋升老年代的年龄等细节参数,虚拟机自动根据根据当前系统的状态动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。
算法:采用复制算法
优点:高效
搭配:Parallel Old或Serial Old(MSC)
---------------------------------------------------------------------------------------------------------
-------------------------老年代收集器----------------------------
适用
1).运行在Client模式下的虚拟机中的老年代
2).在Server模式下,它主要还有两大用途
①.与Parallel Scavenge搭配
②.作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用
特点: 1.单线程GC,Serial收集器的老年代版本
2.在GC时暂停所有用户线程
算法:采用标记-整理算法
优点:简单,高效
缺点:GC时暂停线程带给用户不良体验
搭配:Serial Old(MSC)或ParNew
-----------------------------------------------------------------------------------------------------
适用:运行在Server模式下的虚拟机中的新生代.在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
特点
1).多线程GC(并行):Parallel Scavenge的老年代版本
2).在GC时暂停所有用户线程
3).这个收集器是在JDK 1.6中才开始提供的
算法:采用标记-整理算法
优点:高效
缺点:GC时暂停线程带给用户不良体验,单线程下效果不一定优于Serial
搭配:Parallel Scavenge
-------------------------------------------------------------------------------------
适用:面向服务端应用,适用于新生代和老年代。当前收集器技术发展的最前沿成果
特点:
1.并行+并发。可充分利用CPU资源
2.分代收集。
3.空间整合。 G1从整体看是”标记-整理“算法,从局部(两个Region之间)看,是”复制“算法。 不会产生空间碎片。
4.可预测的停顿。建立可预测的态度时间模型,能让使用者明确指定在一个长度为M毫秒的时间内,消耗在垃圾收集的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
Garbage First名称的由来
G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是由于它能够极力地避免全区域的垃圾收集。G1将内存划分为Region,跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
难点:虽然内存分为Region,但垃圾收集不能真的以Region为单位进行,因为Region不可能是孤立的,存在某个对象被多个Region的引用,那在做可达性判断确定对象是否存活时,是否需要扫描整个堆空间呢?注意:此问题在所有的收集器中都存在(如存在新生代与老年代之间的引用)。
解决:1.使用Remembered Set来避免圈堆扫描。
过程:G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作是,会产生一个Write Barrier暂时中断操作,检查Reference类型引用的对象是否处于不同的Region(在分代的例子中就是检查是否老年代的对象引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。
内存布局:G1的堆内存布局与其他收集器不同,G1将整个堆内存空间划分为多个大小相等的Region,虽然仍然有新生代和老年代的概念,但是新生代和老年代不再是物理隔离的,他们都是一部分Region(不需要连续)的集合。
过程(与CMS相似)
1.初始标记:暂停用户线程,标记GC Roots能直接关联的对象
2.并发标记:用户线程与标记线程并发,进行GC Roots的Trace
3.最终标记修正并发标记阶段,因用户线程继续运行而导致标记产生变动的那一部分对象的标记记录。
4.筛选回收:
算法: 全局标记-整理+局部复制算法
优点:高效,停顿时间可控、可预测
1. 与串行回收器相关的参数
-XX:+UseSerialGC:在新生代和老年代使用串行回收器。
-XX:+SuivivorRatio:设置 eden 区大小和 survivor 区大小的比例。
-XX:+PretenureSizeThreshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。
-XX:MaxTenuringThreshold:设置对象进入老年代的年龄的最大值。每一次 Minor GC 后,对象年龄就加 1。任何大于这个年龄的对象,一定会进入老年代。
2. 与并行 GC 相关的参数
-XX:+UseParNewGC: 在新生代使用并行收集器。
-XX:+UseParallelOldGC: 老年代使用并行回收收集器。
-XX:ParallelGCThreads:设置用于垃圾回收的线程数。通常情况下可以和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的。
-XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于 0 的整数。收集器在工作时,会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。
-XX:GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。
-XX:+UseAdaptiveSizePolicy:打开自适应 GC 策略。在这种模式下,新生代的大小,eden 和 survivor 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。
3. 与 CMS 回收器相关的参数
-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
-XX:+ParallelCMSThreads: 设定 CMS 的线程数量。
-XX:+CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发,默认为 68%。
-XX:+UseFullGCsBeforeCompaction:设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收。
-XX:+CMSParallelRemarkEndable:启用并行重标记。
-XX:CMSInitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
-XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才进行 CMS 回收。
-XX:+CMSIncrementalMode:使用增量模式,比较适合单 CPU。
4. 与 G1 回收器相关的参数
-XX:+UseG1GC:使用 G1 回收器。
-XX:+UnlockExperimentalVMOptions:允许使用实验性参数。
-XX:+MaxGCPauseMills:设置最大垃圾收集停顿时间。
-XX:+GCPauseIntervalMills:设置停顿间隔时间。
5. 其他参数
-XX:+DisableExplicitGC: 禁用显示 GC。
标签:
原文地址:http://blog.csdn.net/ochangwen/article/details/51412595