垃圾回收调优

目录

GC基础

前提认知:

GC算法认知

吞吐量优先

 响应时间优先 (CMSgc 优先)

CMS执行过程

 G1

适用场景

相关 JVM 参数

G1 垃圾回收阶段

几个gc里的Full GC 

G1里的Young Collection 跨代引用

什么是卡表技术

 Remark  重新标记阶段

JDK 8u20 字符串去重

JDK 8u40 并发标记类卸载

JDK 8u60 回收巨型对象

JDK 9 并发标记起始时间的调整

GC调优

首先确定目标

最快的 GC

新生代调优

老年代调优

案例

GC关系以及GC适用地方

参考博客


GC基础

前提认知:

首先说明一点  一般来说Gc执行时,会暂停用户线程,也就是并行gc,如果采用并发gc,则用户线程与gc线程并发执行,也是就可以同时执行

GC算法认知

GC算法分类_m0_71149992的博客-CSDN博客在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的如果存在 循环引用 最终导致无法回收是指 A对象里引用了B对象 ,而B对象也引用了A对象 导致 这2个对象各个计数器值 都为 1,最终导致无法回收对象简单来说 可达性分析算法 首先确定根对象 即肯定不能被当成垃圾被回收的对象(如 Thread 等 其实也就是 java核心 api里的对象),在进行垃圾回收之前,会对堆内存中对象进行扫描,看看这些对象 被根对象是https://blog.csdn.net/m0_71149992/article/details/126287356

吞吐量优先

并行gc 的执行过程

一些vm参数

-XX:+UseParallelGC ~ -XX:+UseParallelOldGC  :  采用并行垃圾回收器  前面是在新生代使用的gc(采用复制算法), 后面是老年代使用的gc(采用标记整理算法)

-XX:+UseAdaptiveSizePolicy :  自适应调整大小策略 主要调整新生代的内存 大小,动态调整伊甸园区和幸存者区域的大小 以及 对象晋升到老年代的寿命(寿命是指:如果没有被垃圾回收掉则标记加1,默认是标记达到15 晋升到老年代)

-XX:GCTimeRatio=ratio : 主要调整吞吐量优先  是指 单位时间内回收垃圾的量。如果 GC 的吞吐量下降,总的暂停时间就会变长。即就是在程序运行期间,垃圾回收的时间占程序运行时间的大小 
计算公式: 一般这个ratio 设置为19
加上这个vm参数 则垃圾回收在整个程序运行时间内的比例小于这个设置参数的比例,则 ParallelGC将堆内存调整大小,来达到这个比例的目标 从而加大吞吐量

-XX:MaxGCPauseMillis=ms :最大暂停毫秒数 默认值为200ms 也就是垃圾回收时间 ,如果将垃圾回收时间变小(用户线程暂停时间变小),则会将堆内存空间变小, 但是回收时间变大,则会增加吞吐量
一般不推荐 与  -XX:GCTimeRatio=ratio 参数共同使用  因为  -XX:GCTimeRatio=ratio  要到达这个吞吐量的比例,则会加大这个堆内存,会导致这个垃圾回收时间变大 ,反之 如果 设置暂停时间变小。则会降低这个堆内存大小,从而达到 这个时间

-XX:ParallelGCThreads=n   设置并行gc线程数量

 响应时间优先 (CMSgc 优先)

CMS执行过程

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld   : 前面一个基于标记清除算法的并发gc  ,用于老年代的 ,与之配套的是 UseParNewGC 用于新生代。如果CMS 这个gc 可能并发失败问题,这个时候会采取一个补救措施,会从并发gc 退到单线程的一个gc  SerialOld (这个 SerialOld  gc 是基于标记整理算法用在老年代 的gc)

-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads : 前面一个设置并行gc线程数量 加上后面一个vm参数则为 并发线程数量 ,一般设置为并行线程的1/4

