四、分代垃圾回收机制及垃圾回收算法

学习垃圾回收的意义

Java 与 C++等语言最大的技术区别:自动化的垃圾回收机制(GC)

为什么要了解 GC 和内存分配策略

1、面试需要

2、GC 对应用的性能是有影响的;

3、写代码有好处

栈:栈中的生命周期是跟随线程,所以一般不需要关注

堆:堆中的对象是垃圾回收的重点

方法区/元空间:这一块也会发生垃圾回收,不过这块的效率比较低,一般不是我们关注的重点

一、什么是GC

自动化的垃圾回收机制

回收区域:

堆:堆中的对象是垃圾回收的重点

方法区/元空间:这一块也会发生垃圾回收,不过这块的效率比较低,回收 class,常量,静态常量,字符串常量池

二、分代回收理论及GC分类

1、堆内存为什么分新生代和老年代

原因如下:

1.绝大部分(98%)对象都是朝生夕死

2.熬过多次垃圾回收的对象就越难回收

根据以上两个理论,朝生夕死的对象放一个区域,难回收的对象放另外一个区域,所以这就构成了新生代和老年代

新生代占堆内存的1/3,又分为Eden区,From区,To区,比例为8:1:1

2、GC(Garbage Collection)分类

1、新生代回收(Minor GC/Young GC):指的是进行新生代的回收。

特点:发生在新生代上,发生的较频繁,执行速度较快

触发条:Eden 区空间不足

2、老年代回收(Major GC/Old GC):指的是进行老年代回收。(目前只有CMS垃圾回收器会有这个单独回收老年代的行为)

3、整堆回收(Full GC):收集整个Java堆和方法区(注意包含方法区)

特点: 主要发生在老年代上(新生代也会回收),较少发生,执行速度较慢

触发条件:调用 System.gc()、老年代区域空间不足、空间分配担保失败、JDK 1.7 及以前的永久代(方法区)空间不足。CMS GC 处理浮动垃圾时,如果新生代空间不足,则采用空间分配担保机制,如果老年代空间不足,则触发 Full GC

3、新生区的垃圾回收机制

新生区分为:Eden区、Survivor0区、Survivor1区(也称为from区和to区)

其中Eden区占80%的内存空间,每块Survivor各占用10%的内存空间(如:Eden占800M,每个Survivor占100M)

1.开始时创建的对象都是分配在Eden区域中,当Eden区快满了,就会触发垃圾回收Minor GC(使用复制算法进行垃圾回收)

2.Minor GC处理后,首先会把Eden区中还存活着的对象一次性转入其中一块空闲着的Survivor区。然后清空Eden区,之后创建的对象就继续放入Eden区中了,直至下次Eden又被填满。

3.Eden再次被填满时,就会再次出发Minor GC,清理后(Minor会清理Eden区和Survivor区的内存),Eden区和存在对象的Survivor区(此时的from区)中存活的对象转移到另一块空着的Survivor区中(此时的to区),并清空Eden区和之前存在对象的Survivor区(此时变为to区了,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。)

这就是复制算法的流程。

一直要保持一个Survivor区是空的以提供复制算法垃圾回收,而这块区域的内存只占整块的10%,其他90%内存都可以被使用,课件内存利用率还是相当高的。

三、垃圾回收算法

  • 复制算法
  • 标记清除
  • 标记整理

1、复制算法(Copying)----新生代多用此算法

将可用内存按容量划分为大小相等的两块,比如A块,B块,每次只使用其中的一块。假如A块快用完了,就把A块还存活着的对象复制到B块上面,然后再把A块空间一次性清理掉,反之一样。这样每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可。实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。

但是要注意:内存移动是必须实打实的移动(复制),所以对应的引用(直接指针)需要调整。

复制回收算法适合于新生代,因为大部分对象朝生夕死,那么复制过去的对象比较少,效率自然就高,另外一半的一次性清理是很快的。

优点:实现简单、运行高效,没有内存碎片,只需扫一遍

缺点:利用率只有一半,所有引用(直接指针)需要调整(因为整体内存都移动了,内存地址变了)。存活对象较多时效率明显会降低

  • Appel式回收

因为复制算法缺点利用率只有一半,就出现了eden区和两块较小的Survivor空间,from区和to区 ,这样只用浪费10%。

