垃圾收集器

server/2024/9/24 17:12:40/

文章目录

    • 概要
    • 几个重要的概念
    • Serial收集器
    • ParNew 收集器
    • Parallel Scavenge收集器
    • Serial Old收集器
    • Parallel Old收集器
    • CMS收集器
      • CMS垃圾收集过程
      • 并发标记过程
      • 多标
      • 漏标
      • CMS收集器的三个缺点
    • G1收集器
      • 垃圾回收细节
      • 参数设置
      • 运行过程
      • 优缺点

概要

垃圾回收算法分类两类,第一类算法判断对象生死算法,如引用计数法、可达性分析算法 ;第二类收集死亡对象方法有四种,如标记-清除算法、标记-复制算法、标记-整理算法。一般的实现采用分代回收算法,根据不同代的特点应用不同的算法。垃圾回收算法是内存回收的方法论。垃圾收集器是算法的落地实现。和回收算法一样,目前还没有出现完美的收集器,而是要根据具体的应用场景选择最合适的收集器,进行分代收集。

常见的垃圾收集器如下:
在这里插入图片描述

JDK8中默认使用组合是: Parallel Scavenge GCParallelOld GC
JDK9默认是用G1垃圾收集器
JDK14 弃用了: Parallel Scavenge GC 、Parallel OldGC
JDK14 移除了 CMS GC

官方文档:官方文档
Java HotSpot VM 包括三种不同类型的收集器,每种收集器具有不同的性能特征:三种不同类型收集器

  • 串行收集器( SerialGC ):使用单个线程来执行所有垃圾收集工作,这使得它相对高效,因为线程之间没有通信开销。它最适合单处理器机器,因为它不能利用多处理器硬件。
  • 并行收集器(也称为吞吐量收集器, ParallelGC ):并行执行 MinorGc ,可以显着减少垃圾收集开销,它适用于在多处理器或多线程硬件上运行的具有中型到大型数据集的应用程序(有多个 GC 线程)。
  • 并发收集器( CMS , G1:大多数并发收集器同时执行其大部分工作(例如,当应用程序仍在运行时)以保持垃圾收集暂停较短。它专为具有中型到大型数据集的应用程序而设计,其中响应时间比总吞吐量更重要。

几个重要的概念

  • 用户/工作线程:java程序运行后,用户不停请求操作jvm内存,这些称为用户线程
  • GC线程:jvm系统进行垃圾回收启动的线程
  • 串行:GC采用单线程,收集时停掉用户线程
  • 并行:GC采用多线程,收集时同样要停掉用户线程
  • 并发:用户线程和GC线程同步进行
  • STW:Stop一the一World ,简称 STW ,指的是 GC 事件发生过程中,会暂停所有的工作线程,产生应用程序的停顿,没有任何响应,有点像卡死的感觉,这个停顿称为 STW
    • 被 STW 中断的应用程序线程会在完成 GC 之后恢复,要减少STW发送频率
    • 几乎所有的 GC 都有这个事件。哪怕是 G1也不能完全避免 Stop一the一world 情况发生;垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间
    • STW 是 JVM 在后台自动发起和自动完成的,用户不可控。

几个衡量GC性能的指标:

  • 吞吐量:即CPU用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量 = 运行用户代码时间 / ( 运行用户代码时间 + 垃圾收集时间 ))。例如:虚拟机共运行100分钟,垃圾收集器花掉1分钟,那么吞吐量就是99%
  • 暂停时间:执行垃圾回收时,程序的工作线程被暂停的时间
  • 内存占用:java堆所占内存的大小
  • 收集频率:垃圾收集的频次

Serial收集器

JVM 刚诞生就只有这种,比较古老,串行的,独占式,成熟,适合单CPU。

比较适用于小数据集(约100M)的应用程序在多处理器上适用,超过这个大小的内存回收速度很慢,所以对于现在来说这个垃圾回收器基本上不使用了

