在Java中,垃圾回收是JVM最常见的工作,也是保证系统能稳定运行的保障之一,常见的垃圾回收算法有两种:分代回收和分区回收,他们各有优缺。当然回收垃圾不可能空手套白狼,所以下面就介绍一下七种垃圾回收器:

下图向我们展示了JDK1.8之后,垃圾回收器的使用场景:

新生代 年老代
Serial CMS
ParNew ParallelOld
ParallelSacvenge SerialOld(MSC)

还有一个最特殊的G1收集器,他会在收集的时候按情况选择收集老年代或新生代;

1.Serial垃圾回收器(单线程、复制算法)

Serial垃圾收集器是最基本、发展时间最长的垃圾收集器,在JDK1.3之前新生代唯一的一个垃圾收集器。他不但只会用一个线程去收集垃圾,在收集垃圾的时候其他的所有工作线程必须停止,即会发生Stop the World现象,直到垃圾回收结束。

对于Stop the world,他会在垃圾回收开始时停掉其他所有的线程,只供垃圾回收器回收使用,这样对于系统尤其是高并发系统来说就是一个噩梦;
从JDK1.3开始,从 Serial——>Paralle——>CMS——>G1 ,不断改进收集器回收策略,减少STW的停顿时间,但仍然无法完全消除;
可以参考 https://blog.csdn.net/tjiyu/article/details/53982412

优点 缺点
简单高效,对于限定单个CPU的环境来说,Serial垃圾收集器没有线程交互(交换)开销,可以获得最高的单线程手机效率 会发生SWT现象

2.ParNew垃圾回收器(Serial+多线程)

ParNew是Serial收集器的多线程版本,也使用复制算法,除了使用多线程对垃圾进行收集之外,他和Serial几乎没有什么区别也会发生Stop The World现象。多用于Server模式下。也可以与下文的CMS收集器配合使用

3.Parallel Scavange 收集器(多线程复制算法)

ParallelScavange垃圾回收器是一个新生代的垃圾回收器,同样使用复制算法,也是一个多线程的垃圾回收器,他重点关注的是程序的吞吐量问题

吞吐量 = CPU运行用户的代码时间 / CPU总的消耗时间 = 运行用户代码时间 / (运行用户代码时间+垃圾回收时间)

它的调优方式有:

  • —XX:MaxGCPauseMillis
    控制最大垃圾回收时间,大于0ms;
  • —XX:GCTimeRatio
    设置垃圾回收时间占总时间的比率,0<n<100的整数;
  • —XX:+UseAdptiveSizePolicy
    不用手动指定一些细节的调整(-Xmn 、-XX:SurvivorRation、-XX:PetenureSizeThreshold),JVM会根据当前系统的运行情况收集监测性能,自动调节参数

以上都为新生代的垃圾回收器,下面来看看老年代的垃圾回收器


4.Serial Old垃圾回收器(单线程标记整理算法)

Serial Old是Serial的老年代收集版本,他同样是个单线程收集器,使用标记-整理算法,这个垃圾收集器主要运行在客户端的JVM中默认的老年代垃圾回收器。当然也会发生STW(Stop The World)现象
主要应用场景有:

  • 用于Client模式
  • 用于Server模式时
    1.在JDK1.5之前,与ParallelScavenge收集器搭配使用,
    2.作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用;

5.Parallel Old收集器(多线程标记整理算法)

在JDK1.5之前新生代使用ParallelScavenge只能与Serial Old搭配使用,只能保证新生代的吞吐量,无法保证老年代的吞吐量。从JDK1.6开始,ParallelOld成为Parallel Scavenge 的老年代收集器版本;
在注重吞吐量的前提下,使用Parallel Scanvenge + Parallel Old作为组合;在多核CPU且对吞吐量及其敏感的Server系统中推荐使用;

6.CMS收集器(多线程标记清除收集器)