Apple式回收是一种更加优化的复制回收分代策略:具体做法是分配一块较大的 Eden 区和两块较小的 Survivor 空间(你可以叫做 From 或者 To,也可以叫做 Survivor1 和Survivor2)

新生代的内存空间比例是Eden : From : To = 8 : 1 : 1

研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例划分,而是将新生代划分为一块较大的Eden区和两块较小的Survivor空间,每次使用Eden区和其中一块Survivor,当回收的时候,将Eden区和Survivor中还存活着的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和刚才说用过的Survivor空间,这样新生代的浪费空间只有10%。

HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10%的对象存活,当10%的 Survivor 空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)

-XX:SurvivorRatio 8 8:1:1

-XX:+UseAdaptiveSizePolicy (默认开启)。这是一个开关参数, 启动自使用大小,当这个参数被激活之后,就不需要人工指定新生代的大小(-Xmn)、Eden 与 Survivor 区的比例(-XX:SurvivorRatio)、 晋升老年代对象大小(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量

2、标记-清除算法----老年代多用此算法

标记清除算法分为“标记”和“清除”两个阶段,第一次扫描所有的对象标记出需要回收的对象。第二次扫描回收被标记的对象。

优点:内存利用率100%

缺点:比复制算法效率略低,因为两遍扫描,内存位置不连续,产生碎片。因为空间碎片太多可能导致分配大对象时无法找到足够的连续内存而不得提前触发另一次垃圾回收动作。

回收的时候如果需要回收的对象越多,需要做的标记和清除的工作越多,所以标记清除算法适用于老年代。

3、标记-整理算法----年代多用此算法

首先标记出所有需要回收的对象,在标记完成后,后续步骤不是直接对对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

优点:没有碎片,利用率100%

缺点:效率偏低,两遍扫描,对象移动导致指针需要调整(整理的时候需要移动),对象移动不单会加重系统负担,同时需要全程暂停用户线程(stw)才能进行,同时所有引用对象的地方都需要更新

四、常见的垃圾回收器

1、垃圾回收器基础

新生代中,每次垃圾回收时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成回收。

老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

    1. 单线程垃圾回收器 Serial、Serial Old
    2. 多线程并行垃圾回收器 Parallel Scavenge、ParNew、Parallel Old
    3. 多线程并发垃圾回收器 CMS、G1

并行:垃圾收集器的多线程同时进行。

并发:垃圾收集的多线程和应用的多线程同时进行。

注:吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间) 虚拟机总共运行了 100 分钟,其中垃圾收集花掉 1 分钟,那吞吐量就是 99%。

垃圾收集时间= 垃圾回收频率 * 单次垃圾回收时间

Stop The World(STW)指的是停止所有的用户线程

单线程进行垃圾回收时,必须暂停所有的工作线程,直到它回收结束。这个暂停称之为“Stop The World”, 但是这种 STW 带来了恶劣的用户体验

例如:应用每运行一个小时就需要暂停响应 5 分。这个也是早期 JVM 和 java 被 C/C++语言诟病性能差的一个重要原因。所以 JVM 开发团队一直努力消除或降低 STW的时间。

GC 收集器和我们 GC 调优的目标就是尽可能的减少 STW 的时间和次数。

各种垃圾收集器组合示意图(黑色的线表示组合)如下:

Oracle 官方也有对应英文解释 https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-DB4CAE94-2041-4A16-90EC-6AE3D91EC1F1

2、单线程垃圾回收器

(1)Serial (使用复制算法)和 Serial Old (使用标记整理算法)

最古老的,单线程,独占式,成熟,适合单 CPU,一般用在客户端模式下。

这种垃圾回收器只适合几十兆到一两百兆的堆空间进行垃圾回收(可以控制停顿时间在 100ms 左右),但是对于超过这个大小的内存回收速度很慢,所以对于现在来说这个垃圾回收器已经是一个鸡肋。

过程:用户线程进行-->用户线程停止,进行新生代回收-->用户线程继续-->用户线程停止,进行老年代回收-->用户线程继续

参数设置

-XX:+UseSerialGC 新生代和老年代都用串行收集器,Serial,Serial Old

3、并行多线程垃圾回收器(重点)

  • Parallel Scavenge (使用复制算法)
  • ParNew (使用复制算法)
  • ParOLD (使用标记整理算法)

过程:用户线程进行-->用户线程暂停,进行新生代回收-->用户线程继续-->用户线程暂停,新型老年代回收-->用户线程继续

