垃圾回收器

HotSpot虚拟机中有7种垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1。

垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法、分代收集)的具体实现,不同商家、不同版本的JVM所提供的垃圾收集器可能会有很在差别,此处主要介绍HotSpot虚拟机中的垃圾收集器。

JDK7/8后,HotSpot虚拟机所有收集器及组合(连线),如下图:

由上图可知

新生代收集器:Serial、ParNew、Parallel Scavenge;

老年代收集器:Serial Old、Parallel Old、CMS;

整堆收集器:G1;

两个回收器间有连线,表明他们可以搭配使用。

并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;

如ParNew、Parallel Scavenge、Parallel Old;

并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);

用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;

如CMS、G1(也有并行);

各个垃圾回收器应用场景:

Serial收集器

Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器;JDK1.3.1前是HotSpot新生代收集的唯一选择;

应用场景:是HotSpot在Client模式下默认的新生代收集器;也有优于其他收集器的地方;  简单高效(与其他收集器的单线程相比); 对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率;在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是可以接受的。

设置参数: "-XX:+UseSerialGC":添加该参数来显式的使用串行垃圾收集器;

ParNew收集器

ParNew垃圾收集器是Serial收集器的多线程版本。

应用场景:在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作;在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

设置参数

"-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;

"-XX:+UseParNewGC":强制指定使用ParNew;

"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

Parallel Scavenge收集器

Parallel Scavenge垃圾收集器因为与吞吐量关系密切,也称为吞吐量收集器(Throughput Collector)。

应用场景

高吞吐量为目标,即减少垃圾收集时间,让用户代码获得更长的运行时间;

当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求时,即程序主要在后台进行计算,而不需要与用户进行太多交互;

例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序;

设置参数

Parallel Scavenge收集器提供两个参数用于精确控制吞吐量:

(A)"-XX:MaxGCPauseMillis"

控制最大垃圾收集停顿时间,大于0的毫秒数;

MaxGCPauseMillis设置得稍小,停顿时间可能会缩短,但也可能会使得吞吐量下降;

因为可能导致垃圾收集发生得更频繁;

(B)"-XX:GCTimeRatio"

设置垃圾收集时间占总时间的比率,0<n<100的整数;

GCTimeRatio相当于设置吞吐量大小;

垃圾收集执行时间占应用程序执行时间的比例的计算方法是:

1 / (1 + n)

例如,选项-XX:GCTimeRatio=19,设置了垃圾收集时间占总时间的5%=1/(1+19);

默认值是1%=1/(1+99),即n=99;

垃圾收集所花费的时间是年轻代和老年代收集的总时间;

如果没有满足吞吐量目标,则增加代的内存大小以尽量增加用户程序运行的时间;

此外,还有一个值得关注的参数:

(C)、"-XX:+UseAdptiveSizePolicy"

开启这个参数后,就不用手工指定一些细节参数,如:

新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等;

JVM会根据当前系统运行情况收集性能监控信息,动态调整这些参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomiscs);

这是一种值得推荐的方式:

1、只需设置好内存数据大小(如"-Xmx"设置最大堆);

2、然后使用"-XX:MaxGCPauseMillis"或"-XX:GCTimeRatio"给JVM设置一个优化目标;

3、那些具体细节参数的调节就由JVM自适应完成;

这也是Parallel Scavenge收集器与ParNew收集器一个重要区别;

上面都是新生代的,下面接着介绍老年代的回收器

Serial Old收集器

Serial Old是 Serial收集器的老年代版本;

应用场景

主要用于Client模式;

而在Server模式有两大用途:

(A)、在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配);

(B)、作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用(后面详解);

Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;JDK1.6中才开始提供;

JDK1.6及之后用来代替老年代的Serial Old收集器; 特别是在Server模式,多CPU的情况下;

这样在注重吞吐量以及CPU资源敏感的场景,就有了Parallel Scavenge加Parallel Old收集器的"给力"应用组合;

设置参数

"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

CMS收集器

并发标记清理(Concurrent Mark Sweep,CMS)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low-latency)垃圾收集器;

应用场景

与用户交互较多的场景;

希望系统停顿时间最短,注重服务的响应速度;

以给用户带来较好的体验;

如常见WEB、B/S系统的服务器上的应用;

设置参数

"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;

CMS收集器3个明显的缺点

(A)、对CPU资源非常敏感

并发收集虽然不会暂停用户线程,但因为占用一部分CPU资源,还是会导致应用程序变慢,总吞吐量降低。

CMS的默认收集线程数量是=(CPU数量+3)/4;

