垃圾收集器:利用垃圾收集算法,实现垃圾回收的实践落地。

1 HotSpot垃圾回收器

HotSpot垃圾回收器有多个,可以配合使用。

1.1 垃圾回收的一些术语

术语: Stop the world

简写为STW,也叫全局停顿,Java代码停止运行,native代码继续运行,但不能与JVM进行交互。

STW主要是为了GC操作的准确性和效率。用户线程不停止的话,会不断有新对象和垃圾对象产生,假设没有STW,会导致GC时间过长,错误清理新对象等情况。

原因:多半由于垃圾回收导致;也可能是Dump线程、死锁检查、Dump堆等导致

危害:服务停止、没有响应主从切换(对于高可用环境,如果停顿时间过长,会引发主从之间的切换)、危害生产安全。

因此,尽量缩短Stop the world的时间

术语--并行收集 VS 并发收集

并行收集:指多个垃圾收集线程并行工作,但是在收集的过程中,用户线程(你的业务线程)还是处于等待状态的

并发收集:指用户线程与垃圾收集线程同时工作

术语-- 吞吐量

CPU用于运行用户代码的时间与CPU总消耗时间的比值

公式:运行用户代码时间/(运行用户代码时间+垃圾收集时间)

2 新生代垃圾收集器(与老年代收集器配合使用)

2.1 Serial收集器

Serial 收集器是最基本、发展历史最悠久的收集器,采用复制算法。采用单线程操作,收集过程中全程STW

适用场景有:

  • 客户端程序,应用以-client 模式运行时,默认使用的就是Serial(java -client -jar 运行)
  • 单核机器

2.2 ParNew收集器

Serial收集器的多线程版,除使用多线程以外,其他和Serial收集器一样,包括:JVM参数、Stop the world表现、垃圾收集算法都是一样的。可使用 -XX:ParallelGCThreads设置垃圾收集的线程数。主要和CMS垃圾收集器配合使用。

2.3 ParallelScavenge收集器

ParallelScavenge收集器,是吞吐量优先收集器,也是采用复制算法,也是多线程的。执行过程与ParNew收集器类似。

Parallel Scavenge收集器适用于注重吞吐量的场景。

Parallel Scavenge收集器特点

可以达到一个可控制的吞吐量

  • -XX:MaxGCPauseMillis:控制最大的垃圾收集停顿时间(尽力)

  • -XX:GCTimeRatio:设置吞吐量的大小,取值0-100,系统花费不超过1/(1+n)的时间用于垃圾收集

自适应GC策略:可用-XX:+UseAdptiveSizePolicy打开

  • 打开自适应策略后,无需手动设置新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)等参数虚拟机会自动根据系统的运行状况收集性能监控信息,动态地调整这些参数,从而达到最优的停顿时间以及最高的吞吐量。

3 老年代收集器(与新生代收集器配合使用)

3.1 Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,单线程,采用的是标记-整理算法,垃圾收集过程Stop The World。

可以与上面三个新生代收集器配合使用;当CMS收集器出现故障时,作为后备处理器。

3.2 Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,只能与Parallel Scavenge收集器配合使用,同样是注重对吞吐量要求较高的场景。

Parallel Old收集器,采用多线程,标记-整理算法,垃圾收集过程Stop The World。

3.3 CMS收集器(JDK9废弃)

CMS: Concuerrent mark sweep(并发标记收集器),采用标记-清除算法。

CMS执行过程的七个阶段:

  1. 初始标记:标记GC Roots能直接关联的对象,触发短暂STW操作
  2. 并发标记:找出所有GC Roots能关联的对象。并发执行,不会触发STW。
  3. 并发预清理阶段(可选):重新标记在并发标记阶段引用被更新的对象,从而减少后面重新标记的工作量。并发执行,不会触发STW。可使用-XX:-CMSPrecleaningEnabled关闭并发预清理阶段,默认打开。
  4. 并发可中止的预清理阶段(可选):和并发预清理做的事情一样,并发执行,无 Stop The World。预清理后,当Eden的使用量大于CMSScheduleEdenSizeThreshold的阈值(默认2M)时,才会执行该阶段。主要作用是允许我们能够控制预清理阶段的结束时机。
  5. 重新标记阶段:修正并发标记期间,因为用户程序继续运行,导致标记发生变动的那些对象的标记。一般来说,重新标记花费的时间会比初始标记阶段长一些,但比并发标记的时间短。存在 Stop The World。
  6. 并发清除阶段:基于标记结果,清除垃圾对象。并发执行,无STW。使用标记清除算法,因为标记整理涉及数据位置,并发情况难以实现。
  7. 并发重置阶段:清除本地CMS GC的上下文信息,为下次GC做准备。