-XX:CMSInitiatingOccupancyFraction=percent :  执行CMS内存的占比,也就是老年代内存到达某个占比,就会进行 CMS gc  ,这样就可以 预留空间给那些浮动垃圾是指  在CMS Gc 并发清理时,其他用户线程产生的垃圾 ,只能等待一次清理,称为 浮动垃圾)  其默认值是为 65%

-XX:+CMSScavengeBeforeRemark :  在CMS gc 重新标记之前 ,对新生代做一个垃圾回收,为什么会有这个参数 主要是前面一次用户线程与 CMS gc并发执行,此时 用户线程 可能出现一些垃圾,而在重新标记阶段,会从新扫描 新生代 、老年代区域内的垃圾,并从新标记,到最后清除垃圾对象,但这个操作会消耗很大的资源,所以会有这个VM参数,在重新标记阶段前,重新去新生代进行一个gc 回收

CMS 缺点 : 假设在一般情况下 ,是有4核cpu在执行用户程序,但是此时如果CMS gc进行执行 会占用一个cpu,导致程序只有使用3个cpu来计算,此时会导致 整个程序吞吐量受到一些影响
因为 CMS gc 是基于 标记清除算法 ,会产生内存碎片,如果内存碎片过多,导致对象无法分配 同时也会导致 CMS gc 并发执行失败,会从并发 gc 回到到 单线程 gc  SerialOld( 基于标记整理算法用在老年代 的gc 。等其在处理完成之后 ,重新回到 CMS gc 上来

 G1

延迟可控的情况下获得尽可能高的吞吐量

适用场景

同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
超大堆内存,会将堆划分为多个大小相等的 Region
整体上是 标记+整理 算法,两个 Region( 区域 之间是 复制 算法
也是一个并发gc  其实是为了替代 CMS GC

相关 JVM 参数

-XX:+UseG1GC  使用G1    在jdk 9 以后不需要设置这个VM参数 在 9以前需要手动添加这个vm参数
-XX:G1HeapRegionSize=size : 堆内存 Region 大小  一般来说 会设置 为  1  、2、  4、 8、 16 中一个  如果设置过大 会对回收垃圾过慢
-XX:MaxGCPauseMillis=time :  最大暂停毫秒数 默认值为200ms 也就是垃圾回收时间 , 收时间变大,则会增加吞吐量
-XX:InitiatingHeapOccupancyPercent=percent 默认45%     老年代占用堆空间比例

G1 垃圾回收阶段

《jvm 虚拟机》认为4个阶段

初始标记 (Initial Marking): 仅仅只是标记一下GC Roots能直接关联到的对象(其实也就是 标记存活对象 ),并且修改TAMS ( Top At Marking Start”  标记开始时的top  的缩写 )指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。 这个阶段需要停顿线程,但耗时很短,而且是 借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿

并发标记 Concurrent Marking ): 从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆 里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行 当对象图扫描完成以 后,还要重新处理SATB 记录下的在并发时有引用变动的对象

最终标记 (Final Marking): 对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留
下来的最后那少量的SATB记录

筛选回收 Live Data Counting and Evacuation ):负 责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间 。这里的操作涉及存活对象的移动,是 必须暂停用户线程,由多条收集器线程并行完成的

执行过程:

Oracle 工程师认为 三个阶段

E  伊甸园区

S 幸存者区

O 老年代


Young Collection

当伊甸园空间不足时,则会触发 新生代垃圾回收 ,当然了 也会触发 Stop The World  即暂停用户线程,然后将存活下来的 对象 已复制算法放入到幸存者区如果在工作一段时间 后,幸存者区  里某些对象 的寿命(年龄)达到某一个预定值(是指如果没有被垃圾回收掉则标记加1,默认是标记达到15 晋升到老年代) ,晋升到老年代, 其余未被回收的对象 将会放到 另一个幸存者区
这个阶段耗时很短

