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

本贴最后更新于 2787 天前,其中的信息可能已经物是人非
该文简述了常用垃圾收集器的特点与区别,内容主要来自《深入理解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 回帖 • 2 关注
  • Java

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

    3167 引用 • 8207 回帖
  • 垃圾收集器
    1 引用 • 1 回帖

相关帖子

欢迎来到这里!

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

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

    涨姿势了

推荐标签 标签

  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 25 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    27 引用 • 66 回帖
  • 倾城之链
    23 引用 • 66 回帖 • 96 关注
  • GitLab

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

    46 引用 • 72 回帖
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    915 引用 • 931 回帖
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 24 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖 • 6 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 4 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 131 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 349 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖 • 2 关注
  • Gzip

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

    9 引用 • 12 回帖 • 111 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 3 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 521 关注
  • 导航

    各种网址链接、内容导航。

    37 引用 • 168 回帖
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 597 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 85 回帖
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    35 引用 • 35 回帖
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    333 引用 • 323 回帖 • 70 关注
  • 创造

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

    172 引用 • 990 回帖
  • ActiveMQ

    ActiveMQ 是 Apache 旗下的一款开源消息总线系统,它完整实现了 JMS 规范,是一个企业级的消息中间件。

    19 引用 • 13 回帖 • 628 关注
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用 • 28 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    9 引用 • 32 回帖 • 169 关注
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    238 引用 • 224 回帖 • 1 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 683 关注