(1)ParNew

多线程垃圾回收器,与 CMS 进行配合,对于 CMS(CMS 只回收老年代),新生代垃圾回收器只有 Serial 与 ParNew 可以选。

CMS是追求STW,而Parallel Scavenge是追求吞吐量的,方向不一样,所以CMS和ParaNew组合

与Serial 基本没区别;唯一的区别:多线程,多 CPU 的,停顿(stw)时间比 Serial 少(在 JDK9 以后,把 ParNew 合并到了 CMS 了)

-XX:+UseParNewGC 新生代使用 ParNew,老年代使用 Serial Old

(2)Parallel Scavenge(ParallerGC)/ Parallel Old(jdk1.8默认使用)

关注吞吐量的垃圾收集器

高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。为了提高回收效率,从 JDK1.3 开始,JVM 使用了多线程的垃圾回收机制。

该垃圾回收器适合回收堆空间上百M~几个G

参数设置

JDK1.8 默认就是以下组合

-XX:+UseParallelGC 表示新生代使用 Parallel Scavenge,老年代使用 Parallel Old

收集器提供了两个参数用于精确控制吞吐量

-XX:MaxGCPauseMillis 控制的停顿的时间

-XX:GCTimeRatio 设置吞吐量大小

不过不要异想天开地认为如果把这个参数的值设置得更小一点就能使得系统的垃圾收集速度变得更快,垃圾收集停顿时间缩短是以牺牲吞吐量和新生代空间为代价换取的,系统把新生代调得小一些,收集 300MB 新生代肯定比收集 500MB 快,但这也直接导致垃圾收集发生得更频繁,原来 10 秒收集一次、每次停顿 100 毫秒,现在变成 5 秒收集一次、 每次停顿 70 毫秒。停顿时间的确在下降,但吞吐量也降下来了。

-XX:GCTimeRatio 参数的值则应当是一个大于 0 小于 100 的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。

例如:把此参数设置为 19,那允许的最大垃圾收集时占用总时间的 5% (即 1/(1+19)), 默认值为 99,即允许最大 1% (即 1/(1+99))的垃圾收集时间

由于与吞吐量关系密切,Parallel Scavenge 是“吞吐量优先垃圾回收器”。

4、并发垃圾回收器-Concurrent Mark Sweep(CMS)和Garbage First(G1)

  • CMS --追求减少STW
  • G1 ---追求可预测的STW

(1)Concurrent Mark Sweep(CMS))并发的多线程标记清除垃圾回收器

CMS是追求STW,而Parallel Scavenge是追求吞吐量的,方向不一样,所以CMS和ParaNew组合

过程:

用户线程进行-->用户线程暂停,初始标记-->并发标记(用户线程继续)--->暂停用户线程,重新标记--->并发清理(用户线程继续)--->用户线程继续

cms收集器是一种以获取最短回收停顿时间(stw)为目标的收集器。

目前很大一部分的 Java 应用集中在互联网站或者 B/S 系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS 收集器就非常符合这类应用的需求。

从名字(包含“Mark Sweep”)上就可以看出,CMS 收集器是基于“标记—清除”算法实现的,它的运作过程相对于前面几种收集器来说更复杂一些。

垃圾回收过程分为 4 个步骤

  • 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿(STW -Stop the world)。
  • 并发标记:从 GC Root 开始对堆中对象进行可达性分析,找到存活对象,它在整个回收过程中耗时最长,不需要停顿。三色标记算法
  • 重新标记:为了修正并发标记时因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(STW)。停顿时间一般会比初始标记稍长一些,但远比并发标记的时间短。
  • 并发清除:不需要停顿。

优点:

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。

-XX:+UseConcMarkSweepGC ,表示新生代使用 ParNew,老年代的用 CMS

缺点:

CPU 资源敏感:因为并发阶段多线程占据 CPU 资源,如果 CPU 资源不足,效率会明显降低。

浮动垃圾:由于 CMS 并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS 无法在当次收集中处理掉它们,只好留待下一次 GC 时再清理掉。这一部分垃圾就称为“浮动垃圾”。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。需要提前,在 1.6 的版本中老年代空间使用率阈值(92%)。我理解的是新生代晋级老年代

如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。

内存碎片:标记 - 清除算法会导致产生不连续的空间碎片