要启动可以通过参数: -XX:+UseSerialGC

新生代采用的是复制算法, 老年代采用的是标记-整理算法。

在这里插入图片描述

ParNew 收集器

ParNew收集器实质上是Serial收集器的多线程并行版本, 除了同时使用多条线程进行垃圾收集之 外, 其余的行为包括Serial收集器可用的所有控制参数 、 收集算法、 Stop The World、 对象分配规则、 回收策略等都与Serial收集器完全一致, 在实现上这两种收集器也共用了相当多的代码

在这里插入图片描述

ParNew收集器在单CPU服务器上的垃圾收集效率绝对不会比Serial收集器高;但是在多CPU服务器上,效果会明显比Serial好
使用方式:-XX:+UseParNewGC
设置线程数: XX:ParllGCThreads

Parallel Scavenge收集器

又称为吞吐量优先收集器,和ParNew收集器类似,是一个新生代收集器。使用复制算法的并行多线程收集器。Parallel Scavenge是Java1.8默认的收集器,特点是并行的多线程回收,以吞吐量优先。

特点:

  • 该收集器的目标是达到一个可控制的吞吐量(Throughput);
  • 自适应调节策略,自动指定年轻代、Eden、Suvisor区的比例。

适用场景:
适合后台运算,交互不多的任务,如批量处理,订单处理,科学计算等

参数:

  • 使用方式:-XX:+UseParallelGC

  • 控制最大垃圾收集停顿时间-XX:MaxGCPauseMillis
    该参数允许的值是一个大于0的毫秒数, 收集器将尽力保证内存回收花费的时间不超过用户设定值。
    不过垃圾收集停顿时间缩短是以牺牲吞吐量和新生代空间为代价换取的:系统把新生代调得小一些, 收集300MB新生代肯定比收集500MB快, 但这也直接导致垃圾收集发生得更频繁, 原来10秒收集一次、 每次停顿100毫秒, 现在变成5秒收集一次、 每次停顿70毫秒。 停顿时间的确在下降, 但吞吐量也降下来了。

  • 吞吐量大小-XX:GCTimeRatio: 该参数的值则应当是一个大于0小于100的整数, 也就是垃圾收集时间占总时间的
    比率, 相当于吞吐量的倒数。 假设GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的时间用于垃圾收集。譬如把此参数设置为19, 那允许的最大垃圾收集时间就占总时间的5%(即1/(1+19)) , 默认值为99, 即允许最大1%(即1/(1+99)) 的垃圾收集时间

  • 设置年轻代线程数 XX:ParllGCThreads: 当cpu合数小于等于8,默认cpu核数相同; 当cpu核数超过8, ParllGCThreads设置为 3+(5*CPU_COUNT)/8

  • -XX:+UseAdaptiveSizePolicy: 有了这个参数之后,就不要手工指定年轻代、Eden、Suvisor区的比例,晋升老年代的对象年龄等,因为虚拟机会根据系统运行情况进行自适应调节

Serial Old收集器

Serial Old是Serial收集器的老年代版本, 它同样是一个单线程收集器, 使用标记-整理算法。 这个收集器的主要意义也是供客户端模式下的HotSpot虚拟机使用

特点:

  • 针对老年代;
  • 采用"标记-整理"算法;
  • 单线程收集;

执行流程:
在这里插入图片描述

应用场景: 主要用于Client模式

  • 在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配);
  • 作为 CMS收集器的后备预案 ,在并发收集发生Concurrent Mode Failure时使用

参数设置:

使用方式:-XX:+UseSerialGC

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本, 支持多线程并行收集, 基于标记-整理算法实现。 这个收集器是直到JDK 6时才开始提供的。在此之前, 新生代的Parallel Scavenge收集器一直处于相 当尴尬的状态, 原因是如果新生代选择了Parallel Scavenge收集器, 老年代除了Serial Old(PS MarkSweep) 收集器以外别无选择, 其他表现良好的老年代收集器, 如CMS无法与它配合工作。