CMS收集器的优缺

优点:STW时间较短,大多数过程并发执行。

缺点:

  1. 垃圾回收并发执行时,会与用户线程有一定CPU资源争抢,影响吞吐量;
  2. CMS只在标记的时候STW,清除时无STW,无法处理浮动垃圾;
  3. 采用标记-清除法,导致内存碎片的产生;
    1. 可以使用UseCMSCompactAtFullCollection:在完成Full GC后是否要进行内存碎片整理,默认开启。CMSFullGCsBeforeCompaction:进行几次Full GC后就进行一次内存碎片整理,默认是0。
  4. 无法等到老年代几乎满了才开始垃圾收集。
    1. 执行过程中多个阶段没有STW操作,会不断有对象晋升到老年代,当老年代预留的内存不够时,会导致Concurrent Mode Failure,从而切换成后备老年代收集器 Serial Old。
    2. 可使用 CMSInitiatingOccupancyFraction设置老年代占比达到多少就触发垃圾收集,默认68%。

CMS适用于希望系统停顿时间短,响应速度快的场景,例如Web。

扩展:并发预处理、并发中断预处理

1、首先,CMS是一个关注停顿时间,以回收停顿时间最短为目标的垃圾回收器。并发预处理阶段做的工作是标记,重标记需要STW(Stop The World),因此重标记的工作尽可能多的在并发阶段完成来减少STW的时间。此阶段标记从新生代晋升的对象、新分配到老年代的对象以及在并发阶段被修改了的对象。

2、并发可中断预清理(Concurrent precleaning)是标记在并发标记阶段引用发生变化的对象,如果发现对象的引用发生变化,则JVM会标记堆的这个区域为Dirty Card。那些能够从Dirty Card到达的对象也被标记(标记为存活),当标记做完后,这个Dirty Card区域就会消失。CMS有两个参数:CMSScheduleRemarkEdenSizeThreshold、CMSScheduleRemarkEdenPenetration,默认值分别是2M、50%。两个参数组合起来的意思是预清理后,eden空间使用超过2M时启动可中断的并发预清理(CMS-concurrent-abortable-preclean),直到eden空间使用率达到50%时中断,进入重新标记阶段。

4 G1收集器

G1(Garbge First),面向服务端应用,可以同时用于新生代和老年代。

G1收集器使用Region作为单位,包括四种类型,分别为Eden、Survior、Old和Humongous,通过参数-XX:G1HeapRegionSize指定Region的大小,取值范围为1MB ~ 32 MB之间。

前三种Region仍然是伊甸园、存活区、老年代,Humongous用来存储大对象,对象过大可以存储到连续的Humongous中。Old和Humongous同属于老年代。

G1收集器的设计思想将内存分成很多小块(Region),跟踪每个Region中垃圾堆积的价值大小,构建一个优先列表,根据允许的收集时间,优先回收价值最高的Region。其中价值大小指的是回收该Region能获得的空间大小以及回收所需要的时间成本。

4.1 G1收集器的垃圾回收机制

主要分为三种,Young GC、Mixed GC、Full GC。

4.1.1 Young GC

所有Eden Region都满了的时候,就会触发Young GC。

  • Eden中存活的对象转移到Survior Region;
  • 原先 Survivor Region中的对象转移到新的 Survivor Region中,或者晋升到Old Region。
  • 空闲 Region会被放入空闲列表中,等待下次被使用。

4.1.2 Mixed GC

老年代大小占整个堆的百分比达到一定阈值(可用-XX:InitiatingHeapOccupancyPercent指定,默认45%),就触发Mixed GC,会回收所有 Young Region,同时回收部分 Old Region。