总体来说,CMS 是 JVM 推出了第一款并发垃圾收集器,所以还是非常有代表性。但是最大的问题是 CMS 采用了标记清除算法,所以会有内存碎片,当碎片较多时,给大对象的分配带来很大的麻烦,为了解决这个问题,CMS 提供一个参数:

-XX:+UseCMSCompactAtFullCollection,内存碎片整理,一般是开启的,如果分配不了大对象,就进行内存碎片的整理过程。这个地方一般会使用 Serial Old ,因为 Serial Old 是一个单线程,所以如果内存空间很大、且对象较多时,CMS 发生这样情况会很卡。

浮动垃圾和内存碎片造成的最大的弊端就是需要重启,可能需要人工每天晚上重启。重启总比serialod快

  • CMS 总结

CMS 问题比较多,所以现在没有一个版本默认是 CMS,只能手工指定。但是它毕竟是第一个并发垃圾回收器,对于了解并发垃圾回收具有一定意义,所以我们必须了解。

为什么 CMS 采用标记-清除,在实现并发的垃圾回收时,如果采用标记整理算法,那么还涉及到对象的移动(对象的移动必定涉及到引用的变化,这个需要暂停业务线程来处理栈信息,这样使得并发收集的暂停时间更长),所以使用简单的标记-清除算法才可以降低 CMS 的 STW 的时间。该垃圾回收器适合回收堆空间几个 G~ 20G 左右。

在 JDK1.8 中,配置参数:

(2)Garbage First(G1)

设计思想-追求可预测的停顿时间

随着 JVM 中内存的增大,STW 的时间成为 JVM 急迫解决的问题,但是如果按照传统的分代模型,总跳不出 STW 时间不可预测这点。为了实现 STW 的时间可预测,首先要有一个思想上的改变。G1 将堆内存“化整为零”,将堆内存划分成多个大小相等独立区域(Region),每一个 Region都可以根据需要,扮演新生代的 Eden 空间、Survivor 空间,或者老年代空间。回收器能够对扮演不同角色的 Region 采用不同的策略去处理,这样无论是新生对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。

Region只是一小块内存,可能是Eden区,也可能是Survivor区,也可能是Old区,另外还有一类特殊的Humongous区域,专门来存储大对象。G1认为只要大小超过一个Region容量一半的对象即可判定为大对象。

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

而对于那些超过了整个 Region 容量的超级大对象,将会被存放在 N 个连续的 Humongous Region 之中,G1 的进行回收大多数情况下都把 Humongous Region 作为老年代的一部分来进行看待。

内部布局改变

G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

算法:标记—整理 (humongous) 和复制回收算法(survivor)。

参数设置

-XX:+UseG1GC 使用 G1 垃圾回收器 >6G,且GC延迟要求有限(稳定且可预测的暂停时间低于0.5秒)的应用程序

-XX:+G1HeapRegionSize 分区大小, 取值范围为 1MB~32MB,且应为 2 的 N 次幂

一般建议逐渐增大该值,随着 size 增加,垃圾的存活时间更长,GC 间隔更长,但每次 GC 的时间也会更长。

最大 GC 暂停时间 MaxGCPauseMillis,因为进行选择性的回收所以可以追求可预测的stw

过程:

分四步:初始标记,并发标记,最终标记,筛选回收

1、初始标记( Initial Marking) -stw

和CMS一样,仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改 TAMS 指针的值,让第二步和用户线程并发运行时,能正确地在可用的 Region 中分配新对象。这个阶段需要停顿线程(stw),但耗时很短,而且是借用进行 Minor GC (young GC)的时候同步完成的,所以 G1 收集器在这个阶段实际并没有额外的停顿。

2、并发标记( Concurrent Marking)

TAMS(Top at Mark Start)

要达到 GC 与用户线程并发运行,必须要解决回收过程中新对象的分配,所以 G1 为每一个 Region 区域设计了两个名为 TAMS(Top at Mark Start)的指针,从 Region 区域划出一部分空间用于记录并发回收过程中的新对象。这样的对象认为它们是存活的,不纳入垃圾回收范围。

从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后 ,并发时有引用变动的对象 ,这些对象会漏标( 三色标记思想能解决 ),漏标的对象会被一个叫做SATB(snapshot-at-the-beginning,快照)算法来解决。

3、最终标记( Final Marking),三色标记发生的阶段 -stw

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

4、筛选回收( Live Data Counting and Evacuation) -stw

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

  • 特点