工作过程:

在这里插入图片描述
应用场景:
注重吞吐量以及CPU资源敏感的场景,Parallel ScavengeParallel Old组合

设置参数:
-XX:+UseParallelOldGC:指定使用Parallel Old收集器

CMS_115">CMS收集器

CMS(concurrent mark sweep)是以获取最短垃圾收集停顿时间为目标的收集器,CMS收集器的关注点尽可能缩短垃圾收集时用户线程的停顿时间,停顿时间越短就越适合与用户交互的程序,目前很大一部分的java应用几种在互联网的B/S系统服务器上,这类应用尤其注重服务器的响应速度,系统停顿时间最短,给用户带来良好的体验,CMS收集器使用的算法是标记-清除算法实现的

CMS_119">CMS垃圾收集过程

整个过程分为四个阶段:

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清除

其中 初始标记 和 重新标记 都需要stopTheWorld

在这里插入图片描述

  • 初始标记(Initial-Mark):这个阶段程序所有的工作线程都将会因为"Stop-the-Wold"机制而出现短暂的的暂停,这个阶段的主要任务标记处GC Roots 能够关联到的对象.一旦标记完成后就恢复之前被暂停的的所有应用。 由于直接关联对象比较小,所以这里的操作速度非常快
  • 并发标记(Concurrent-Mark):从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要暂停用户线程, 用户线程可以与垃圾回收器一起运行。
  • 重新标记(Remark):由于并发标记阶段,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此,为了修正并发标记期间因为用户继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常比初始标记阶段长一些,但也远比并发标记阶段时间短。
  • 清除并发(Concurrent-Sweep):此阶段清理删除掉标记判断已经死亡的对象,并释放内存空间。由于不需要移动存活对象,所以这个阶段可以与用户线程同时并发运行。

由于最消耗事件的并发标记与并发清除阶段都不需要暂停工作,因为整个回收阶段是低停顿(低延迟)的

并发标记过程

到目前为止,所有收集器在根节点枚举遍历其直接关联的对象时是要 STW的,并发收集器在继续往下进行可达性标记时是允许用户线程并发执行的,这样有效的减少了整体 STW 时间, 那这个并发标记到底是如何工作的呢?这就是我们要说的三色标记。

三色标记

要找出存活对象,根据可达性分析,从GC Roots开始进行遍历访问,可达的则为存活对象。

在这里插入图片描述
三色标记(Tri-color Marking)作为工具来辅助推导, 把遍历对象过程中遇到的对象, 按照“是否访问过”记成以下三种颜色:

  • 白色:尚未访问过。
  • 黑色:本对象已访问过,而且本对象 引用到 的其他对象 也全部访问过了。
  • 灰色:本对象已访问过,但是本对象 引用到 的其他对象尚未全部访问完。全部访问后,会转换为黑色。

在这里插入图片描述
假设现在有白、灰、黑三个集合(表示当前对象的颜色),其遍历访问过程为:

  1. 初始时,所有对象都在 【白色集合】中;
  2. 将GC Roots 直接引用到的对象 挪到 【灰色集合】中;
  3. 从灰色集合中获取对象:
    3.1. 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中;
    3.2. 将本对象 挪到 【黑色集合】里面。
  4. 重复步骤3,直至【灰色集合】为空时结束。
  5. 结束后,仍在【白色集合】的对象即为GC Roots 不可达,可以进行回收。

如果标记结束后对象仍为白色,意味着已经“找不到”该对象在哪了,不可能会再被重新引用。

当Stop The World (以下简称 STW)时,对象间的引用 是不会发生变化的,可以轻松完成标记。 而当需要支持并发标记时,即标记期间应用线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。

多标

假设已经遍历到E(变为灰色了),此时应用执行了 objD.fieldE = null