执行过程分为四步:

  1. 初始标记:跟CMS类似,标记处GC Roots能直接关联到的对象,存在短暂STW

  2. 并发标记:跟CMS类似,找出所有GC Roots能关联的对象。并发执行,不会触发STW。

  3. 最终标记:更新在并发标记期间引起的变更,存在STW

  4. 筛选回收:首先对各个Region的回收价值和成本排序,根据用户所期望的停顿时间指定回收计划,筛选出合适的Region回收。停顿时间:MaxGCPauseMillis。

    回收过程:将Region的存活对象复制到空闲Region中,然后删除原Region,是复制算法,无内存碎片,过程存在STW。

G1收集器Mixed GC除了并发标记以外的过程都是STW的,由于一次只回收一部分Region,所以停顿时间可控。

4.1.3 Full GC

复制对象内存不够,或者无法分配足够的内存(例如大对象无法分配连续的内存),就会触发Full GC。Full GC机制采用单线程的Serial Old模式。

因此G1收集器的优化原则是,尽可能的减少Full GC。

4.2 G1优化原则:减少Full GC

  1. 增加预留内存(增大-XX:G1 Reserve Percent,默认为堆的10%)
  2. 更早地回收垃圾(减少- XX: InitiatingHeapOccupancyPercent,老年代达到该值就触发 Mixed GC,默认45%
  3. 增加并发阶段使用的线程数(增大-XX: ConcGCThreads)

上面三点大白话总结:多留一点内存,有内存可以分配;有垃圾早点回收;垃圾回收快点。

4.3 G1收集器的使用

G1收集器作用于整个堆,可以控制停顿时间(MaxGCPauseMillis=200),并且没有内存碎片。

G1收集器占用内存较大(通常需要6G以上),可以代替CMS收集器。对于JDK8,主要根据内存选择,内存小于6G,选CMS;内存大于6G,使用G1。CMS在JDK9中被废弃,高于JDK8的版本可以选G1。

5 实验收集器

截止目前JDK14依然处于实验状态

  • Shenandoah(IBM开发,是ZGC竞品,进入OpenJDK,被Oracle JDK12剔除。)

  • ZGC(一款革命性的收集器)

  • Epsilon:不干活的垃圾收集器。

    • 控制内存分配,但是不执行垃圾回收工作,堆耗尽就直接关闭JVM。

6 如何选择垃圾收集器?

理论出发主要有下面几点。

  1. 项目主要关注矛盾点。例如吞吐量(Parallel)、访问延迟(CMS/G1)、应用启动速度
  2. 基础设施:CPU、内存等
  3. JDK版本。例如JDK6没有G1,Oracle JDK没有Shenandoah。

最新文章

  1. iOS 键盘遮挡输入 解决办法
  2. php文件写入PHP_EOL与FILE_APPEND
  3. 浅谈T-SQL中的子查询
  4. Visual Studio开发环境最佳字体及配色
  5. 【BZOJ】【2819】NIM
  6. 高性能Socket组件和RPC,让你像写本地代码一样开发网络应用和分布式程序
  7. Linux 下修改Tomcat使用的JVM内存大小
  8. ASP.NET Core官方资料入口
  9. Bootstrap入门(九)组件3:按钮组
  10. Unity3D高性能战争迷雾实现
  11. Rich feature hierarchies for accurate object detection and semantic segmentation(理解)
  12. python web 开发
  13. 八大排序算法——选择排序(动图演示 思路分析 实例代码Java 复杂度分析)
  14. scala中的isInstanceOf和asInstanceOf
  15. str和unicode类
  16. 向大家介绍15个漂亮的Ubuntu GDM主题
  17. [vue]vue-cli下载原理
  18. 使用jQuery可能出现的错误
  19. 获取url路径中的参数
  20. vector中删除的注意事项

热门文章

  1. Ubantu nodejs卸载与二进制安装
  2. Javascript 数组对象常用的API
  3. 【Matlab】线性调频信号LFM 仿真
  4. 【JavaWeb】【Maven】001 下载与配置
  5. apt和apt-get的区别
  6. 使用hbuilder打包vue项目容易出现的坑点
  7. [BUUCTF]PWN16——jarvisoj_level2
  8. Table.PromoteHeaders升降标题Table…Headers(Power Query 之 M 语言)
  9. 从头造轮子:asyncio之 run_until_complete (1)
  10. git 生成ssh