并行与并发:G1 能充分利用多 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿的时间,部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1收集器仍然可以通过并发的方式让 Java 程序继续执行。

分代收集:与其他收集器一样,分代概念在 G1 中依然得以保留。虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但它能够采用不同的方式处理新创建的对象和已经存活了一段时间、熬过多次 GC 的旧对象以获取更好的收集效果。

空间整合:与 CMS 的“标记—清理”算法不同,G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC。

可预测的停顿:G1 收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个 Java 堆中进行全区域的垃圾收集。G1 跟踪各个 Region 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region(这也就是 Garbage-First 名称的来由)。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限的时间内可以获取尽可能高的收集效率。

追求停顿时间:

-XX:MaxGCPauseMillis 指定目标的最大停顿时间,G1 尝试调整新生代和老年代的比例,堆大小,晋升年龄来达到这个目标时间。

怎么玩?

该垃圾回收器适合回收堆空间上百 G。一般在 G1 和 CMS 中间选择的话平衡点在 6~8G,只有内存比较大 G1 才能发挥优势。

G1 GC 模式

Young GC

选定所有年轻代里的 Region。通过控制年轻代的 region 个数,即年轻代内存大小,来控制 young GC 的时间开销。(复制回收算法)

Mixed GC

选定所有年轻代里的 Region,外加根据全局并发标记( global concurrent marking ,上述四个过程)统计得出收集收益高的若干老年代 Region。在用户指定的开销目标范围内尽可能选择收益高的老年代 Region。mixed GC 不是 full GC,它只能回收部分老年代的 Region。如果 mixed GC 实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行 Mixed GC,就会使用 serial old GC(full GC)来收集整个 GC heap。所以我们可以知道,G1 是不提供 full GC 的。

G1 GC 主要的参数

-XX:G1HeapRegionSize=n 设置 Region 大小,并非最终值

-XX:MaxGCPauseMillis 设置 G1 收集过程目标时间,默认值 200ms,不是硬性条件

-XX:G1NewSizePercent 新生代最小值,默认值 5%

-XX:G1MaxNewSizePercent 新生代最大值,默认值 60%

-XX:ParallelGCThreads STW 期间,并行 GC 线程数

-XX:ConcGCThreads=n 并发标记阶段,并行执行的线程数

-XX:InitiatingHeapOccupancyPercent 设置触发标记周期的 Java 堆占用率阈值。默认值是 45%。这里的 java 堆占比指的是non_young_capacity_bytes,包括 old+humongous

5、CMS 及 G1 的细节

(1)CMS

CMS 垃圾收集器是基于标记清除算法实现的,主要用于老年代垃圾回收。CMS 收集器的 GC 周期主要由 7 个阶段组成,其中有两个阶段会发生stop-the-world(初始标记、重新标记),其它阶段都是并发执行的。

(2)G1

G1 垃圾收集器是基于标记整理算法实现的,是一个跨越新生代和老年代的垃圾收集器,既负责年轻代,也负责老年代的垃圾回收。

G1 的垃圾回收分为 Young GC、Mix GC 。

G1 Young GC 主要是在 Eden 区进行,当 Eden 区空间不足时,则会触发一次 Young GC。

将 Eden 区数据移到 Survivor 空间时,如果 Survivor 空间不足,则会直接晋升到老年代。

此时 Survivor 的数据也会晋升到老年代。Young GC 的执行是并行的,期间会发生 STW。

当堆空间的占用率达到一定阈值后会触发 G1 Mix GC(阈值由命令参数 -XX:InitiatingHeapOccupancyPercent 设定,默认值 45),

Mix GC 主要包括了四个阶段,其中只有并发标记阶段不会发生 STW,其它阶段均会发生 STW。

五、各个收集器对比

100M serial

几百兆几个G parallel scavenge /parallel old

1g~几十个G CMS

6个G~几百G G1

垃圾回收器的重要参数(使用-XX:)

jinfo -flags pid

参数 描述

UseSerialGC 虚拟机运行在 Client 模式下的默认值,打开此开关后,使用 Serial+Serial Old 的收集器组合进行内存回收

UseParNewGC 打开此开关后,使用 ParNew + Serial Old 的收集器组合进行内存回收

UseConcMarkSweepGC 打开此开关后,使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用