当CPU数量多于4个,收集线程占用的CPU资源多于25%,对用户程序影响可能较大;不足4个时,影响更大,可能无法接受。

增量式并发收集器:

针对这种情况,曾出现了"增量式并发收集器"(Incremental Concurrent Mark Sweep/i-CMS);

类似使用抢占式来模拟多任务机制的思想,让收集线程和用户线程交替运行,减少收集线程运行时间;

但效果并不理想,JDK1.6后就官方不再提倡用户使用。

(B)、无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败

1、浮动垃圾(Floating Garbage)

在并发清除时,用户线程新产生的垃圾,称为浮动垃圾;

这使得并发清除时需要预留一定的内存空间,不能像其他收集器在老年代几乎填满再进行收集;

也要可以认为CMS所需要的空间比其他垃圾收集器大;

"-XX:CMSInitiatingOccupancyFraction":设置CMS预留内存空间;

JDK1.5默认值为68%;

JDK1.6变为大约92%;

2、"Concurrent Mode Failure"失败

如果CMS预留内存空间无法满足程序需要,就会出现一次"Concurrent Mode Failure"失败;

这时JVM启用后备预案:临时启用Serail Old收集器,而导致另一次Full GC的产生;

这样的代价是很大的,所以CMSInitiatingOccupancyFraction不能设置得太大。

(C)、产生大量内存碎片

由于CMS基于"标记-清除"算法,清除后不进行压缩操作;

前面《Java虚拟机垃圾回收(二) 垃圾回收算法》"标记-清除"算法介绍时曾说过:

产生大量不连续的内存碎片会导致分配大内存对象时,无法找到足够的连续内存,从而需要提前触发另一次Full GC动作。

解决方法:

1、"-XX:+UseCMSCompactAtFullCollection"

使得CMS出现上面这种情况时不进行Full GC,而开启内存碎片的合并整理过程;

但合并整理过程无法并发,停顿时间会变长;

默认开启(但不会进行,结合下面的CMSFullGCsBeforeCompaction);

2、"-XX:+CMSFullGCsBeforeCompaction"

设置执行多少次不压缩的Full GC后,来一次压缩整理;

为减少合并整理过程的停顿时间;

默认为0,也就是说每次都执行Full GC,不会进行压缩整理;

由于空间不再连续,CMS需要使用可用"空闲列表"内存分配方式,这比简单实用"碰撞指针"分配内存消耗大;

G1收集器

G1(Garbage-First)是JDK7-u4才推出商用的收集器;

应用场景

面向服务端应用,针对具有大内存、多处理器的机器;

最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;

如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒;

用来替换掉JDK1.5中的CMS收集器;

在下面的情况时,使用G1可能比CMS好:

(1)、超过50%的Java堆被活动数据占用;

(2)、对象分配频率或年代提升频率变化很大;

(3)、GC停顿时间过长(长于0.5至1秒)。

是否一定采用G1呢?也未必:

如果现在采用的收集器没有出现问题,不用急着去选择G1;

如果应用程序追求低停顿,可以尝试选择G1;

是否代替CMS需要实际场景测试才知道。

设置参数

"-XX:+UseG1GC":指定使用G1收集器;

"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;

"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;

"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region;

原文地址:https://www.cnblogs.com/ken-jl/p/8998674.html

时间: 2024-10-21 06:59:47

垃圾回收器的相关文章

了解CMS(Concurrent Mark-Sweep)垃圾回收器

http://www.iteye.com/topic/1119491 1.总体介绍: CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器.对于要求服务器响应速度的应用上,这种垃圾回收器非常适合.在启动JVM参数加上-XX:+UseConcMarkSweepGC ,这个参数表示对于老年代的回收采用CMS.CMS采用的基础算法是:标记-清除. 2.CMS过程: 初始标记(STW initial mark) 并发标记(Concurrent mar

[Think In Java]基础拾遗1 - 对象初始化、垃圾回收器、继承、组合、代理、接口、抽象类

目录 第一章 对象导论第二章 一切都是对象第三章 操作符第四章 控制执行流程第五章 初始化与清理第六章 访问权限控制第七章 复用类第九章 接口 第一章 对象导论 1. 对象的数据位于何处? 有两种方式在内存中存放对象: (1)为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象置于堆栈或者静态存储区域内来实现.这种方式牺牲了灵活性. (2)在被称为堆的内存池中动态地创建对象.在这种方式,知道运行时才知道对象需要多少对象,它们的生命周期如何,以及它们的具体类型.

总结Java垃圾回收器的方法和原理

