1. Serial 新生代收集器,单线程,复制算法
单线程,不仅仅说明它只会用一个CPU或一条收集线程去完成垃圾手机工作,更重要的时进行垃圾收集时,必须暂停其他说有的工作线程,直至收集结束。Stop The World
优点:简单而高效,没有线程交互的开销。
2. ParNew 是Serial的多线程版本,复制算法
与Serial可用的所有控制参数、收集算法、Stop The World、对象分配原则、回收策略完全一样,实现上,两种收集器共用了相当多的代码。
ParNew在单CPU环境中绝对不会有比Serial更好的效果。
注:并发强调的是一起出发(交替执行),并行强调的是一起执行。
3. Parallel Scavenge 新生代收集器,多线程,复制算法
关注点与其他收集器不同,CMS关注尽可能缩短垃圾收集时用户线程停顿时间,而Parallel Scavenge目的时达到一个可控制的吞吐量。就是CPU用于运行用户线程的时间与CPU总消耗时间的比值,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
停顿时间短适合与用户交互的程序,良好的响应速度能提升用户体验,高吞吐量可以高效的利用CPU时间,尽快完成运算任务,主要适合后台运算而不需要太多交互任务。
4. Serial Old 是Serial老年代版本,单线程,标记-整理算法
主要意义是给Client模式下的虚拟机使用。
5. Parallel Old 是Parallel Scavenge老年代版本,多线程,标记整理
6. CMS(Concurrent Mark Sweep) 标记-清除算法
互联网网站或者B/S系统服务端,注重服务的相应速度,希望系统停顿时间最短。
分为四个步骤:
- 初始标记:标记一下GC Roots能直接关联到的对象,速度很快。
- 并发标记:进行GC Roots Tracing的过程。与用户线程同时运行。
- 重新标记:为了修正并发标记期间因为用户程序继续运行导致标记产生变动的那部分对象的标记记录,这个阶段停顿时间会比初始标记阶段稍长,但是比并发标记时间短。
- 并发清除:与用户线程同时运行。
由于整个过程耗时最长的并发标记和并发清楚过程收集器线程可以与用户线程一起工作,所以,总体上来说CMS收集器的内存回收过程是与用户线程一起并发执行的。
三个明显缺点
对CPU资源非常敏感。并发设计都对CPU资源敏感,并发阶段虽然不会导致用户线程变慢,但是会因为占用一部分线程而导致应用程序变慢,总吞吐量会降低。
无法处理浮动垃圾。由于并发清理阶段用户线程还在运行,伴随程序运行就会有新的垃圾不断产生,这部分垃圾出现在标记过程之后,CMS午发在当次收集中处理掉它们,只好下次GC时清理,这部分垃圾称为“浮动垃圾”。
基于标记-清除算法实现的,就意味着收集结束会有大量的空间碎片,空间碎片多,会给大对象分配带来很大麻烦,往往会出现老年代空间还有很大空间剩余,但是无法找到足够大的空间来分配当前对象,不得不触发Full GC。
CMS:采用标记清除算法
解决这个问题的办法就是可以让CMS在进行一定次数的Full GC(标记清除)的时候进行一次标记整理算法,CMS提供了以下参数来控制:
-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5
也就是CMS在进行5次Full GC(标记清除)之后进行一次标记整理算法,从而可以控制老年带的碎片在一定的数量以内,甚至可以配置CMS在每次Full GC的时候都进行内存的整理。
7.G1 面向服务端引用,未来可以替代CMS的收集器
与其他GC收集器比较,特点
- 并行和并发:缩短Stop The World 停顿时间,其他收集器原本需要停顿Java线程执行GC堆,G1仍然可以通过并发的方式让Java程序继续执行。
- 分代收集:与其他收集器相同,分代概念在G1依然得以保留。但是采用不同方式处理。
- 空间整合:与CMS使用‘标记-清除’不同,G1从整体来看使用的是“标记-整理”实现。从局部(两个Region之间)使用的是“复制”实现,G1的运作期间不会产生空间碎片。
- 可预测的停顿:这是相对于CMS的优势。
G1之前的其他收集器进行收集范围是整个新生代和老年代,使用G1收集器,Java对的内存布局就与其他收集器有很大差别,它将整个java内存分为多个大小相等的独立区域(Region),虽然保留新生代老年代的概念,但新生代和老年代不再是物理隔离的,他们都是一部分Region(不需要连续)的集合。
运作步骤
初始标记(Initial Marking)
并发标记(Concurrent Marking)
最终标记(Final Marking)
筛选回收(Live Data Counting and Evacuation):对Region的回收价值和成本进行排序,根据用户的期望的GC停顿时间制定回收计划。
这个阶段可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控的,而且停顿用户现场将大幅度提高收集效率。
内存分配策略
对象优先在Eden分配
大对象直接进入老年代:避免Eden区及两个Survivor区之间发生大量的内存复制
长期存活的对象将进入老年代:在Eden出生,经历过一次GC仍然存活,并且能被Survivor容纳,将被移动到Survivor空间,设置对象年龄为1,每熬过一次GC年龄增加一岁,年龄增加到一定程度(通常15岁),就会晋升老年代。MaxTenuringThreshold配置。
动态对象年龄判定:如果在Survivor空间相同年龄所有对象的大小总和打于Survivor空间大小的一半,年龄大于或者等于该对象年龄可以直接进入老年代,无需等到MaxTenuringThreshold。
空间分配担保:发生GC之前,虚拟机会检查老年代最大可用的连续空间是否打于新生代所有对象总空间,如果这个条件成立,就可以确保是安全的。(老年代能容纳Survivor晋升到老年代的对象)
选择server模式的VM。服务端常使用
-Xms2g
设置堆内存最小值2G
-Xmx2g
设置堆内存最大值2G
-Xmn1g
设置新生代大小1G
-Xss1024K
设置单线程栈空间大小1024K
-XX:PermSize=256m
设置永久代初始大小256m。JDK8中已移除
-XX:MaxPermSize=512m
设置永久代最大值512m。JDK8中已移除
-XX:ParallelGCThreads=8
设置并行收集器收集时使用的CPU数。并行收集线程数 8。
-XX:+UseConcMarkSweepGC
开启CMS收集器,默认新生代收集器 UseParNewGC。
-XX:+UseParNewGC
开启ParNew收集器。
-XX:+UseCMSCompactAtFullCollection
开启对老年代空间进行压缩整理(默认开启)。
-XX:SurvivorRatio=4
Eden与一个Surivivor的比值大小。默认为8:1:1,即Eden占8/10。
-XX:MaxTenuringThreshold=10
晋升老年代的最大年龄。默认为15,比如设为10,则对象在10次普通GC后将会被放入年老代。
-XX:CMSInitiatingOccupancyFraction=80
触发CMS收集器的内存比例。比如80%的意思就是说,当内存达到80%,就会开始进行CMS并发收集。
-XX:NewRatio
表示新生代与老年代所占的比值为1:4,Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
参考:
java常用命令
gc发生场景
jvm参数大全
内存区域垃圾收集
JVM内存设置多大合适?
JVM 内存区域大小参数设置