UseParallelGC 虚拟机运行在 Server 模式下的默认值,打开此开关后,使用 Parallel Scavenge + Serial Old(PS MarkSweep) 的收集器组合进行内存回收

UseParallelOldGC 打开此开关后,使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收

SurvivorRatio 新生代中 Eden 区域与 Survivor 区域的容量比值,默认为 8,代表 Eden : Survivor = 8 : 1

PretenureSizeThreshold 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配

MaxTenuringThreshold 晋升到老年代的对象年龄,每个对象在坚持过一次 Minor GC 之后,年龄就增加 1,当超过这个参数值时就进入老年代

UseAdaptiveSizePolicy 动态调整 Java 堆中各个区域的大小以及进入老年代的年龄

HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个 Eden 和 Survivor 区的所有对象都存活的极端情况

ParallelGCThreads 设置并行 GC 时进行内存回收的线程数

GCTimeRatio GC 时间占总时间的比率,默认值为 99,即允许 1% 的 GC 时间,仅在使用 Parallel Scavenge 收集器生效

MaxGCPauseMillis 设置 GC 的最大停顿时间,仅在使用 Parallel Scavenge 收集器时生效

CMSInitiatingOccupancyFraction 设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认值为 68%,仅在使用 CMS 收集器时生效

UseCMSCompactAtFullCollection 设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理,仅在使用 CMS 收集器时生效

CMSFullGCsBeforeCompaction 设置 CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理,仅在使用 CMS 收集器时生效

-XX:+UseSerialGC 新生代和老年代都用串行收集器,Serial,Serial Old

-XX:+UseParallelGC 表示新生代使用 Parallel Scavenge,老年代使用 Parallel Old . JDK1.8 默认就是以下组合

-XX:+UseConcMarkSweepGC ,表示新生代使用 ParNew,老年代的用 CMS

-XX:+UseG1GC 使用 G1 垃圾回收器 >6G,且GC延迟要求有限(稳定且可预测的暂停时间低于0.5秒)的应用程序

-Xms 设置堆的最小值和初始大小

-Xmx 设置堆堆区内存可被分配的最大上限

-Xmn 设置堆的初始和最大大小

-XX:+PrintGCDetails 打印 GC 详情

-XX:+HeapDumpOnOutOfMemoryError 当堆内存空间溢出时输出堆的内存快照新生代大小配置参数的优先级:

-XX:SurvivorRatio 设置eden区和survior区的比例 8 81:1

java -XX:+PrintCommandLineFlags -version 查看jdk使用的垃圾收集器信息

java -XX:+PrintGCDetails -version 看看jvm内存信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/204914.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 日志(略讲)

日志操作 日志输出: # 输出日志信息 logging.debug("调试级别日志") logging.info("信息级别日志") logging.warning("警告级别日志") logging.error("错误级别日志") logging.critical("严重级别日志")级别设置…

Java程序员,你掌握了多线程吗?(文末送书)

目录 01、多线程对于Java的意义02、为什么Java工程师必须掌握多线程03、Java多线程使用方式04、如何学好Java多线程送书规则 摘要:互联网的每一个角落,无论是大型电商平台的秒杀活动,社交平台的实时消息推送,还是在线视频平台的流…

unity 2d 入门 飞翔小鸟 下坠功能且碰到地面要停止 刚体 胶囊碰撞器 (四)

1、实现对象要受重力 在对应的图层添加刚体 改成持续 2、设置胶囊碰撞器并设置水平方向 3、地面添加盒状碰撞器 运行则能看到小鸟下坠并落到地面上

Windows本地如何添加域名映射?(修改hosts文件)

1. DNS(域名系统) Domain Name System(域名系统):为了加快定位IP地址的速度, 将域名映射进行层层缓存的系统. 目的:互联网通过IP(10.223.146.45)定位浏览器建立连接,但是我们不易区别IP,为了方便用户辨识I…

柏睿网络分析:为什么微模块化机房越来越受欢迎?

与传统机房相比,微模块化机房的建设周期更短,扩展性更强,能耗更低,运维难度也相对较低。因此,微模块化机房是一种高效、灵活、节能的机房解决方案,适用于各种规模的数据中心。 一体化分布式部署&#xff1a…

idea利用SpringMVC框架整合ThymeLeaf

简洁一些:两个重要文件 1.controller指定html文件:我们访问http://localhost:8080/test package com.example.appledemo.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import o…