在这里插入图片描述
此刻之后,对象E/F/G是“应该”被回收的。然而因为E已经变为灰色了,其仍会被当作存活对象继续遍历下去。最终的结果是:这部分对象仍会被标记为存活,即本轮GC不会回收这部分内存。

这部分本应该回收 但是 没有回收到的内存,被称之为“浮动垃圾”。浮动垃圾并不会影响应用程序的正确性,只是需要等到下一轮垃圾回收中才被清除。

漏标

假设GC线程已经遍历到E(变为灰色了),此时应用线程先执行了:

objG G = objE.fieldG; // 赋值给零食变量
objE.fieldG = null; // 灰色E 断开引用 白色G
objD.fieldG = G; // 黑色D 引用 白色G

在这里插入图片描述
此时切回GC线程继续跑,因为E已经没有对G的引用了,所以不会将G放到灰色集合;尽管因为D重新引用了G,但因为D已经是黑色了,不会再重新做遍历处理。 最终导致的结果是:G会一直停留在白色集合中,最后被当作垃圾进行清除。这直接影响到了应用程序的正确性,是不可接受的。

不难分析,漏标只有同时满足以下两个条件时才会发生:

漏标的发生有两个条件:
1、一个或者多个黑色对象重新引用了白色对象;即黑色对象成员变量增加了新的引用
2、灰色对象断开了白色对象的直接或间接引用;即灰色对象原来成员变量的引用发生了变化。

读写屏障
针对于漏标问题,JVM 团队采用了读屏障与写屏障的方案。其目的很简单,就是在读写前后将 G 这类对象给记录下来。

读屏障

oop oop_field_load(oop* field) {// 读屏障-读取前操作pre_load_barrier(field);return *field;
}

当读取成员变量之前,先记录下来,这种做法是保守的,但也是安全的。因为条件1中【一个或者多个黑色对象重新引用了白色对象】,重新引用的前提是:得获取到该白色对象,此时已经读屏障就发挥作用了

写屏障:
所谓的写屏障,其实就是指给某个对象的成员变量赋值操作前后,加入一些处理(类似 Spring AOP 的概念)。

void oop_field_store(oop* field, oop new_value) {// 写屏障-写前操作pre_write_barrier(field);*field = new_value;// 写屏障-写后操作post_write_barrier(field, value);
}

不管是条件1还是条件2中,都有对一个灰色对象或者黑色对象的属性进行写操作。

解决漏标问题,只要破坏漏标的两个条件之一即可,不同收集器采用的方案也不一样:

增量更新(Incremental Update)

1、主要针对对象新增的引用,利用写屏障将其记录下来,这样破坏了条件1
2、后续重新扫描时还会继续从记录下来的新增引用深度扫描下去
CMS收集器采用的是这种方案

原始快照(Snapshot At The Beginning,SATB):

1、当某个对象断开其属性的引用时,利用写屏障,将断开之前的引用记录下来,
2、尝试保留开始时的对象引用图,即原始快照,当某个时刻的 GC Roots确定后,当时的对象引用图就已经确定了。
3、后续标记是按照开始时的快照走,比如 E > G ,即使期间发生变化,通过写屏障记录后,保证标记还是按照原本的视图来,
4、SATB破坏的是漏标条件2,主要针对是引用的减少。

G1收集器采用的是这种方案

另外,他们各自的总结如下:
1、原始快照相对增量更新来说效率更高,因为不需要在重新标记阶段再次深度扫描被删除引用的对象,当然原始快照可能造成更多的浮动垃圾。
2、而 CMS 对增量引用的根对象会做深度扫描,G1 因为很多对象都位于不同的 region,CMS 就一块老年代区域;重新深度扫描对象的话 G1 的代价会比CMS 高,所以 G1 选择原始快照不深度扫描对象,只是简单标记,等到下一轮GC 再深度扫描。

CMS_241">CMS收集器的三个缺点

CMS收集器对CPU资源非常敏感