Concurrent Mark Sweep (CMS)垃圾收集器时针对老年代的一个并发线程的垃圾收集器,其目的是获取最短垃圾回收停顿时间,它采用的是多线程的标记—清除,但是它需要更多的内存来完成这个动作;多应用于:

  • 与用户交互较多的场景
  • 希望系统的停顿时间最短,注重服务的响应速度
  • 给用户带来较好的体验
  • 常见的WEB、B/S系统的服务器应用上

CMS的运行过程

1.初始标记
只是标记一下GC Roots能直接关联的对象,速度很快但仍然需要暂停所有的工作线程;

目前主流的JVM都是准确式GC,可以直接得知哪些内存存放着对象引用,所以系统执行GC Roots停顿时,并不需要全部、逐个查找全局性和执行上下文的引用位置;
.
在HotSpot中,是是用一组称为OopMap 的数据结构来达到这个目的的:

  • 在类加载时计算对象内什么偏移量上是哪个数据
  • 在JIT编译的时候,也会记录栈和寄存器中哪些位置是引用

这样在GC扫描的时候就可以直接得到信息

2.并发标记
进行GC Roots跟踪的过程,从刚才产生的集合中标记存活的对象,并发执行不需要暂停工作线程;
但是并不能保证标记出所有的存活对象;

3.重新标记
为了修正并发标记期间因为用户程序继续运行而导致标记变动的那一部分对象的标记记录;需要“Stop The World”且停顿时间比初始标记时间长但远比并发标记的时间短;
4.并发清除
回收所有的垃圾对象;

–gc

清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除阶段垃圾收集线程是和用户线程并行工作的,所以总体来看CMS的内存回收和用户线程是一起并发执行的

CMS收集器显著的三个缺点:

概述 详解 解决方案
对CPU资源敏感 并发收集虽然不会暂停用户线程,但是因为占用了一部分的CPU资源,所以造成系统变慢,吞吐量降低 增量式并发收集器,类似于抢占式来模拟多任务机制的思想,让收集线程和用户线程交替运行,减少收集线程的运行时间
无法处理浮动垃圾,可能出现Concurrent Mode Failure失败 (1)在并发清除时,用户线程产生新的垃圾叫做浮动垃圾,这使得在并发清除之前需要预留一定的内存空间,不能像其他收集器一样等到老年代快要填满的时候在进行收集 (2)如果CMS预留空间无法满足程序要求,就会出现一次Concurrent Mode Failure失败;这使得CMS启动临时预案:使用Serial Old来处理老年代垃圾,这将会导致另一次的Full GC
产生大量的内存碎片 由于CMS采用的是标记-清除算法,所以在垃圾回收之后不能进行压缩操作,造成内存碎片问题;若在内存碎片不足以值称创建一个新的的大型对象的时候,会触发Full GC对新生代+老年代进行垃圾回收 (1)-XX:+UseCMSCompactAtFullCollection使得CMS出现问题时不进行Full GC而是开启内存碎片的整合过程(2)-XX:+CMSFullGCsBeforeCompaction 设置n次的不压缩碎片的Full GC之后来一次压缩整理

7.G1收集器

G1 (Garbage-First)垃圾收集器,是在JDK1.7之后才出的一款商用的垃圾回收器;

G1的收集步骤

步骤 解释
初始标记(Initial Marking) 仅标记一下GC Roots能关联的对象,且修改TAMS(Next Top At Mark Start)让下一个阶段并行运行时用于程序能在可用的region中创建对象 ,需要Stop The World 但是速度很快
并发标记(Concurrent Marking) 在进行GC Roots 遍历的时候,标记出刚才存活的对象
最终标记(Final Marking) 为了修改在并发标记期间因为用户的并行运行而导致标记变动的那一部分的记录,需要Stop The World且停顿的时间比初始标记稍长,但是比并发标记短
筛选回收(Live Data Counting and Evacuation) 采取复制算法,从一个或多个region复制存活对象到堆的另一个region,并且在此过程中压缩和释放内存,可采用并发进行,降低停顿时间,并增加吞吐量


