垃圾收集器简单说明(《 深入理解 java 虚拟机》读后摘录)

本贴最后更新于 3003 天前,其中的信息可能已经物是人非
该文简述了常用垃圾收集器的特点与区别,内容主要来自《深入理解java虚拟机》,方便在对比的时候有个参考!
 
这是收集器的关系图:
 
 
 
 
这是hotspot 的七种作用于不同分器之间有连线,说明他代的收集器,,如果两个收集们可以搭配使用,虚拟机所处的区域,表示新生代还是老年带收集器。

新生代的垃圾收集器有:Serial收集器、ParNew收集器、Parallel Scavenge收集器

老年代的垃圾收集器有:Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器

概念解释:
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序继续运行,而垃圾收集程序运行在另一个cou核心上
Stop-The-World  : 简称STW ,表示全局暂停,表现为JAVA代码停止,native代码可以执行,但是不能和JVM交互,大部分情况是GC引起的
 
垃圾收集算法说明:标记-清除算法、复制算法、标记-整理算法、分代收集算法
 
标记-清除算法
该算法如同它的名字一样,分为两个阶段:标记、清除。首先标记出所有需要回收的对象,然后,统一清除这些被标记的对象。该算法的缺点是:1、效率不高;2、产生大量不连续的内存碎片,导致有大量内存剩余的情况下,由于,没有连续的空间来存放较大的对象,从而触发了另一次垃圾收集动作。

复制算法

由于标记-清除算法的效率不高,从而提出了复制算法。复制算法将可用的内存分成两样大小的两块,每次只使用其中一块内存。当这块内存用完之后,就把还存活的对象复制到另外一块上面,然后,把这块清空。复制算法克服了标记-清除算法的两个缺点,但是太浪费内存,相当于内存空间减小了一半。

随着时间的积累,现在使用的复制算法的虚拟机,不再是把内存分为1:1的两块。因为98%的对象是寿命很短的,创建之后,很快就被回收了,存活下来的只有2%,所以,用来存储存活对象的内存区,可以小一些。现在的商业虚拟机是把可用内存分为一个较大的Eden空间和两个较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,把Eden和Survivor中的存活对象一次复制到另一块Survivor内存区上,然后把Eden和刚才用过的Survivor空间清空。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,这样,每次新产生的对象可以使用90%的内存空间。

标记-整理算法

从名字可以看出,该算法是对“标记-清除”算法的改进升级版。同样的该算法分为两个阶段:标记、整理。标记阶段同“标记-清除”算法。整理阶段,不是直接对标记对象进行清理,而是让所有存活的对象都移动到一端,然后,直接把边界以外的内存清空。这就解决了“标记-清除”算法会造成大量不连续内存碎片的问题。

分代收集算法

分代收集算法是根据对象的存活周期的不同,将内存划分为几块。当前的商业虚拟机的垃圾收集都采用了该算法。一般把Java堆分成新生代(年轻代)和老年代(年老代)。这样就可以根据各年代中对象的存活周期来选择最合适的收集算法了。新生代,由于只有少量的对象能存活下来,所以选用“复制算法”,只需要付出少量存活对象的复制成本。老年代,由于对象的存活率高,没有额外的空间分担,就必须使用“标记-清除”或“标记-整理”算法。

1.Serial 收集器【单线程-复制算法-新生代
 
Serial 收集器是最基本,历史最久的收集器(单线程),在jdk1.3.1之前是虚拟机新生代收集的唯一选择,在进行垃圾收集工作的时候必须暂停所有其他的工作线程(用户不可见的情况下把用户的正常工作线程停掉一段时间),直到收集结束。
jvm Client 模型下的默认新生代收集器,对于单个cpu的环境来说,Serial  由于没有线程交互的开销,收集效率更好。
 
 
运行示意图:
 
2.ParNew 收集器【多线程-并行-复制算法-新生代
 
ParNew 其实就是Serial收集器的多线程版本,除了使用多个线程进行垃圾收集,其它行为(控制参数、收集算法、STOP THE WORD 、对象分配规则、回收策略)和Serial 完全一致.与Serial 共用了很多代码。
它是许多运行jvm server 模式中首选的新生代收集器,其中一个与性能无关且很重要的原因是,目前只有它能与CMS收集器配合工作(请看收集器关系图)
 
运行关系图(ParNew /Serial old):
 
默认开启的收集线程数与cpu核心数相同,内核较多的情况下可以使用参数   -XX: ParallelGcThreads 参数来限制收集线程数
-XX: +UseParNewGC  强制指定  ParNew收集器
-XX: +UseConcNarkSweep-GC 指定cms收集器,新生代收集器将默认为ParNew
 
3.Parallel Scavenge 收集器 【多线程-并行-复制算法-新生代】
 