在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(处理器核心数量 +3) /4,也就是说, 如果处理器核心数在四个或以上, 并发回收时垃圾收集线程只占用不超过25%的 处理器运算资源, 并且会随着处理器核心数量的增加而下降。 但是当处理器核心数量不足四个时, CMS对用户程序的影响就可能变得很大。 如果应用本来的处理器负载就很高, 还要分出一半的运算能 力去执行收集器线程, 就可能导致用户程序的执行速度忽然大幅降低

CMS收集器无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败而导致另一次Full GC的产生。

由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后(比如产生新的GC ROOTS),CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为"浮动垃圾"。同样也是由于在垃圾收集阶段用户线程还需要持续运 行, 那就还需要预留足够内存空间提供给用户线程使用, 因此CMS收集器不能像其他收集器那样等待 到老年代几乎完全被填满了再进行收集, 必须预留一部分空间供并发收集时的程序运作使用。

在JDK 5的默认设置下, CMS收集器当老年代使用了68%的空间后就会被激活, 这是一个偏保守的设置, 如果 在实
际应用中老年代增长并不是太快, 可以适当调高参数-XX:CMSInitiatingOccu-pancyFraction的值 来提高CMS的触发百分比, 降低内存回收频率, 获取更好的性能。 到了JDK 6时, CMS收集器的启动 阈值就已经默认提升至92%。但这又会更容易面临另一种风险: 要是CMS运行期间预留的内存无法满 足程序分配新对象的需要, 就会出现一次“并发失败”(Concurrent Mode Failure) , 这时候虚拟机将不 得不启动后备预案: 冻结用户线程的执行, 临时启用Serial Old收集器来重新进行老年代的垃圾收集, 但这样停顿时间就很长了。

空间碎片:CMS是一款基于标记-清除算法实现的收集器,所有会有空间碎片的现象。

当空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大
的连续空间来分配当前对象,不得不提前触发一次Full GC。
为了解决这个问题, CMS收集器提供了一个-XX:+UseCMS-CompactAtFullCollection开关参数(默认是开启的, 此参数从 JDK 9开始废弃) , 用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程, 由于这个 内存整理必须移动存活对象, 是无法并发的。 这样空间碎片问题是解决了, 但停顿时间又会变长, 因此虚拟机设计者们还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction(此参数从JDK 9开始废弃) , 这个参数的作用是要求CMS收集器在执行过若干次(数量 由参数值决定) 不整理空间的Full GC之后, 下一次进入Full GC前会先进行碎片整理(默认值为0, 表示每次进入Full GC时都进行碎片整理)。

G1_260">G1收集器

Garbage First是一款面向服务端应用的垃圾收集器,主要针对配备多核CPU及大容量内存的机器,以极高概率满足GC停顿时间的同时,还兼具高吞吐量的性能特征。

设计思想
作为CMS的替代者和继承人,设计者希望能够建立起具有:“停顿时间模型” 的收集器,通过该模型的意思是:要达到在指定时间为M毫秒内,垃圾收集耗时大概率不超过N毫秒的目标。

思想转变:要实现这个目标,首先要有一个思想上的转变,G1收集器出现之前的其他所有收集器,他们的收集范围要么是新生代( Minor GC ),要么是老年代( Major GC ),要么是整堆( Full GC ),而G1跳出了这个樊笼,它可以面向堆内存任何部分来组成回收集( Collection Set , CSet )进行回收,衡量标准不再是它属于哪个分代,而是哪个回收集中存放的垃圾最多,回收收益最大,就回收哪个

新的内存布局:当然G1能达到这个目标的关键在于G1开创了基于Region 的堆内存布局,当然也依然遵循了分代收集理论,但是堆内存布局与其他收集器有明显差异,G1不在坚持固定大小以及固定数量的分代区域划分,而是把连续的java堆内存划分成多个大小相等的独立区域( Region ),每个Region 可以根据需要扮演新生代的 Eden , Survivor ,或者老年代。收集器能够对扮演不同角色的 Region 采用不同的策略去处理,这样无论是对于新创建的对象还是对于熬过很多次垃圾收集的旧对象都有很好的收集效果。

