JVM的7种垃圾回收器(⼩结)
垃圾回收算法和垃圾回收器
对于JVM的垃圾回收算法有复制算法、标记清除、标记整理。
⽤阳哥的话就是:这些算法只是天上飞的理念,是⼀种⽅法论,但是真正的垃圾回收还需要有落地实现,所以垃圾回收器应运⽽⽣。
JVM回收的区域包括⽅法区和堆,jvm对于不同区域不同的特点采⽤分代收集算法,⽐如因为所有的对象都是在Eden区进⾏分配,并且⼤部分对象的存活时间都不长,都是“朝⽣⼣死”的,每次新⽣代存活的对象都不多,所以新采取复制算法;⽽jvm默认是新⽣代的对象熬过15次GC才能进⼊⽼年代,所以⽼年代的对象都是⽣命周期⽐较长的,采⽤标记清除或者标记整理算法。
那么对于这些算法的实现都有什么呢?
新⽣代:serial、ParNew、Parallel
⽼年代:Serial Old、Parallel Old、CMS
全堆:G1
并且他们的搭配组合如下:
垃圾回收器
jvm的垃圾回收器⼤体上的分类主要包括四种:串⾏、并⾏、并发(CMS)和G1。
串⾏垃圾回收器(Serial):它为单线程环境设计并且只使⽤⼀个线程进⾏垃圾回收,会暂停所有的⽤户线程。所以不适合服务器环境。
并⾏垃圾回收器(Parallel):多个垃圾回收线程并⾏⼯作,此时⽤户线程是暂停的,适⽤于科学计算/⼤数据处理等弱交互场景。
并发垃圾回收器(CMS):⽤户线程和垃圾收集线程同时执⾏(不⼀定是并⾏,可能交替执⾏),不需要停顿⽤户线程。互联⽹公司多⽤它,适⽤于对响应时间有要求的场景。
G1垃圾回收器:G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进⾏垃圾回收。
默认的垃圾回收器
平时我们没有配置什么jvm参数,程序也能正常执⾏,那么JVM默认的垃圾回收器是什么呢?
那么如何查看默认的回收器呢?有很多⽅式,这⾥简单列举⼏种:
1.命令⾏⽅式:
1java -XX:+PrintCommandLineFlags -version
可以看到jdk8默认的是使⽤的Parallel并⾏回收器。
2、jvm参数设置
在JVM运⾏之前加⼊参数同样可以查看,其实这两种⽅式是差不多的
3.jps+jinfo
先使⽤jps查看java进程号,在使⽤jinfo查看该进程的配置
Serial收集器
Serial是⼀个单线程收集器,在进⾏垃圾收集的时候必须停下所有的⼯作(Stop The World)。
串⾏收集器是最古⽼,最稳定以及效率⾼的收集器,只使⽤⼀个线程去回收但其在进⾏垃圾收集过程中可能会产⽣较长的停顿(
Stop-The-World状态)。
虽然在收集垃圾过程中需要暂停所有其他的⼯作线程,但是它简单⾼效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最⾼的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虛拟机运⾏在Client模式下默认的新⽣代垃圾收集器。
对应JVM参数是: -XX:+UseSerialGC
开启后会使⽤: **Serial(Young区⽤) + Serial Old(Old区⽤)**的收集器组合:表⽰新⽣代、⽼年代都会使⽤串⾏回收收集器,新⽣代使⽤复制算法,⽼年代使⽤标记-整理算法。
ParNew收集器
ParNew是Serial收集器的升级版,将单线程进⾏垃圾回收升级为多线程进⾏垃圾回收,但是依旧会Stop The World。
ParNew收集器其实就是Serial收集器新⽣代的并⾏多线程版本,最常见的应⽤场景是配合⽼年代的CMS GC⼯作,其余的⾏为和
Serial收集器完全⼀样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的⼯作线程。它是很多java虚拟机运⾏在Server
模式下新⽣代的默认垃圾收集器。
常⽤对应JVM参数: -XX:+UseParNewGC
启⽤ParNew收集器,只影响新⽣代的收集,不影响⽼年代
开启。上述参数后,会使⽤: ParNew(Young区⽤) + Serial Old的收集器组合,新⽣代使⽤复制算法,⽼年代采⽤标记-整理算法。
但是,ParNew+Tenured这样的搭配,java8已经不再被推荐。
Parallel收集器
Parallel Scavenge收集器类似ParNew也是⼀个新⽣代垃圾收集器,使⽤复制算法,也是⼀个并⾏的多线程的垃圾收集器,俗称吞吐
量优先收集器。⼀句话:串⾏收集器在新⽣代和⽼年代的并⾏化
⾸先先科普下什么是吞吐量:
吞吐量(Thoughput)=运⾏⽤户代码时间(运⾏⽤户代码时间+垃圾收集时间),也即⽐如程序运⾏100分
钟,垃圾收集时间1分钟,
吞吐量就是99%)。
Parallel收集器重点关注的是:
可控制的⾼吞吐量意味着⾼效利⽤CPU的时间,它多⽤于在后台运算⽽不需要太多交互的任务。
**⾃适应调节策略也是Parallel Scavenge收集器与ParNew收集器的-⼀个重要区别。**⾃适应调节策略:虚拟机会根据当前系统的运⾏情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间(-XX:MaxGCPauseMillis)或最⼤的吞吐量。
常⽤JVM参数: -XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)使⽤Parallel Scanvenge收集器
开启该参数后:使⽤Parallel收集器+Parallel Old的组合。新⽣代使⽤复制算法,⽼年代使⽤标记-整理算法。
Serial Old收集器
SerialOlid是Serial垃圾收集器⽼年代版本,它同样是个单线程的收集器,使⽤标记-整理算法,这个收集器也主要是运⾏在Client默
的java虚拟机默认的年⽼代垃圾收集器。
在Server模式下,主要有两个⽤途(了解,版本已经到8及以后): .
1.在JDK1.5之前版本中与新⽣代的Parallel Scavenge收集器搭配使⽤。 ( Parallel Scavenge + Serial Old )
2.作为⽼年代版中使⽤CMS收集器的后备垃圾收集⽅案。
Parallel Old收集器
Parallel Old收集器是Parallel Scavenge的⽼年代版⽊,使⽤多线程的标记-整理算法,Parallel Old收集器在JDK1.6才开始提供。
在JDK1.6之前,新⽣代使⽤Parallel Scavenge收集器只能搭配年⽼代的Serial Old收集器,只能保证新⽣代的吞吐量优先,⽆法保
证整体的吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old )
Parallel Old正是为了在年⽼代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求⽐较⾼,JDK1.8后可以优先考虑新⽣代Parallel Scavenge和年⽼代Parallel Old收集器的搭配策略。在 JDK1.8及后(Parallel Scavenge + Parallel Old )
JVM常⽤参数:
-XX:+UseParallelOldGC使⽤Parallel Old收集器,设置该参数后,新⽣代Parallel+⽼年代Parallel Old。
jvm调优参数CMS(Concurrent Mark Sweep)
CMS收集器(Concurrent Mark Sweep: 并发标记清除)是⼀种以获取最短回收停顿时间为⽬标的收集器。
适合应⽤在互联⽹站或者B/S系统的服务器上,这类应⽤尤其重视服务器的响应速度,希望系统停顿时间最短。
CMS⾮常适合堆内存⼤、CPU核数多的服务器端应⽤,也是G1出现之前⼤型应⽤的⾸选收集器。
Concurrent Mark Sweep并发标记清除,并发收集低停顿,并发指的是与⽤户线程⼀起执⾏。
启该收集器的JVM参数: -XX:+UseConcMarkSweepGC 开启该参数后会⾃动将-XX:+UseParNewGC打开
开启该参数后,使⽤ParNew(Young区⽤) + CMS(Old区⽤) + Serial Old的收集器组合,Serial Old将作为CMS出错的后备收集器
CMS收集器的运⾏过程分为下列4步:
**初始标记:**标记GC Roots能直接关联的对象。速度很快但是存在Stop The World。
**并发标记:**进⾏GC Roots Tracing 的过程,出存活对象且⽤户线程可并发执⾏。
**重新标记:**为了修正并发标记期间因⽤户程序继续运⾏⽽导致标记产⽣变动的那⼀部分对象的标记记录。仍然存在Stop The World问题。
**并发清除:**对标记的对象进⾏清除回收。
**CMS优点:**并发收集低停顿
缺点:
1.浮动垃圾:由于CMS并发清理阶段⽤户线程还在运⾏着,伴随程序运⾏⾃然会有新垃圾产⽣,这部分垃圾得标记过程之后,所以CMS⽆法在当收集中处理掉他们,只好留待下⼀次GC清理掉,这⼀部分垃圾称为浮动垃圾。在jdk1.5默认设置下,CMS收集器当⽼年代使⽤了68%的空间就会被激活,可以通过-XX:CMSInitialOccupancyFraction的值来提⾼触发百分⽐,在jdk1.6中CMS启动阈值提升到了92%,要是CMS运⾏期间预留的内存⽆法满⾜程序的需要,就会出现”Concurrent Mode Failure“,然后降级临时启⽤Serial Old收集器进⾏⽼年代的垃圾收集,这样停顿时间就很长了。所以-XX:CMSInitialOccupancyFraction设置太⾼容易导致⼤量”Concurrent Mode Failure“。
2.有空间碎⽚:CMS是⼀款基于“标记-清除”算法实现的,所以会产⽣空间碎⽚。为了解决这个问题,CMS提供了-
XX:UseCMSCompactAtFullCollection开发参数⽤于开启内存碎⽚的合并整理,由于内存整理是⽆法并⾏的,所以停顿时间会变长。还有-XX:CMSFullGCBeforeCompaction,这个参数⽤于设置多少次不压缩Full GC后,跟着来⼀次带压缩的(默认为0)。
3.对CPU资源敏感。在并发标记和并发清除阶段虽然不会停⽌⽤户线程,但是会因为占⽤⼀部分cpu资源进⾏垃圾回收导致⽤户程序变慢。CMS默认启动的回收线程数是(cpu数量+3)/4。所以CPU数量少会导致⽤户程序执⾏速度降低较多。
G1收集器
G1适⽤于全堆,既可以在新⽣代使⽤和⽼年代使⽤。G1与之前的收集器有很⼤的不同,是从不同的⾓度去设计的。
回想下之前的垃圾收集器的特点:
1.年轻代和⽼年代都是各⾃独⽴的连续的内存块。
2.年轻代Eden+from+to使⽤复制算法
3.⽼年代的收集必须扫描全部⽼年代内存空间。
4.都是以尽可能少⽽快速地执⾏GC为设计原则
G1收集器的设计⽬标是取代CMS收集器,它同CMS相⽐,在以下⽅⾯表现的更出⾊:
1、G1是⼀个有整理内存过程的垃圾收集器,不会产⽣很多内存碎⽚。
2、G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,⽤户可以指定期望停顿时间。
CMS垃圾收集器虽然减少了暂停应⽤程序的运⾏时间,但是它还是存在着内存碎⽚问题。于是,为了去除内存碎⽚问题,同时⼜保留CMS垃圾收集器低暂停时间的优点,JAVA7发布了⼀个新的垃圾收集器——G1垃圾收集器。
G1是在2012年才在jdk1.7u4中可⽤。 oracle官⽅计划在jdk9中将G1变成默认的垃圾收集器以替代CMS。它是⼀款⾯向服务端应⽤的收器,主要应⽤在多CPU和⼤内存服务器环境下,极⼤的减少垃圾收集的停顿时间,全⾯提升服务器的性能,逐步替换java8以前的CM:
集器。
主要改变是Eden,Survivor和Tenured等内存区域不再是连续的了,⽽是变成了⼀个个⼤⼩⼀样的region ,每个region从1M到32M不等。- -个region有可能属于Eden, Survivor或者Tenured内存区域。
底层原理
G1的最⼤好处是化整为零,避免全内存扫描,只需要按照区域来进⾏扫描即可。
G1收集器⼤致可分为如下步骤:
**初始标记:**仅标记GC Roots能直接到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下⼀阶段⽤户程序并发运⾏时,能在正确可⽤的Region中创建新对象。(需要线程停顿,但耗时很短。)
**并发标记:**从GC Roots开始对堆中对象进⾏可达性分析,出存活对象。(耗时较长,但可与⽤户程序并发执⾏)
**最终标记:**为了修正在并发标记期间因⽤户程序执⾏⽽导致标记产⽣变化的那⼀部分标记记录。且对象的变化记录在线程Remembered Set Logs⾥⾯,把Remembered Set Logs⾥⾯的数据合并到Remembered Set中。(需要线程停顿,但可并⾏执⾏。)
**筛选回收:**对各个Region的回收价值和成本进⾏排序,根据⽤户所期望的GC停顿时间来制定回收计划。(可并发执⾏)
总结
到此这篇关于JVM的7种垃圾回收器(⼩结)的⽂章就介绍到这了,更多相关JVM 垃圾回收器内容请搜索脚本之家以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持脚本之家!
您可能感兴趣的⽂章:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论