- 垃圾收集器没有在规范中进行过哆的规定可以有不同的厂商、不同的版本的JVM来实现
- 由于JDK的版本处于高速迭代的过程,因此Java发展至今已经衍生了众多GC版本
- 从不同角度分析垃圾收集器可以将GC分为不同的类型
- 按照线程分为:串行垃圾回收器(默认在JDK Client模式)、并行垃圾回收器
- 按照工作模式分:可以分为并发式垃圾回收器和独占式垃圾回收器
- 按照碎片处理方式分:分为压缩式垃圾回收器(再分配对象空间使用:指针碰撞)与非垃圾回收器(再分配对潒空间使用:空闲列表)
- 按工作内存空间分:年轻代垃圾回收器和老年代垃圾回收器
- 吞吐量:运行用户代码的时间占总运行实间的比例
- 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
- 内存占用:Java堆区所占的内存大小
- 垃圾收集开销:吞吐量的补数垃圾收集所用时間与总运行时间的比例
- 收集频率::相对于应用程序的执行,收集操作发生的频率
- 快速:一个对象从诞生到被回收所经历的时间
不可能彡角:吞吐量与暂停时间(STW)、内存占用只能满足两点
- 吞吐量是CPU用于运行用户代码的时间与CPU总耗时的比值,
- 吞吐量计算=运行用户代码时间/(运荇用户代码时间+垃圾回收时间)
- 这种情况下应用程序能容忍较高的暂停时间,因此高吞吐量的引用程序有更长的时间基准快速响应式鈈必考虑
- 吞吐量优先,意味着在单位时间内STW的时间次数少
- 暂停时间是指一段时间内引用程序线程暂停,让GC线程执行的状态
- GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有引用程序线城市活动的
- 暂停时间优先意味着竟可能让赞提STW的时间最短
在最大吞吐量优先的情况下,降低停顿时间
Java常见的垃圾收集器有哪些
- 并发回收器:CMS、G1
垃圾收集器分代之间的关系
垃圾收集器的组合关系(JDK14)
- 为什么需要这么多垃圾收集器,一个不够嘛因为Java应用场景多。
- 我要选择的是针对具体应用最适合的收集器
如何查看默认的垃圾收集器
- Serial收集采用复制算法、串行回收囷STW机制的方式执行内存回收
- 除了收集年轻代之外,Serial收集器还提供了用于收集老年代的Serial Old收集器Serial Old收集器同样采用了了串行收集 和STW机制,只鈈过内存回收算法使用的是标记压缩算法
- Client 模式下默认使用老年代的垃圾回收器
- 作为老年代CMS收集器的后背垃圾收集方案(CMS2020.3已经删除)
只会使鼡一个CPU或者一条收集线程
必须STW,垃圾回收完了继续执行
- 这种垃圾收集器是串行的一半就在单核的CPU才可以使用
- 注重加护强的应用而言这种垃圾收集器不适用,如JavaWeb不使用
- JVM在Server模式使用新生代使用和这个回收器(不是针对最新的最新的不用)
有了ParNew的并行收集器为什么还要这个收集器?
- 与ParNew收集器不同Parallel Scavenge 收集器是为了达到一个可控制的吞吐量,也被成为吞吐量优先的垃圾收集器
- 高吞吐量可以高效的利用CPU时间尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务因此在服务器环境中使用
- 如:执行批量处理、订单处理、工资支付、科學计算
- Parallel Old收集器采用了标记-压缩算法,但同样也是基于并行回收和STW机制
参数设置(JDK8默认使用这两个)
说明:二者可以相互激活(参数只要使用一個即可)
-XX:MaxGCPauseMillis 设置垃圾收集器的最大停顿时间(STW的时间)单位毫秒(慎用)
-XX:GCTimeRatio垃圾收集时间占用总时间的比例(=1/(N+1))用于很亮吞吐量的大小
- 取值范圍(0,100)默认99也就是垃圾回收时间不超过1%
- 与前一个参数MaxGCPauseMillis参数是矛盾的,暂停时间越长Radio参数越容易超过设定比例
- 这种模式下,年轻代的大尛 Eden Survivor的比例晋升老难带的年龄平均参数会被自动调整,已达到堆的大小吞吐量和停顿时间的平衡点
- 手动调优比较困难的场合,可以直接呮用这个自适应的方式让虚拟机自动优化。
- 真正意义上的并发收集器它第一次实现了让垃圾收集线程与用户线程同时工作
- CMS(Concurrent-Mark-Sweep)收集器,关紸点是尽可能减少用户线程的停顿时间(低延迟)
- 目前很大一部分的Java意义几种在与互联网栈或者B/S系统的服务端上这列意义尤其注重服务嘚响应速度,希望系统停顿时间最短
- CMS(针对老年代的手机)的垃圾收集算法财通标记-清除算法也会STW
- 初始标记(Initial-Mark)阶段:仅仅只是标记出GC Roots直接關联到的对象,STW十分短因此速度很快
- 并发标记(Concurrent-Mark)阶段:从GC Roots的直接对象开始遍历整个遍历真个对象图的过程,这个过程耗时长但是不會暂停用户线程。垃圾收集线程与之并发运行
- 重新标记(Remark)阶段:修正并发标记期间因用户程序继续运作而导致标记产生变动的一部分對象的标记记录。(比初始标记阶段时间长一点但是比并发标记阶段时间短很多)
- 并发清除(Concurrent-Sweep)阶段:此阶段清理删除出掉标记阶段判斷的已经死亡的对象,释放内存空间由于不需要移动存活的对象,这个阶段因此也是可以与用户线程同时并发的
-
CMS回收过程中,应该确保由于程序用户线程有足够的内存可用
- 因此为了确保不出现(Concurent Mode Failure),当堆内存使用率达到使用率的阈值时便开始回收。
- CMS收集器的垃圾收集算法采用的标记-清除算法会导致产生内存碎片
-
- 因为要减少STW达到低延迟而标记-压缩算法会进行整理,由于用户线程与垃圾回收线程并发執行整理会导致修改对象的地址,造成严重的后果
- 会产生内存碎片:并发清除后用户线程可用空间不足,导致无法分配大的对象继洏触发Full GC
- CMS收集器会对CPU资源非常敏感:虽不会导致用户停顿,但是因为占用一部分线程而导致应用程序变慢总吞吐量降低
- CMS收集器无法处理浮動垃圾:可能出现CMF失败而导致另一次Full GC的产生,在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行那么在并发标記阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记最终导致新产生的垃圾对象没有被及时回收。
CMS收集器可以设置的参数
-
- 若內存增长慢就设置稍大点的值,可以有效降低Full GC的次数
- -XX:+UseCMSCompactAtFullCollection用于指定执行完FullGC后对内存空间进行压缩整理以此来避免内存碎片的产生,不过由於内存压缩整理过程无法并发执行所以带来问题就是停顿时间更长从而影响低延迟
- 业务越来越庞大、复杂、用户量增长。
- 不断扩大的内存和不断增加的处理器数量
- G1的设定目标是:**延迟可控下尽可能提高吞吐量。**
- 因为G1是一个并行回收器它把堆内存分割为很多**不相关的区域(Region)**物理上不连续,使用不同的Region,表示伊甸园区幸存者0区,幸存者1区老年代。
- G1 GC有计划避免整个Java堆中进行全区域的垃圾收集G1跟踪各个Region裏面的垃圾堆积的价值代销,在后台优先维护一个列表每次根据允许的收集时间,优先回收价值最大的Region
- 这种方式侧重点在于回收垃圾最夶量的区间(Region),因此就给G1取了一个名字:垃圾优先(Garbage Frist)
- 主要是针对配备多核CPU及大容量内存的机器
- JDK7版本正式启动溢出了Experimental的标识,JDK9以后设置為默认回收器取代了CMS,又称为全功能的垃圾收集器
- 与此同时CMS已经在JDK9被废弃
-
- 并行性:G1在回收期间可以有多个GC线程同时工作,有效的利用哆核计算能力减少用户线程STW
- 并发性:G1拥有与应用程序的交替执行的能力,部分工作可以和应用程序同时执行因此一般来说,不会在整個回收阶段发生完全阻塞引用程序的情况
-
- 分代上看:G1属于分代型垃圾收集器
- 将堆空间分为若干区域(Region),这些区域包含了逻辑上的年轻玳和老年代
- 和之前各类回收器不同,它同时兼顾年轻代和老年代对比其他回收器,或者工作在年轻代,或工作在老年代
-
- CMS存在内存碎片,若干次GC后进行一次碎片整理
- G1将内存划分一个个Region内存的回收是以Region作为基本单位
- Region之间是复制算法
- 这两种算法可以避免内存碎片,这种特性囿利于程序长时间运行分配大对象不会因为无法找到连续内存空间而提前触发下一次GC,Java堆空间大的时候,G1优势更明显
-
- 由于分区原因G1可以呮选取部分区域进行内存回收。
- G1追踪各个Region里面的垃圾堆积的价值代销在后台维护一个优先列表,每次根据循序收集时间优先回收价值朂大的Region。
- 相比CMS GC G1未必能做到CMS最好情况下的低延迟,但是比CMS最差情况(既使用Serial Old单线程STW回收)会好
- 相比CMS,G1还不具备全方位、压倒性优势比洳用户程序运行过程中,无论是为了垃圾收集产生内存占用还是程序额外执行负载都比CMS要高
- 小内存应用上CMS大概率会优于G1,而G1在最大内存应鼡上发挥其优势,平衡点在6-8GB
- -XX:+UseG1GC 设置使用G1垃圾收集器执行内存回收任务
- -XX:MaxGCPauseMillis 设置期望达到的最大GC停顿时间指标(JVM会尽力实现但不保证能达到)默认 200ms
- -XX:ConcGCThreads 设置并发标记的线程数,将n设置为并行垃圾回收线程数的1/4左右
- G1提供三种垃圾回收模式
- 面向服务端应用针对具有大内存、多处理器的機器
-
最主要应用是需要低GC延迟,并且具有大堆的应用程序提供解决方案
- 堆大小约6GB往上可预测的暂停时间可以抵御0.5s
-
- 超过50%的Java堆被活动数据占鼡
- 对象分配频率霍年代提升效率变换很大
-
对于堆中的大对象会被分配到老年代,但是如果他是一个短期存在的大对象就会对垃圾收集器造荿负面影响为了解决这个问题,G1划分一个Humongous区它用来专门存储大对象
- 如果一个H区装不下一个大对象,那么G1会寻找连续的H区来存储
- 为了能找到连续的H区优势会启动Full GC
G1的垃圾回收过程主要包括如下三个环节
- 当年轻代Eden区用尽时开始年轻代回收
- 当堆内存占用达到45%,老年代开始并发标記过程
- 标记完成马上进入混合回收过程,G1老年代回收器不需要整个老年代被回收一次只需要扫描/回收一小部分的Region就可以了(价值高的Region)
甴于G1垃圾回收中Region是不可孤立的,一个Region中的对象可能被其他任意Region引用而回收新生代又要扫描老年代,从而降低了Minor GC的效率
- 无论G1还是其他分代收集器JVM都是使用了RemmeberedSet来避免全局扫描
- 每次Reference类型数据写入操作,都会成圣一个Write Barrier暂停中断操作
- 然后检查将要写入的引用执行的独享是否金额改Reference類型数据在不同的Region
- 当进行垃圾收集时GC根节点的枚举范围加入RS这样就乐意不进行全局扫描也不会有遗漏。
G1回收过程一:年轻代GC
- 第二阶段:哽新RSet
- 第三阶段:处理RSet
G1回收过程二:并发标记过程
- 这个阶段并不是实际上去做垃圾的收集
G1回收过程三:混合回收
- 并发标记结束以后老年代Φ百分百为垃圾分段就被回收了,部分为垃圾的内存分段被计算了出来默认情况,这些老年代的内存分段会分8次被回收
- 混合回收的回收集(Collection Set)包括八分之一的老年代内存分段Eden区内存分段,Surivor区分段。
- 混合回收的算法那和年轻代回收的算法完全一样只是回收多了老年代內存分段
- 由于老年代中内存分段默认分8次回收,G1会优先回收占内存分段比例超过65%的区域
- 混合回收不一定要进行8次有一个阈值默认是10%
- 低于這个阈值就不会被回收
G1的初衷就是为了避免Full GC的出现。G1会停止应用程序执行(STW)
- 要避免Full GC的发生一旦发生需要进行调整,什么事发生Full GC 如:堆內存太小
- 并发处理过程之前空间耗尽
- 避免使用-Xmn 或者-XX:NewRatio等相关选项显示的社会组年轻代大小
- 规定年轻代的大小会覆盖暂停时间目标
- 暂停事案件目标不要太严苛
- G1 GC的吞吐量目标是90%的应用程序时间和10%的回收时间
- 评估G1 GC的吞吐量是,暂停时间目标不要太严苛目标太过严苛,表示你愿意承担个多的垃圾回收开销而这些会直接影响到吞吐量
- 优先调整堆代销让JVM自适应完成。
- 如果内存小于100m使用串行收集器
- 若是单核、单机程序,没有停顿时间的要求串行收集器
- 如果是多CPU、需要高吞吐、允许停顿时间超过1秒,选择并行(Parallel)或者JVM自适应
- 如果各多CPU追求更低地炖,需偠快速响应(延迟不超过一秒如互联内网应用),使用并发收集器(CMS G1)
- 官网推介G1性能高。现在互联网的项目基本都是使用G1