Region 的大小可以通过参数 -XX:G1HeapRegionSize=value 设定,取值范围为 1MB~32MB,且应为 2 的 N 次幂。

Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。 G1认为只要大小超过了一个 Region 容量一半的对象即可判定为大对象,对于那些超过了整个 Region 容量的超级大对象,将会被存放在 N 个连续的Humongous Region 之中。G1仍然保留了新生代,老年代的概念,只不过它们不再连续和固定的了,

回收策略:G1之所以能建立可预测的“停顿时间模型”的原因在于它将Region 作为单次回收的最小单元,即每次回收的空间都是 Region 的整数倍,同时G1会去追踪各个 Region 里面垃圾的“价值”(回收所获得的空间大小以及回收所需要的时间的经验值),然后在后台维护一个优先级列表,每次根据用户设定停顿时间( -XX:MaxGCPauseMillis=time ,默认200毫秒),优先回收价值收益最大的那些 Region 。

回收时G1 将存活的对象从堆的一个或多个 Region 复制到堆上的单个其他Region ,并在此过程中压缩和释放内存。这个工作是在多处理器上并行执行,以减少暂停时间并提高吞吐量。因此,每次垃圾回收时,G1 都会不断努力减少碎片

垃圾回收细节

1、跨 Region 引用如何解决:前面我们知道通过记忆集( RSet )解决跨代引用,但是在G1中,每个 Region 都需要维护自己的记忆集,记录别的 Region指向自己,但是G1中的 Region 数量要比传统收集器的分代数量明显多的多,所以G1中使用记忆集要比其他收集器有着更高的内存占用负担,根据经验,G1至少要耗费大约相当于java堆容量10%~20%

2、并发标记问题:如何保证并发标记阶段GC收集线程与用户线程互不干扰,当然G1是通过原始快照( SATB )解决的(CMS是通过增量更新实现的)。
另外一个需要解决的就是并发回收阶段如何处理用户线程新创建对象的内存分配,G1的做法是为每个 Region 设计了两个名为 TAMS ( Top at MarkStart )的指针,把 Region 中的一部分空间划分出来用于存放并发回收过程中的新对象分配。G1收集器在本次回收时默认这些对象是存活的,不回收的。

3、如何建立可靠的可预测模型: 用户通过 -XX:MaxGCPauseMillis=time参数指定的停顿时间只是一个期望值,但是G1怎么做才能满足用户的期望呢?
G1收集器在收集过程中会记录每个 Region 的回收耗时,每个 Region 记忆集里的脏卡数量等各个可测量的步骤花费的成本,并分析出平均值,标准偏差,置信度等统计信息。根据这些信息决定 Region 的回收价值。

参数设置

1、启用G1收集器:-XX:+UseG1GC
2、设置分区大小: -XX:G1HeapRegionSize=value
3、设置最大GC暂停时间: -XX:MaxGCPauseMillis=time
4、设置堆的最大内存,对于需要大堆( >6GB )且GC延迟需求有限(稳定且可预测的暂停时间低于0.5秒)的应用程序,推荐使用G1收集器

运行过程

在这里插入图片描述

初始标记:标记出 GC Roots 直接关联的对象,并且修改TAMS指针的值,这个阶段速度较快,STW,单线程执行
并发标记:从 GC Root 开始对堆中的对象进行可达新分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。
重新标记:修正在并发标记阶段因用户程序执行而产生变动的标记记录,即处理 SATB 记录。STW,并发执行。
筛选回收阶段会对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划,筛出后移动合并存活对象到空Region,清除旧的,完工。因为这个阶段需要移动对象内存地址,所以必须STW

总结:
G1前面的几步和CMS差不多,只有在最后一步,CMS是标记清除,G1需要合并Region属于标记整理

优缺点