Young Collection + CM
在 Young GC 时会进行 GC Root 的初始标记 这个初始化标记时间不占用并发标记的时间
老年代占用堆空间比例达到阈值时,进行并发标记 (不会 Stop The World  即暂停用户线程 ),由下面的 JVM 参数决定
-XX:InitiatingHeapOccupancyPercent=percent 默认45%     老年代占用堆空间比例


Mixed Collection  混合回收
会对 伊甸园 、幸存者区 、老年代 进行全面垃圾回收
最终标记(Remark) Stop The World  。  为何暂停用户线程 因为 在前面并发标记时候 用户线程也是并发执行,可能用户线程里面某些操作改变一些对象的引用或者产生新的垃圾,这些操作都会对并发标记产生影响,所以在最终标记 时 会 Stop The World  ,  然后进行拷贝存活对象
拷贝存活(Evacuation)   会  Stop The World  。  这里对于老年代回收,并不是拷贝全部老年代里的对象,而是 回收一些价值最大的区域即垃圾最多的老年代区
-XX:MaxGCPauseMillis=ms   :  最大暂停毫秒数 默认值为200ms 也就是垃圾回收时间
                                  
新生代发生的事件:
当伊甸园空间和幸存者空间不足时,则会触发 新生代垃圾回收 ,当然了 也会触发 Stop The World  即暂停用户线程,然后将存活下来的 对象 已复制算法放入到新的幸存者区,如果旧的 幸存者区里某些对象 的寿命(年龄)达到某一个预定值(是指如果没有被垃圾回收掉则标记加1,默认是标记达到15 晋升到老年代) ,晋升到老年代, 其余未被回收的对象 将会放到 新的幸存者区
老年代发生的事件:
 老年代之前经过并发标记阶段,发现某些对象没用了,将那些还存活下来的对象 ,通过复制算法 复制到新的一个老年代区当然 g1也有选择性回收对象 ,他是 根据最大暂停时间(-XX:MaxGCPauseMillis)以及回收一些价值最大的区域进行回收,因为回收这些对象可以释放大量内存,同时因为这样操作可以使复制算法减少复制对象 ,从而达到设置的 最大暂停毫秒数。
如果复制区域可以达到设置最大暂停毫秒数,将所有的区域都进行复制
所以这个混合回收目的 就是回收一些垃圾最多的区域 ,已到达设置的最大暂停毫秒数

几个gc里的Full GC 

SerialGC

新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc

ParallelGC
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足发生的垃圾收集 - full gc