甘草书店:#9 2023年11月23日 星期四 「麦田创业历程分享1——联合创始人的魔幻相遇」

既然甘草是一家创业主题的书店咖啡馆,那就从我,从麦田开始分享一下创业历程吧。 需要声明的是,我从不认为我有资格对别人的创业指指点点,每位创业者的性格、背景、基础、诉求各有不同,时代发展也日新月异,…

netty07-粘包半包以及解决方案

粘包指的是发送方在发送数据时,多个数据包被合并成一个大的数据包发送到接收方,接收方在接收时无法准确地区分各个数据包的边界,从而导致数据粘在一起。 半包指的是发送方发送的数据包被拆分成了多个小的数据包,在接收方接收时&a…

Vue的Nuxt项目部署在服务器,pm2动态部署和npm run build静态部署

Nuxt项目的部署有两种方式,一种是静态部署,一种是动态部署 静态部署需要关闭项目的ssr功能,动态部署则不需关闭,所以怎么部署项目就看你用不用ssr功能了 。 1.静态部署 先说静态部署,很简单,只需要在nuxt…

【C语言】程序设计加密解密

🚩write in front🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评百大博…

黛姿秘语,匠心之作,严谨工艺铸就完美肌肤

在喧嚣的都市中,我们每个人都渴望找到一款可以信赖长期使用的护肤品,让匹配的肌肤重新焕发光彩,找到更加年轻的自己,但是目前市面上的产品让人眼花缭乱,作为普通的消费者,无法去辨别什么产品好,…

北京市经信局局长姜广智带队调研三六零 强调大模型应与行业结合

12月6日,北京市经济和信息化局局长姜广智、副局长王磊带队走访调研三六零集团,就共促城市级数字安全基础设施项目落地,打造引领行业发展标杆项目,推动大模型落地应用赋能产业、行业发展等话题进行交流。360集团创始人周鸿祎接待来…

(十五)Flask覆写wsgi_app函数实现自定义中间件

中间件 一、剖析: 在前面讲session部分提到过:请求一进来,Flask会自动调用应用程序对象【Flask(__name__)】的__call__方法,这个方法负责处理请求并返回响应(其实如下图:其内部就是wsgi_app方法&#xff…

C++ 指针进阶

目录 一、字符指针 二、指针数组 三、数组指针 数组指针的定义 &数组名 与 数组名 数组指针的使用 四、数组参数 一维数组传参 二维数组传参 五、指针参数 一级指针传参 二级指针传参 六、函数指针 七、函数指针数组 八、指向函数指针数组的指针 九、回调函…

docker基本管理和概念

1、定义:一个开源的应用容器引擎,基于go语言开发,运行在liunx系统中的开源的、轻量级的“虚拟机” docker的容器技术可以在一台主机上轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器 docker的宿主机是liunx系统,集…

第二十一 网络通信

网络通信 21.1 网络程序设计基础 网络程序设计编写的是与其他计算机进行通信的程序。 21.1.1 局域网与互联网 实现两台计算机的通信,必须用一个网络线路来连接两台计算机 21.1.2 网络协议 1.ip协议 IP是Internet Protocol的简称,是一种网络协议 2…

Vue项目使用Sortable.js实现拖拽功能

想了解更多-可前往 Sortable.js官网 查看组件属性及参数 安装组件&#xff08;我这里使用的是NPM安装&#xff09; npm install sortablejs --save在需要使用拖拽功能的页面中使用&#xff08;完整功能代码&#xff09; <div class"tag_box"><div class&q…

使用VS Code远程开发MENJA小游戏并通过内网穿透分享本地游戏到公网

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 推荐一个人工智能学习网站 点击跳转学习 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通…

centos服务器安装docker和Rabbitmq

centos服务器 一 centos安装docker1 安装docker所需要的依赖包2配置yum源3查看仓库中所有的docker版本4安装docker5 设置docker为开机自启6验证docker是否安装成功 二 使用docker安装RabbitMQ拉取RabbitMQ镜像创建并运行容器 一 centos安装docker 1 安装docker所需要的依赖包 …

Python Flask-Login:构建强大的用户认证系统

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Flask-Login是Flask框架的一个扩展&#xff0c;专为构建用户认证系统而设计。它简化了用户登录和身份验证的流程&#xff0c;为开发者提供了强大而灵活的工具。本文将深入介绍Flask-Login的核心功能、基本用法和…