目录
导读
垃圾收集器有哪些
Serial收集器——单线程收集器
特点:
应用场景:
设置参数
ParNew收集器——Serial收集器的多线程版本
特点
应用场景
设置参数
为什么只有ParNew能与CMS收集器配合
Parallel Scavenge收集器
特点
应用场景
设置参数
Serial Old收集器
特点
应用场景
Parallel Old收集器
特点
应用场景
设置参数
CMS(Concurrent Mark Sweep)收集器
特点
应用场景
CMS收集器运作过程
设置参数
缺点
CMS&Parallel Old
G1 收集器
特点
应用场景
具体什么情况下应用G1垃圾收集器比CMS好,可以参考以下几点(但不是绝对):
建议:
设置参数
工作流程
总结
导读
上篇博客讲完啦:垃圾收集算法,今天接着讲垃圾收集算法的实现:垃圾收集器。jvm的自动内存管理机制就是由这个垃圾收集器来实现的。前面讲了垃圾收集器能做什么,现在讲下垃圾收集器都有哪些
JVM规范对于垃圾收集器的应该如何实现没有任何规定,因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器差别较大,这里只看HotSpot虚拟机。就像没有最好的算法一样,垃圾收集器也没有最好,只有最合适。我们能做的就是根据具体的应用场景选择最合适的垃圾收集器。
内存回收机制
* 对象存活判定算法
* 哪些内存需要回收
* 垃圾收集算法
* 垃圾收集器(对垃圾收集算法的实现)
内存分配与回收策略
* 原则
垃圾收集器有哪些
正如每种算法都有存在的原因,该串行收集器也有存在的原因。每种垃圾收集器都有它存在意义,优点和缺点。所以我们需要选择真正适合我们系统的垃圾收集器!上图中连线的就是可以互相搭配使用的收集器。
我们先明确一个观点:虽然我们是在对各个收集器进行比较,但并非为了挑选出一个最好的收集器。因为直到现在为止还没有最好的收集器,更没有万能的收集器,如果有完美的收集器存在,那么HotSpot虚拟机就没必要实现那么多不同的收集器啦,所以我们选择的只能是具体应用最合适的收集器。
它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” :将用户正常工作的线程全部暂停掉),直到它收集结束。
正如每种算法都有存在的原因,该串行收集器也有存在的原因:因为简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,没有线程交互的开销,专心做GC,自然可以获得最高的单线程效率。串行收集器的缺点很明显,虚拟机的开发者当然也是知道这个缺点的,所以一直都在缩减Stop The World的时间。
收集器运行过程如下图:
添加该参数来显式的使用串行垃圾收集器:
"-XX:+UseSerialGC"
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器完全一样。
是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
CMS收集器是一个被认为具有划时代意义的并发收集器,因此如果有一个垃圾收集器能和它一起搭配使用让其更加完美,那这个收集器必然也是一个不可或缺的部分了。
收集器运行过程如下图:
在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作;
但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。
指定使用CMS后,会默认使用ParNew作为新生代收集:
"-XX:+UseConcMarkSweepGC"
强制指定使用ParNew:
"-XX:+UseParNewGC"
指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相:
"-XX:ParallelGCThreads"
CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收集线程与用户线程(基本上)同时工作; CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作;因为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其余几种收集器则共用了部分的框架代码;
它的关注点是:吞吐量(如何高效率利用CPU),
而CMS等垃圾收集器的关注点更多的是:用户线程的停顿时间(提高用户体验)。
所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。
运行示意图
Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,不进行手工优化,可以选择把内存管理优化交给虚拟机去完成。
Parallel Scavenge收集器提供两个参数用于精确控制吞吐量:
控制最大垃圾收集停顿时间
"-XX:MaxGCPauseMillis"
设置垃圾收集时间占总时间的比率
"-XX:GCTimeRatio"
设置垃圾收集时间占总时间的比率,0 < n < 100的整数;
GCTimeRatio相当于设置吞吐量大小;
垃圾收集执行时间占应用程序执行时间的比例的计算方法是: 1 / (1 + n) 。
例如,选项-XX:GCTimeRatio=19,设置了垃圾收集时间占总时间的5% = 1/(1+19);默认值是1% = 1/(1+99),即n=99;
垃圾收集所花费的时间是年轻一代和老年代收集的总时间;
如果没有满足吞吐量目标,则增加代的内存大小以尽量增加用户程序运行的时间;
GC自适应的调节策略(GC Ergonomics)
另外还有一个参数:
"-XX:+UseAdptiveSizePolicy"
开启这个参数后,就不用手工指定一些细节参数,如:
以上介绍的是新生代的垃圾收集器,以下是老年代的垃圾收集器
Serial收集器的老年代版本,它同样是一个单线程收集器。
运行示意图:
Parallel Scavenge收集器的老年代版本。
使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器。在JDK1.6才有的。
Parallel Scavenge/Parallel Old收集器运行示意图如下:
指定使用Parallel Old收集器:
"-XX:+UseParallelOldGC"
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常适合在注重用户体验的应用上使用。
CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器;第一次实现了让垃圾收集线程与用户线程(基本上)同时工作;
从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程可分为四个步骤:
由于整个过程耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作。所以总体来说,CMS的内存回收是与用户线程一起“并发”执行的。
CMS收集器运行示意图如下:
指定使用CMS收集器
"-XX:+UseConcMarkSweepGC"
(一)对CPU资源敏感
(二)无法处理浮动垃圾
(三)产生大量内存碎片
总体来看,CMS与Parallel Old垃圾收集器相比,CMS减少了执行老年代垃圾收集时应用暂停的时间;
但却增加了新生代垃圾收集时应用暂停的时间、降低了吞吐量而且需要占用更大的堆空间;
(原因:CMS不进行内存空间整理节省了时间,但是可用空间不再是连续的了,垃圾收集也不能简单的使用指针指向下一次可用来为对象分配内存的地址了。相反,这种情况下,需要使用可用空间列表。即,会创建一个指向未分配区域的列表,每次为对象分配内存时,会从列表中找到一个合适大小的内存区域来为新对象分配内存。这样做的结果是,老年代上的内存的分配比简单实用碰撞指针分配内存消耗大。这也会增加年轻代垃圾收集的额外负担,因为老年代中的大部分对象是在新生代垃圾收集的时候从新生代提升为老年代的。)当新生代对象无法分配过大对象,就会放到老年代进行分配。
最新、技术最前沿的垃圾收集器。使用的垃圾收集算法:
GC
线程) 进行垃圾收集并发 & 并行 充分利用多CPU
、多核环境下的硬件优势 来缩短 垃圾收集的停顿时间
G1
收集器是 针对性 对 Java
堆内存区域进行垃圾收集,而非每次都对整个 Java
堆内存区域进行垃圾收集。
G1
收集器除了将 Java
堆内存区域分为新生代 & 老年代之外,还会细分为许多个大小相等的独立区域( Region
),然后G1收集器会跟踪每个 Region
里的垃圾价值大小,并在后台维护一个列表;每次回收时,会根据允许的垃圾收集时间 优先回收价值最大的Region
,从而避免了对整个Java堆内存区域进行垃圾收集,从而提高效率。G1
收集器还能建立可预测的停顿时间模型:即让 使用者 明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得从超出N毫秒。即具备实时性G1
收集器是基于 标记-整理算法实现的收集器G1
收集器不会产生内存空间碎片。服务器端虚拟机的内存区域(包括 新生代 & 老年代)
可以通过下面的参数,来设置一些G1相关的配置。
指定使用G1收集器:
"-XX:+UseG1GC"
当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45:
"-XX:InitiatingHeapOccupancyPercent"
为G1设置暂停时间目标,默认值为200毫秒:
"-XX:MaxGCPauseMillis"
设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region:
"-XX:G1HeapRegionSize"
新生代最小值,默认值5%:
"-XX:G1NewSizePercent"
新生代最大值,默认值60%:
"-XX:G1MaxNewSizePercent"
设置STW期间,并行GC线程数:
"-XX:ParallelGCThreads"
设置并发标记阶段,并行执行的线程数:
"-XX:ConcGCThreads"
G1
收集器的工作流程分为4个步骤:
总结