CMS
新生代内存不足发生的垃圾收集 - minor gc
老年代内存不足
如果 CMS  回收垃圾的速度高于用户线程产生的垃圾的速度,这个时候还不是处于 full gc,如果用户线程产生的垃圾的速度高于 CMS  回收的速度 ,则此时并发收集失败(原因 是C MS gc 是基于 标记清除算法 ,会产生内存碎片,如果内存碎片过多,导致对象无法分配 同时也会导致 CMS gc 并发执行失败,会从并发 gc 回到到 单线程 gc  SerialOld( 基于标记整理算法用在老年代 的gc)
此时才称为 full gc

G1
新生代内存不足发生的垃圾收集 - minor gc 
老年代内存不足
-XX:InitiatingHeapOccupancyPercent=percent 默认45%     老年代占用堆空间比例
假设 老年代与堆内存比达到某个阈值时 以上时,则触发并发标记阶段和混合收集阶段,这2个阶段工作过程中回收垃圾的速度高于用户线程产生的垃圾的速度,这个时候还不是处于 full gc,是处于并发回收垃圾阶段,虽然也有 Stop The World  即暂停用户线程,但这并不是 full gc,如果用户线程产生的垃圾的速度高于 g1 回收的速度 ,则此时并发收集失败,这时候退化为串行数据,此时才为 full gc,当然也会产生Stop The World  导致响应速度变慢

G1里的Young Collection 跨代引用

G1和CMS都使用卡表来处理跨代指针

新生代回收的跨代引用(老年代引用新生代)问题

如果要回收新生代对象,则需要查询新生代对象的根对象,有一部分 根对象是在老年代,如果遍历老年代里的对象进行查找,这个效率极低因此采用一种卡表技术,在将老年代进行细分 分成一个个卡 ,每个卡大小为 512字节 ,如果老年代引用了新生代的对象,则对应的卡标记为 “脏卡”,这样到时候遍历这个老年代对象就不需要遍历全部了,而是关注那些“脏卡”区域对象,这样减少搜索范围,提高了效率

什么是卡表技术

G1 gc 算法与实现中写到 :

 深入jvm 中写道

 

 Remark  重新标记阶段

在标记阶段时,对象a 与对象b 已被gc 处理过,标记为存活对象,但是某个对象c不在被对象b引用 ,此时该对象c被标记成垃圾对象,但是 标记阶段未结束时,与 gc 线程 并发执行的用户线程 ,在这些线程里面的某些操作 ,又将将这个对象c被对象a引用用,这时候需要对这个对象c引用进一步检测所以为了防止出现这个情况 ,它是这么做的 如果这个对象c引用发生改变时,jvm 就会加入一个写屏障的指令写屏障可以看作在虚拟机层面对“引用类型字段赋值这个动作的AOP切 面在引用对象赋值时会产生一个环形通知,供程序执行额外的动作,也就是说赋值的 前后都在写屏障的覆盖范内),并将这个对象c放入到一个队列中,并标记为未处理完成对象,等到并发标记结束后,再次进入重新标记阶段,将队列里对象c拿出来在进行检测,如果没有对象去引用对象c,则会被回收,如果还有引用这个对象c ,则把这个对象标为存活对象

三色标记:
· 白色: 表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是
白色的,若在分析结束的阶段,仍然是白色的对象,即代表不可达。
· 黑色: 表示对象已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描过。黑色的对象代
表已经扫描过,它是安全存活的,如果有其他对象引用指向了黑色对象,无须重新扫描一遍。黑色对 象不可能直接(不经过灰色对象)指向某个白色对象。
· 灰色: 表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个引用还没有被扫描过。

 

JDK 8u20 字符串去重

优点:节省大量内存
缺点:略微多占用了 cpu 时间,新生代回收时间略微增加
开启vm 参数
-XX:+UseStringDeduplication
String s1 = new String ( "hello" ); // char[]{'h','e','l','l','o'}
String s2 = new String ( "hello" ); // char[]{'h','e','l','l','o'}
执行过程:
将所有新分配的字符串放入一个队列
当新生代回收时,G1并发检查是否有字符串重复
如果它们值一样,让它们引用同一个 char[]
注意,与 String.intern() 不一样
        String.intern() 关注的是字符串对象
        而字符串去重关注的是 char[]
        在 JVM 内部,使用了不同的字符串表

JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸 载它所加载的所有类
- XX:+ClassUnloadingWithConcurrentMark 默认启用

JDK 8u60 回收巨型对象

一个对象大于 region( region : g1  会将堆划分为多个大小相等的 Region ) 的一半时,称之为巨型对象 

G1 不会对巨型对象进行拷贝

回收时被优先考虑

G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生
代垃圾回收时处理掉  ( 刚才在G1里的Young Collection 跨代引用 里写到  老年代引用 新生代对象 ,会采用卡表的 技术,则对应卡标为“脏卡”,所以g1 会根据这个脏卡的引用 是否还存在之前的新生代对象的引用 即这个引用为0 ,将会被 新生 代垃圾回收时处理掉 )

JDK 9 并发标记起始时间的调整

并发标记必须在堆空间占满前完成,否则退化为 FullGC
JDK 9 之前需要使用 - XX:InitiatingHeapOccupancyPercent
JDK 9 可以动态调整
                -XX:InitiatingHeapOccupancyPercent 用来设置初始值
                进行数据采样并动态调整
                总会添加一个安全的空档空间