Parallel Scavenge 收集器的特点是它 的关注点与其他收集器不同,CMS等关注点是尽可能的缩短垃圾回收时用户线程的停顿时间,而Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量(Throughput)。所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即:吞吐量=运行用户代码时间 /  (运行用户代码时间+垃圾收集时间),虚拟机总共运行100分钟,其中垃圾收集花了1分钟,那么吞吐量就是99%。
 
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验;而高吞吐量则适合高效利用CPU时间的应用,例如运算任务和交互较少的应用.
 
Parallel Scavenge 收集器提供  -XX: MaxGCPauseMillis 参数控制最大垃圾收集停顿时间(取值范围大于0)
                                                  -XX: GCTimeRatio 参数控制吞吐量大小,默认值为99(取值范围1-99)
                                                  -XX:+UseAdaptiveSizePolicy 自动调整新生代停顿/吞吐的开关(和ParNew的一个重要区别)
 
 -XX: MaxGCPauseMillis:该参数不是越小越好(收集速度更快),GC的停顿时间缩短是以牺牲吞吐量和新生代空间来换取的。
                                   例一:系统新生代调小一点,收集200M,当然要比收集500M速度要快
                                   例二:原来10秒收集一次,每次停顿100毫秒,现在5秒收集一次,每次停顿70毫秒,停顿降低了,但是吞吐量也降低了(按60秒算,60/10*100=600ms < 60/5*70=1400ms
 
  -XX: GCTimeRatio表示用户程序占用总时间的比值,垃圾收集是吞吐量的倒数;默认为:99,就是允许最大99%,垃圾收集即【1/(1+99)=0.01】,垃圾收集占1%,如设置为19,吞吐量是95%,垃圾收集占5%【1/(1+19)=0.05】)
 
 -XX:+UseAdaptiveSizePolicy:开启则不需要手动指定新生代大小-Xmn、eden与Survivor的比例-XX:Sur-vivorRatio、晋升老年的年龄-XX:Pretenure-SizeThreshold等参数,设置最大堆-Xmx和以上两个参数就好,系统会根据当前系统运行情况手机性能监控信息,动态调整这些参数提供合适的停顿时间和吞吐(GC自适应调节策略GC Er-Scavenge),比较适合对收集器运作原理不了解的朋友。
 
4.Serial Old 收集器 【单线程-标记整理算法-老年代】
 
该收集器是Serial 收集器的老年代版本主要在client 模式下的 JVM使用。
如果在 server 模式下有两大用途:
1.在jdk以及之前版本中与 Parallel Scavenge 收集器配合使用。
2.作为CMS 收集器的后备预案
 
运行图:
 
5.Parallel Old 收集器【多线程-标记整理算法-老年代】
 
Parallel Old 收集器 是Parallel 收集器 的老年代版本,使用多线程和“标记-整理算法”
该收集器在jdk1.6中开始提供,在此之前新生代收集器如果选择了Parallel Scavenge,则老年代收集器只能选择Serial Old 收集器。
由于老年代Serial Old收集器的性能“拖累”,使用Parallel Scacenge收集器未必能在“整体"应用上获得吞吐量最大化的效果,有时候还不如ParNew+CMS的组合的整体性能好
 
Parallel Old 收集器出现后,Parallel Scacenge(吞吐量优先)有了比较名副其实的组合。在注重吞吐量和对cpu资源比较敏感的场合都可以优先考虑Parallel Scacenge + Parallel Old
 
运行图:
 
 
6.CMS收集器 【多线程-标记清除算法-老年代】

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,使用“标记-清除”算法。

      CMS收集器分4个步骤进行垃圾收集工作: 1.初始标记(STW) 2.并发标记 3.重新标记(STW) 4.并发清除

    

      其中“初始标记”、“重新标记”是需要暂停其它所有工作线程的。初始标记只是标记一下GC Roots 能直接关联到的对象,速度很快,并发标记就是进行GC RootsTracing的过程,重新标记是修正并发标记后产生变动的一部分对象(通常比初始标记稍微长一点);通常情况下标记耗时:初始标记<重新标记<并发标记


      缺点:

      1.cms收集器对CPU资源非常敏感,并发阶段虽然不会导致用户线程停顿,但是会因为占用一部分线程导致程序变慢,总吞吐降低。(默认回收线程是(CPU数量*3)/4,也就是4核以上时垃圾回收占用不少于25%的CPU资源,内核低于25%影响更大,为了应付突然用户执行速度降低较大的情况,虚拟机提供了“I-cms”,垃圾回收线程和用户线程交替执,这样会让垃圾回收时间更长,但是会对用户影响较小,现在不推荐使用I-CMS了)

      2.CMS无法处理浮动垃圾(Floating Garbage),可能会导致"Concurrent Mode Fail-ure" 失败而导致另一次Full GC的产生。由于CMS并发清理用户线程在运行,会持续有新垃圾产生,这部分出现在标记过程后,CMS无法在当次收集清理掉,需要下一次GC时清理,就叫“浮动垃圾"。

        由于垃圾收集,用户线程在运行需要预留足够的内存,因此CMS收集器不像别的收集器要等到老年代几乎完全被填满了再收集,需要预留一部分内存提供并发收集时候的程序运行使用,JDK1.6默认CMS收集器默认启动阈值是92%,如果预留的空间不够用户线程使用,会出现"Concurrent Mode Faillure"失败,这时候虚拟机启动后备方案,临时启动Se-rial Old 收集器来重新收集老年代,这样收集时间比较长, 所有:参数-XX: CM SIniti-atingOccupancyFraction设置太高容易导致大量 "Concurrent Mode Failure"失败,导致性能下降.

       3.由于标记-清除算法实现的收集器会导致大量空间碎片,会给大对象内存分配带来麻烦,导致提前(总体空间够)出发FULL GC ,为了解决这个问题,

        CMS提供了-XX: +UseCMSCom-PactAtFullCollection开关参数(默认开),用来在CMS顶不住要Full GC的时候进行内存碎片整理,内存整理无法并发,空间碎片问题解决了,但是停顿时间会变长。

        CMS提供了-XX: CMSFullGCsBe-foreCompaction参数用来设置执行多少次不压缩的Full GC后进行一次带压缩的FULL GC,默认值为0,表示每次Full GC 都要进行碎片整理

 