它的特点有以下几种:

特点1:并行与并发

G1收集器能充分利用CPU、多核环境下的硬件优势,使用多个CPU核心来缩短Stop The World的停顿时间。部分其他的垃圾回收器需要在GC的时候使工作线程停下来,而G1和CMS一样都可以通过并发回收的方式让收集线程和工作线程一起并行执行;

特点2:分代收集,收集范围包括新生代和老年代

虽然G1不需要与其他的垃圾回收器配合,自己可以管理整个堆的GC,但是他还是保留了分区的概念。他能够采用不同的方式去处理新生的对象和存活时间很长的对象;

特点3:内存布局

G1将内存分为若干个等大小的区域(region),Eden/Survivor/Old分别是region的逻辑集合,物理上存在并不一定连续;

G1在GC策略上已经没有了Full GC,他区分的使Fully Young GC和Mixed GC;

GC策略 解释
Fully Young GC 年轻代的垃圾回收
Mixed GC 混合了年轻代和老年代的GC

但是在这里存在一个跨代引用的问题:
当垃圾回收时,先从GC Roots开始,这会先经过新生代,在经过老年代,这没有什么问题。但是老年代引用新生代的对象会影响新生代的 Young GC ,这种跨代需要处理。为了避免在回收年轻代的时候扫描整个老年代,需要记录老年代对年轻带的引用,Young GC的时候只需要扫描这个记录。
在CMS和G1中都用到了一个叫做Card Table(卡表)的数据结构,但是用法不太一样。

收集器 用法
CMS JVM将内存分为一个固定大小的card,然后用card table来维护每个card的状态,一个字节对应一个card。当一个card的状态发生变化,就将这个card的card table的状态置为dirty,young gc只需要扫描dirty card即可
G1 在card的基础上引入Rset(remember set)。每个区域(region)都会维护一个rset,记录着从本region到其他region的card。比如A对象在region A,B对象在region B ,且B.f=A ,则region A中的rset需要记录region B 的card所在地址这样的好处是可以对region进行单独回收,这要求rset不但要维护从年轻代到年老代的引用,也要维护年老代到年轻代的引用,对于跨代扫描只需要每次都扫描rset上的card

特点4:实现多种垃圾收集算法

  • 从整体上看,是基于标记-整理算法;
  • 从局部看,是基于复制算法;
    他们都不会产生内存碎片,有效的利用了系统资源;

特点5:可预测的GC停顿

G1除了追求低停顿外,还能建立可预测的停顿时间模型;其可以明确的指定M毫秒时间片内,垃圾消耗时间不超过N毫秒;

为什么G1可以实现预测停顿时间模型:

  • 可以有计划的避免在Java堆中进行全区域的垃圾回收;
  • G1根据各个region获取其收集价值的大小,在后台维护一个优先列表;
  • 每次根据允许的收集时间,优先回收价值最大的region(名称Garbage -First的由来)