GC调优

首先确定目标

【低延迟】还是【高吞吐量】,选择合适的回收器

低延迟:CMS G1 ZGC

高吞吐量 :ParallelGC

虚拟机: Zing

g1 集成了 cms 和 ParallelGC 长处

最快的 GC

答案是不发生 GC

查看 FullGC 前后的内存占用,考虑下面几个问题
    数据是不是太多?
                                resultSet = statement.executeQuery("select * from 大表 limit n")

数据表示是否太臃肿?
对象图
对象大小 16 Integer 16 int 4

是否存在内存泄漏?
static Map map =
建议使用第三方缓存实现

新生代调优

新生代的特点
  • 所有的 new 操作的内存分配非常廉价
TLAB :thread-local allocation buffffer
TLAB 作用: 每个线程都会在伊甸园中分配一个私有区域,如果创建一个对象 首先去TLAB缓冲区 中查看是否有空间去对象分配,如果有则优先在这个地方分配 为什么这么做 原因 对象分配也是有线程安全问题,如果一个线程1这个地方使用进而分配对象,但是分配还没结束 ,此时线程二也要这块空间进行分配对象,造成分配空间混乱 因此在内存分配时需要增加线程保护,这个操作是jvm操作的 TLAB 可以 减少内存分配并发冲突问题,让每个线程在自己的私有空间内进行分配对象

  • 死亡对象的回收代价是零
大部分gc 采用复制算法 ,将伊甸园和幸存者from区 存活对象已复制算法 复制到 幸存者to区 ,并将伊甸园和幸存者from区 内存释放,同时 将幸存者from区 与 幸存者to区 对调 所以 回收代价为零
  • 大部分对象用过即死
  • Minor GC 的时间远远低于 Full GC

  • 堆内存是否设置越大越好吗?

                -Xmn  设置新生代大小

看下 Oracle官方 建议

设置年轻一代(托儿所)的堆的初始大小和最大大小(以字节为单位)。GC在这个区域执行的频率比在其他区域更高。如果年轻一代的规模太小,那么就会执行大量的小型垃圾收集。如果大小太大,则只执行完整的垃圾收集,这可能需要很长时间才能完成Oracle建议您将年轻一代的大小保持在大于总体堆大小的25%和小于50%以下

  • 吞吐量与新生代空间大小曲线图 

一般来说新生代空间越大,吞吐量也变得更大, 但是 新生代空间大小到达某个顶点值时,吞吐量下降,因为 空间大了 ,暂停时间也变得更长了,其中新生代采用复制算法 复制对象,其中有2个阶段 

  1. 标记阶段
  2. 复制阶段

其中复制阶段 的时间更长,牵扯到内存块占用的移动以及更新对象引用地址

新生代里的对象大部分是朝生夕死的 ,只有少量的对象存活,所以少量存活的对象复制占用的时间还是比较短的,所以标记阶段的消耗的时间显得不是这么重要了

如何设置这个新生代空间大小

  • 新生代能容纳所有【并发量 * (请求-响应)】的数据
  • 幸存区大到能保留【当前活跃对象+需要晋升对象】
  • 晋升阈值配置得当,让长时间存活对象尽快晋升
  • -XX:MaxTenuringThreshold=threshold  对象晋升阈值 即新生代对象升级到老年代的年龄
  • -XX:+PrintTenuringDistribution   打印对象晋升详细信息

老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好
  • 先尝试不做调优,如果没有 Full GC 那么已经...,否则先尝试调优新生代
  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/​​​3

-XX:CMSInitiatingOccupancyFraction=percent    垃圾对象占了老年代多少比例 开始使用CMS gc 回收

案例

  1. 案例1 Full GC 和 Minor GC频繁