运行图:
 
7.G1收集器 【多线程-标记整理算法-不分代】
 
  G1(Garbage First)收集器,基于“标记-整理”算法,可以非常精确地控制停顿。
特点:
        1.并行与并发:充分利用多CPU/多核硬件优势来缩短STW,部分其它收集器需要停顿JAVA线程进行GC,
        2.分代收集:G1依然保留分代,但是G1可以不需要其它收集器配合独立管理整个GC堆,能采用不同的方式处理新建对象、存活一段时间对象、多久GC后的对象以获取更好的手机效果。
        3.空间整合:G1从整体上看是基于“标记-整理”算法,从局部看(两个Region之间)上看是基于“复制”算法,这两种算法都不会产生内存碎片。
        4.可预测的停顿:这是G1相对CMS的另一个优势,降低停顿是G1和CMS共同的关注点,但是G1除了降低停顿,还能提供可预测的时间模型,能让使用者明确指定赢长度为M毫秒的时间片内,消耗在垃圾收集上的时间不超过N毫秒,这几乎是实时java(RTSJ)的垃圾收集器的特征咯。
 
    G1之前的收集器都是进行整个新生代/老年代的收集,G1将堆分为多个大小相等的独立区域(Region),虽然还保留新生代/老年代的概念,但是新生代和老年代不再是物理隔离,他们都是一部分Region(不连续)的集合。每个Region都有一个对应的”Remembered Set“用来避免进行全堆扫描,虚拟机发现程序对Reference 引用的对象进行写操作时没回产生一个 Write Barrier 暂时中断写操作,检查引用对象是否在别的Region中(分代收集器中就是检查老年代是否引用新生代对象)。如果存在就通过CardTable吧相关引用信息记录到被引用对象所属的Region 的Remembered Set中,在内存回收时,在根节点的枚举范围中加入Remem-Bered Set 即可保证不对全堆扫描,也不会遗漏。
    
    如果不算维护Remem-Bered Set 的操作,G1运行步骤分为     1.初始标记 2.并发标记  3.最终标记 4.筛选回收
    开始的运行步骤和CMS比较类似,
    ”初始标记“只是标记GC ROOTS 能直接关联的对象,并修改TAMS(Next Top at Mark Start)的值,让下阶段用户程序并发运行时能在正确可用的Region中创建新对象,需要停顿线程,但耗时很短。
    ”并发标记“从GC ROOT开始对堆对象进行可达性分析,找出存活对象,这阶段耗时较长,但是可以和用户线程并发执行
    ”最终标记“是为了修正并发标记旗舰变化的部分,虚拟机将对象变化的记录在线程Remembered Set Logs 中,最终标记是吧Remembered Set Logs  数据合并到Remembered Set 中,这阶段需要
停顿线程,也可以并行执行
   “筛选回收”阶段首先对各Region的回收价值和成本进行排序,根据用户期望的GC停顿时间制定回收计划,因为只回收一部分Region ,时间用户可控制,所以停顿用户线程将大幅度提高GC效率
 
 
运行图:
 
2016-09-01 谷占东 end
 
 
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • 垃圾收集器
    1 引用 • 1 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • relyn

    涨姿势了

推荐标签 标签

  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 58 关注
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 67 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 134 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    53 引用 • 37 回帖
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    8 引用 • 30 回帖 • 407 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 94 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 51 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    179 引用 • 995 回帖
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 62 关注
  • OnlyOffice
    4 引用 • 3 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 16 关注
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    125 引用 • 169 回帖 • 1 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 31 关注
  • 安全

    安全永远都不是一个小问题。

    199 引用 • 816 回帖 • 1 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖 • 1 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1434 引用 • 10054 回帖 • 490 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    497 引用 • 1387 回帖 • 283 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 664 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 362 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖 • 3 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 463 关注