1. 垃圾回收只与内存有关 在Java中,我们new完对象之后,垃圾回收器负责回收无用的对象占据的内存资源.这与C++不同,在C++中,准许使用局部对象,回收对象时候,需要用到finalize()析构函数.C++的对象创建在堆栈中,而Java对象创建在堆中,所以我们创建完对象之后,Java的垃圾回收器在堆中,会自动帮我们回收垃圾,至于何时回收垃圾,我们不得而知了. 2.垃圾回收用到的方法 (1)finalize() 该方法是用来回收“特殊”的内存,而这内存不是new出来的,所以垃圾回收器无法回收

垃圾回收器设计

1利用栈对象自动释放(可以禁止析构函数就不能产生栈对象了哦 小技巧) 利用栈对象出作用域自动释放的特性构造一个空的智能指针类 2解决智能释放问题,只要这样就不用手工delete类指针了 在智能指针类的析构函数里面delete对象成员去对象里面释放垃圾指针等.. 3解决被智能指针代理的真实对象的函数的调用问题 重载->运算符,以便能直接调用这个智能指针存储的对象中的函数 或者使用代理模式不过没有重载的方法简单 4继承引用计数类,来为自身增加引用技术功能, 引用计数的++ --在智能指针里完成 #i

第5章(2) Java的垃圾回收器

终结处理 Java有垃圾回收器回收无用对象占据的内存资源.但垃圾回收器只知道释放经由new分配的内存,如果用其他的方法获得了一块"特殊"的内存区域,就需要我们自己完成清理工作.Java提供了finalize()方法来解决这一问题. finalize()方法: 工作原理:一但垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存. 其作用,换句话说就是:在你不再需要某个对象之前,如果必须执行某些动作,那么你

垃圾回收器如何确定哪些对象要回收---《深入理解java虚拟机》

垃圾回收器如何确定哪些对象要回收: 引用计数法 很多教科书判断对象是否存活的算法是这样的:给对象添加一个引用计数器,每当有一个地发引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的. 客观地说,引用技术算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软的COM技术.使用ActionScript 3 的FlashPlayer.Python语言以及在游戏脚本领域中被广泛应用的Squirrel中都

C++垃圾回收器的实现

一.简单介绍 这是一个自己写C++垃圾自己主动回收器,用到的都是标准C++语法.採用了引用计数加mark-sweep的方法.在没有循环引用的情况下,引用计数能够保证垃圾实时得到回收:对于有循环引用的情况下,计数就不能回收了,这时就要用mark-sweep的方法.事实上全然使用mark- sweep的方法也是能够的,但有了引用计数,能够回收大量的非循环引用垃圾,降低最后的mark-sweep时的工作量. 考虑到大家的15分钟阅读热情,在说细节之前,先show一下这个指针怎么使用.顺便提一下,这个指

谈一谈垃圾回收器

目的: 使用垃圾回收器的唯一原因就是:回收程序不再使用的内存. 针对的目标对象: Java的垃圾回收器会自动回收不再使用的Java对象,释放内存.但是回收的是用new创建的,分配在堆上的内存. finalize(): 那么,如果不是用这种方式创建的对象,该怎么回收?比如:Java调用了本地的c语言方法创建了个对象,那么这时,该对象不是放在堆上的.除非你手动去调用c的free()方法,否则,这个对象将永远不会被清理. Java的finalize()方法可以解决上面的问题.垃圾回收器在回收垃圾对象时

JVM中的G1垃圾回收器

我们先回顾一下主流Java的垃圾回收器(HotSpot JVM).本文是针对堆的垃圾回收展开讨论的. 堆被分解为较小的三个部分.具体分为:新生代.老年代.持久代. 绝大部分新生成的对象都放在Eden区,当Eden区将满,JVM会因申请不到内存,而触发Young GC ,进行Eden区+有对象的Survivor区(设为S0区)垃圾回收,把存活的对象用复制算法拷贝到一个空的Survivor(S1)中,此时Eden区被清空,另外一个Survivor S0也为空.下次触发Young GC回收Eden+S

C++内存管理变革(6):通用型垃圾回收器 - ScopeAlloc

本文已经迁移到:http://cpp.winxgui.com/cn:a-general-gc-allocator-scopealloc C++内存管理变革(6):通用型垃圾回收器 - ScopeAlloc 许式伟2008-1-22 引言 在前文,我们引入了GC Allocator(具备垃圾回收能力的Allocator),并提供了一个实作:AutoFreeAlloc(具体内容參见<C++内存管理变革(2):最袖珍的垃圾回收器 - AutoFreeAlloc>). 可是,如前所述,AutoFree