gc 频繁 说明 空间紧张如果幸存者空间紧张 ,则会降低晋升阈值,导致一些寿命很短的对象,晋升到老年代,进而触发 full gc ,进而 查看新生代内存大小,增加新生代和幸存者 空间,以及设置一下晋升阈值,进而将一些寿命比较的对象留着 新生代

  1. 案例2 请求高峰期发生 Full GC,单次暂停时间特别长 (CMS)

可以查看日志 ,看看哪个阶段消耗时间比较长,一般来说 CMS gc 重新标记阶段耗时比较长因为重新标记阶段既要标记新生代又要标记老年代所以消耗时间比较长,所以添加一个VM 参数   -XX:+CMSScavengeBeforeRemark  在CMS gc 重新标记之前 ,对新生代做一个垃圾回收,进而减少重新标记阶段耗时间

  1. 案例3 老年代充裕情况下,发生 Full GC (CMS jdk1.7)

一般来说 是在老年代内存不足或者碎片过多 从而导致full gc,通过日志排查没有发现并发失败和碎片过多的提示,说明老年代空间充裕的,所以可能项目运行的jdk版本较低在1.7以前是用永久代实现方法区,而1.8以后采用本地内存的元空间实现方法区,说明  1.7以前的永久代空间不足,也会导致 full gc,1.8以后 元空间的内存 是采用本地内存,不是由java控制的,默认采用操作系统内存,所以不会出现元空间空间不足所以加大永久代空间

GC关系以及GC适用地方

新生代收集器:Serial、ParNew、Parallel Scavenge;

老年代收集器:Serial old、Parallel old、CMS;

整堆收集器:G1;


Java堆内存分为新生代和老年代: 新生代主要存储短生命周期的对 象,适合使用复制算法进行垃圾回收 老年代主要存储长生命周期的对 象和大对象,适合使用标记整理算法进行垃圾回收 。因此,JVM针对新生代和老年代 分别提供了多种不同的垃圾收集器,
针对新生代提供的垃圾收集器有 :Serial、ParNew、Parallel Scavenge,
针对老年代提供的垃圾收集器有 :Serial Old、Parallel Old、CMS,还有针对不同区域的G1分区收集算法,


  • Serial垃圾收集器:单线程,复制算法
Serial垃圾收集器基于复制算法实现,它是一个单线程收集器,在它 正在进行垃圾收集时,必须暂停其他所有工作线程,直到垃圾收集结束。
Serial垃圾收集器采用了复制算法,简单、高效,对于单CPU运行环境 来说,没有线程交互开销,可以获得最高的单线程垃圾收集效率,因此 Serial垃圾收集器是Java虚拟机运行在Client模式下的新生代的默认垃圾 收集器


  • ParNew垃圾收集器:多线程,复制算法
ParNew垃圾收集器是Serial垃圾收集器的多线程实现,同样采用了复 制算法,它采用多线程模式工作,除此之外和Serial收集器几乎一样。 ParNew垃圾收集器在垃圾收集过程中会暂停所有其他工作线程, 是Java虚 拟机运行在Server模式下的新生代的默认垃圾收集器。
ParNew垃圾收集器默认开启与CPU同等数量的线程进行垃圾回收 ,在Java应用启动时可通过 -XX:ParallelGCThreads 参数调节ParNew垃圾收集器的工作线程数。


  • Parallel Scavenge垃圾收集器:多线程,复制算法
Parallel Scavenge收集器是为提高新生代垃圾收集效率而设计的垃圾收集器,基于多线程复制算法实现,在系统吞吐量上有很大的优化,可以 更高效地利用CPU尽快完成垃圾回收任务。
Parallel Scavenge通过自适应调节策略提高系统吞吐量,提供了三个 参数用于调节、控制垃圾回收的停顿时间及吞吐量,分别是
控制最大垃圾 收 集 停 顿 时 间 的: -XX:MaxGCPauseMillis 参 数
控 制 吞 吐 量 大 小 的: - XX:GCTimeRatio 参 数 
控 制 自 适 应 调 节 策 略 开 启 与 否 的 :-UseAdaptiveSizePolicy参数。

  •  Serial Old垃圾收集器:单线程,标记整理算法