1、并发性:继承了CMS的优点,可以与用户线程并发执行。当然只是在并发标记阶段。其他还是需要STW
2、分代GC:G1依然是一个分代回收器,但是和之前的各类回收器不同,它同时兼顾年轻代和老年代。而其他回收器,或者工作在年轻代,或者工作在老年代;
3、空间整理:G1在回收过程中,会进行适当的对象移动,不像CMS只是简单地标记清理对象。在若干次GC后,CMS必须进行一次碎片整理。而G1不同,它每次回收都会有效地复制对象,减少空间碎片,进而提升内部循环速度。
4、可预测性:为了缩短停顿时间,G1建立可预存停顿的模型,这样在用户设置的停顿时间范围内,G1会选择适当的区域进行收集,确保停顿时间不超过用户指定时间。

几点建议:
1、如果应用程序追求低停顿,可以尝试选择G1
2、经验值上,小内存6G以内,CMS优于G1,超过8G,尽量选择G1
3、是否代替CMS只有需要实际场景测试才知道。(如果使用G1后发现性能还不如CMS,那么还是选择CMS


http://www.ppmy.cn/server/45143.html

相关文章

android13 差分包制作命令

./out/host/linux-x86/bin/ota_from_target_files -v -iCode/SourceCode/android13/ntls/userdebug/hpg2_24-target_files-38.zip --block -p ./out/host/linux-x86 Code/SourceCode/android13/ntls/userdebug/hpg2_24-target_files-39.zip update_ud.zip 脚本命令行参数 命令…

富凡行是什么软件,来具体聊一聊它的详情,感兴趣的不要错过了

目前做网络项目的人很多,也就衍生出了很多的软件、项目、平台。接触过了很多的产品,感触颇深,确实市面上的东西差别都很大,有好的,有不好的。 我也是喜欢在网上做点副业,自己捣鼓一下,毕竟互联网…

Codeforces Round 948 (Div. 2) E. Tensor(思维题-交互)

题目 n(3<n<100)个点的有向图&#xff0c; 图的边的关系未知&#xff0c;但保证以下两点&#xff1a; 1. 只存在j->i&#xff08;i<j&#xff09;的边 2. 对于任意三个点i、j、k&#xff08;i<j<k&#xff09;&#xff0c;要么k可以到达i&#xff0c;要么…

渗透测试工具Cobalt strike-2.CS基础使用

三、结合metasploit,反弹shell 在kali中开启使用命令开启metasploit msfconsole ┌──(root㉿oldboy)-[~] └─# msfconsole --- msf6 > use exploit/multi/handler [*] Using configured payload generic/shell_reverse_tcp --- msf6 exploit(multi/handler) > show …

Stable Diffusion 3报告

报告链接&#xff1a;Stable Diffusion 3: Research Paper — Stability AI 文章目录 要点表现架构细节通过重新加权改善整流流量Scaling Rectified Flow Transformer Models灵活的文本编码器RF相关论文 引言 随着人工智能技术的飞速发展&#xff0c;文本到图像生成领域正经…

写代码之前一定要提前想好思路

就和写数学题目一样&#xff0c;在做题目之前要先把思路确立下来。可能是我早年做数学的时候老是着急做题目没怎么分析过题目&#xff0c;把这个习惯不自觉地代入了代码的写入当中。习惯的养成使得我即使明白了自己的问题也依然会不断的犯错&#xff0c;看来只有刻意地提醒自己…

【算法刷题day60】Leetcode:84. 柱状图中最大的矩形

文章目录 Leetcode 84. 柱状图中最大的矩形解题思路代码总结 草稿图网站 java的Deque Leetcode 84. 柱状图中最大的矩形 题目&#xff1a;84. 柱状图中最大的矩形 解析&#xff1a;代码随想录解析 解题思路 反方向接雨水。见上一篇文章 代码 class Solution {public int la…

【数据结构】探索树中的奇妙世界

专栏介绍&#xff1a; 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累…