Java中七大垃圾回收器相关推荐

  1. Java中的垃圾回收器的类型概述 《对Java的分析总结》(六)

    1.垃圾收集器的类型 从不同的角度来分析垃圾收集器,可能将其分为不同的类型 1./垃圾收集器是垃圾回收算法(标记-清除算法.复制算法.标记-整理算法.火车算法)的具体实现 2.不同商家.不同版本的JV ...

  2. java SE 费用_Java SE 6中的垃圾回收器G1收费是虚惊一场

    [51CTO快译]在Sun宣布Java SE 6 update 14版本中的垃圾回收器G1将收费之后,引起了Java社区相当大的反响.之后不久的6月5日,有一个细心的匿名读者发表了这样一个帖子: &q ...

  3. 四大垃圾回收算法七大垃圾回收器

    JVM的运行时内存也叫做JVM堆,从GC的角度可以将JVM分为新生代.老年代和永久代. 其中新生代默认占1/3堆内存空间,老年代默认占2/3堆内存空间,永久代占非常少的对内存空间. 新生代又分为Ede ...

  4. Java中的垃圾回收与对象生命周期

    转载自   Java中的垃圾回收与对象生命周期 1. 垃圾回收 垃圾回收是Java程序设计中内存管理的核心概念,JVM的内存管理机制被称为垃圾回收机制. 一个对象创建后被放置在JVM的堆内存中,当永远 ...

  5. 【java虚拟机序列】java中的垃圾回收与内存分配策略

    在[java虚拟机系列]java虚拟机系列之JVM总述中我们已经详细讲解过java中的内存模型,了解了关于JVM中内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略. 垃 ...

  6. JVM七大垃圾回收器上篇Serial、ParNeW、Parallel Scavenge、 Serial Old、 Parallel Old、 CMS、 G1

    GC逻辑分类 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本. 从不同角度分析垃圾收 ...

  7. 一篇文章搞定java中的垃圾回收机制面试题

    一篇文章搞定java中的垃圾回收机制面试题 任何语言在运行过程中都会创建对象,也就意味着需要在内存中为这些对象在内存中分配空间,在这些对象失去使用的意义的时候,需要释放掉这些内容,保证内存能够提供给新 ...

  8. 一文读懂Java七大垃圾回收器

    java和c++最大的不同就是java中不需要手动去回收对象,在运行时jvm会自动帮我们进行垃圾回收,接下来让我们揭开垃圾收集器的面纱 名词解释 并行:多条线程并行工作 并发:多个线程并发执行,但他们 ...

  9. Java中的垃圾回收

    转载自http://www.wolfbe.com/detail/201609/365.html GC算法基础 摘要:研究人员发现应用中绝大多数的内存分配会分为两大类:绝大部分的对象很快会变为不可用状态 ...

最新文章

  1. 1.7 Java字符流的使用:字符输入/输出流、字符文件和字符缓冲区的输入/输出流
  2. python金字塔图绘制_如何用R或Python绘制3d(4变量)三元(金字塔)图?
  3. [当人工智能遇上安全] 7.基于机器学习的安全数据集总结
  4. 《数字短片创作(修订版)》——第一部分 剧本创作 第1章 数字短片创意技法 剧本创作的构思...
  5. 订单不断,我是这样做的
  6. 中国剩余定理 互质与非互质版本
  7. 【MySQL】PREPARE 的应用
  8. C#序列化和反序列化代码总结
  9. Eigen编译_Eigen向量化_内存对齐 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  10. SQL显示数据库恢复挂起解决
  11. 大数据中心大数据资源平台建设方案
  12. Guava的两种本地缓存策略
  13. 新浪邮箱界面登录按钮效果制作
  14. 设计模式之原型模式【选用鸣人影分身阐述】
  15. C++ endl 详解
  16. tcprewrite批量修改报文ip地址一
  17. 数据分析师的工资水平如何?
  18. 【大数据】服务器硬盘基础知识
  19. 里奥机器人控制app_Mio控制APP-mio机器人控制app1.1.3 安卓版【咪奥机器人控制】-东坡下载...
  20. Lingke: A Fine-grained Multi-turn Chatbot for Customer Service

热门文章

  1. 基于Matlab Robotics Toolbox的Dobot机械臂运动规划(1)
  2. Spring框架技术总结(一)
  3. 瀑布模型,冯.诺依曼结构——摘自百科
  4. 全同态加密(FHE)设计思路
  5. 【抽象代数】素理想、极大理想、唯一析因环、主理想整环、欧几里得环
  6. 计算机国二外键题,2015年计算机二级考试《MySQL》练习试题模拟
  7. RX5700XT和RX6700XT的区别 RX 5700XT和RX 6700XT选哪个好
  8. 关于https://urs.microsoft.com/urs.asmx .
  9. 5 个好用的开发者 Vim 插件
  10. 在这款任天堂游戏里,我发现了一部关卡设计的黄金教案