Serial Old垃圾收集器是Serial垃圾收集器的老年代实现,同Serial 一样采用单线程执行,不同的是,Serial Old针对老年代长生命周期的特点基于标记整理算法实现。 Serial Old垃圾收集器是JVM运行在Client模式 下的老年代的默认垃圾收集器。
新生代的Serial垃圾收集器和老年代的Serial Old垃圾收集器可搭配 使用,分别针对JVM的新生代和老年代进行垃圾回收,其垃圾收集过程
如图所示。 在新生代采用Serial垃圾收集器基于复制算法进行垃圾回收,
未被其回收的对象在老年代被Serial Old垃圾收集器基于标记整理算法进 行垃圾回收


  • Parallel Old垃圾收集器:多线程,标记整理算法
Parallel Old垃圾收集器采用多线程并发进行垃圾回收,它根据老年代长生命周期的特点,基于多线程的标记整理算法实现。Parallel Old垃 圾收集器在设计上优先考虑系统吞吐量,其次考虑停顿时间等因素,
如果系统对吞吐量的要求较高,则可以优先考虑新生代的Parallel Scavenge垃圾收集器和老年代的Parallel Old垃圾收集器的配合使用。
新生代的Parallel Scavenge垃圾收集器和老年代的Parallel Old垃圾收集器的搭配运行过程如图 1-16所示。新 生代基于Parallel Scavenge垃 圾收集器的复制算法进行垃圾回收,老年代基于Parallel Old垃圾收集器 的标记整理算法进行垃圾回收


  • CMS垃圾收集器
CMS(Concurrent Mark Sweep)垃圾收集器是为老年代设计的垃圾收 集器,其主要目的是达到最短的垃圾回收停顿时间,基于线程的标记清除算法实现,以便在多线程并发环境下以最短的垃圾收集停顿时间提高系统的稳定性。
CMS的工作机制相对复杂,垃圾回收过程包含如下4个步骤
(1)初始标记:只标记和GC Roots直接关联的对象,速度很快,需要
暂停所有工作线程。
(2)并发标记:和用户线程一起工作,执行GC Roots跟踪标记过程,
不需要暂停工作线程。
(3)重新标记:在并发标记过程中用户线程继续运行,导致在垃圾回
收过程中部分对象的状态发生变化,为了确保这部分对象的状态正确性,
需要对其重新标记并暂停工作线程。
(4)并发清除:和用户线程一起工作,执行清除GC Roots不可达对象
的任务,不需要暂停工作线程。
CMS垃圾收集器在和用户线程一起工作时( 并发标记和并发清除)不需 要暂停用户线程,有效缩短了垃圾回收时系统的停顿时间 ,同时由于CMS垃 圾收集器和用户线程一起工作,因此其并行度和效率也有很大提升。CMS收集器的工作流程如图所示


  • G1垃圾收集器
G1(Garbage First)垃圾收集器为了避免全区域垃圾收集引起的系统 停顿,将堆内存划分为大小固定的几个独立区域,独立使用这些区域的内 存资源并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,在垃圾回收过程中根据系统允许的最长垃圾收集时间,优先回收垃圾
最多的区域。
G1垃圾收集器通过内存区域独立划分使用和根据不同优先级 回收各区域垃圾的机制,确保了G1垃圾收集器在有限时间内获得最高的垃圾收集效率 。相对于CMS收集器,G1垃圾收集器两个突出的改进。
◎ 基于标记整理算法,不产生内存碎片。
◎ 可以精确地控制停顿时间,在不牺牲吞吐量的前提下实现短停顿垃
圾回收。

 

参考博客

蘑菇社区 - 专注于技术分享的社区平台

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值