五、垃圾回收

news2024/11/15 21:22:10

1. 垃圾回收基础

1.1 什么是垃圾

简单说就是:内存中已经不再被使用到的内存空间就是垃圾。

1.2 如何判定是垃圾

1.2.1 引用计数法

引用计数法:给对象添加一个引用计数器,有访问就 +1,引用失效就 -1

引用计数法的优缺点:

  1. 优点:实现简单,效率高;

  2. 缺点:不能解决对象之间的循环引用问题。

    在这里插入图片描述

1.2.2 根搜索算法

从根(GC Roots)节点向下搜索对象节点,搜素走过的路径称为 引用链。当一个对象到根之间没有连通时,则该对象不可用,不可用的对象会被判定为垃圾,对其进行回收。

一个对象可能存在于多条引用链中,即可能不止一个根节点能连通到该对象,所以根节点用复数形式表示:GC Roots

在这里插入图片描述

很多商用虚拟机都是采用根搜索算法来判定一个对象是不是垃圾。

1.2.2.1 可作为 GC Roots 的对象

可作为 GC Roots 的对象包括:

  1. 虚拟机栈中(方法参数和局部变量)引用的对象;
  2. 方法区中的类静态成员变量引用的对象;
  3. 方法区中常量引用的对象;
  4. 本地方法栈中 JNI 引用的对象;
  5. 作为同步锁的对象;
1.2.2.2 HotSpot 虚拟机对根搜索算法的优化(OopMap 数据结构)

按照根搜索算法,为了判定一个对象是否为垃圾,需要从每一个 GC Roots 开始遍历每一条引用链,从而查询某个对象是否在这些引用链中。

这样做的话,对于代码量很大,引用链很多的项目,判定对象是否为垃圾的效率是很低的。

为了解决这个问题,HotSpot 虚拟机采用 OopMap 这种数据结构来优化判定对象是否为垃圾的效率:

OopMap 数据结构中,存储了对象之间的引用关系,通过 OopMap 可以加快定位对象的引用位置,而不用从 GC Roots 开始遍历引用链,从而大大提高判定对象是否为垃圾的效率。

1.2.2.2.1 安全点 & 安全区域

OopMap 的协助下,JVM 可以很快地完成对 GC Roots 的枚举。但是 JVM 并没有对每一条指令都记录并生成一个 OopMap

记录并生成 OopMap 的这些 “特定位置” 被称为 安全点

当用户线程执行到安全点后,才允许暂停用户线程,执行 GC 线程进行 GC

如果一段代码中,对象引用关系不会发生变化,这段代码区域中任何地方开始 GC 都是安全的,那么这个区域称为 安全区域

1.2.3 引用分类
1.2.3.1 强引用

类似于 Foo foo = new Foo() 这样的,称 new 出来的对象被强引用了。

被强引用的对象,在每次 GC 时都不会被回收。

1.2.3.2 软引用

类似于 SoftReference<Foo> sRef = new SoftReference<>(new Foo()) 这样的,称 new 出来的对象被软引用了。

被软引用的对象,当 GC 时,在内存不够用的情况下才会被回收。

1.2.3.3 弱引用

类似于 WeakReference<Foo> wRef = new WeakReference<>(new Foo()) 这样的,称 new 出来的对象被弱引用了。

被弱引用的对象,在每次 GC 时都会被回收。

1.2.3.4 虚引用(幽灵引用)

类似于 PhantomReference<Foo> wRef = new PhantomReference<>(new Foo(), referenceQueue) 这样的,称 new 出来的对象被虚引用了。

虚引用也称为幽灵引用或幻影引用,是最弱的引用。

被虚引用的对象,在每次 GC 时都会被回收。

1.2.3.5 示例:GC 对软引用和弱引用的影响(finalize() & ReferenceQueue
  1. GC 对软引用的影响

    在这里插入图片描述

    1. 当垃圾收集器准备回收某个对象时 ,会调用该对象的 finalize() 方法。但是,调用该方法,也并不代表着垃圾收集器一定会回收该对象。

    2. 当垃圾收集器回收了被软引用的对象后,相关的软引用会放到 ReferenceQueue 队列中。注意:ReferenceQueue 中保存的是 Reference 本身,而不是 Reference 中引用的对象。

  2. GC 对弱引用的影响

    在这里插入图片描述

1.2.4 跨代引用

跨代引用:就是一个代中的对象引用了另一个代中的对象。如:

  1. 新生代中的对象引用了老年代中的对象;
  2. 老年代中的对象引用了新生代中的对象。

跨代引用假说:跨代引用相对于同代引用来说只是极少数的。

隐含推论 :存在互相引用关系的两个对象,应该是倾向于同时生存或同时消亡的。具体为:

  1. 当两个对象处于同一代中时,它们之间的引用就是同代引用,互相同代引用的两个对象,它们之间倾向于同时生存或同时消亡。

  2. 当两个对象不处于同一代中时,它们之间的引用就是跨代引用。此时,当老年代中的对象引用了新生代中的对象时,新生代中的对象不会被回收,而是会被转存到老年代中。也就是说,互相跨带引用的两个对象,经过多次 GC 后,最终会转为互相同代引用。

1.2.4.1 跨代引用造成的问题

当进行垃圾回收时,为了查询跨代引用,不仅要扫描新生代中的对象,还要扫描老年代中的对象,这样才能判断存在着多少个新生代中的对象和老年代中的对象之间的跨代引用。

但是,这样做的话就会极大地影响垃圾回收的性能。为了解决这个问题,引入了新的数据结构:记忆集

1.2.4.2 记忆集(解决跨代引用造成的问题)

记忆集(Remembered Set):一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。

可以理解为,记忆集中记录了存在跨代引用的新生代对象和老年代对象的内存地址,当进行垃圾回收时,只需要判断记忆集中记录的存在跨代引用的对象是否为垃圾即可。

1.2.4.2.1 实现记忆集的三种方式

实现记忆集这种数据结构所采用的三种方式:

  1. 字长精度:每个记录精确到一个机器字长,该字包含跨代指针。

  2. 对象精度:每个记录精确到一个对象,该对象里有字段含有跨代指针。

  3. 卡精度:每个记录精确到一块内存区域,该区域内有对象含有跨代指针。

1.2.4.2.2 卡表(Card Table,记忆集的具体实现)

卡表是记忆集的一种具体实现,定义了记忆集的记录精度与堆内存的映射关系。

卡表采用了卡精度实现记忆集这种数据结构。

1.2.4.2.2 卡页(Card Page,卡表的组成元素)

卡表的每个元素都对应着其标识的内存区域中的一块特定大小的内存块。这个内存块称为卡页。

也就是说,卡表由卡页组成,每个卡页中保存了多个对象。

当某个卡页中的对象存在跨代引用时,就会记录下来。当进行垃圾回收时,会将这些记录了的对象筛选出来,通过根搜索算法来判定它们是否为垃圾。

1.2.4.2.3 写屏障

卡表中记录了对象间的引用关系,那么当对象间的引用关系发生变化时,卡表中的数据记录状态也应该随着改变。那么由谁来维护卡表的数据记录状态?什么时候来改变卡表中的数据记录状态?答:在 HotSpot 虚拟机中,是通过 写屏障 这种技术来维护卡表中的数据记录状态的。

写屏障可以看成是 JVM 对 “引用类型字段赋值” 这个动作的 AOP

所谓的对 “引用类型字段赋值” 这个动作的 AOP,就是说,JVM 会在 “引用类型字段赋值” 前后额外地添加一些程序,达到维护卡表的目的。其中:

  1. JVM 在 “引用类型字段赋值” 这个动作之前添加程序,称为 写前屏障

  2. JVM 在 “引用类型字段赋值” 这个动作之后添加程序,称为 写后屏障

1.2.5 判定是否为垃圾的步骤

判定是否为垃圾的步骤为:

  1. 先通过根搜索算法判定对象是否可用;

  2. 对于不可用的对象,再看是否有必要执行 finalize() 方法;

  3. 以上两步执行完后,如果对象仍然没有被使用,那么该对象就属于垃圾。

    通过第 12 步判定对象是否为垃圾时,存在两种情况:

    1. 通过根搜索算法判定对象不在任何一条引用链上;且没必要执行 finalize() 方法;

    2. 通过根搜索方法判定对象不在任何一条引用链上;且执行 finalize() 方法时,没有进行对象的自救。

    如果执行完第 12 步,且满足以上两种情况,同时对象仍然没有被使用,那么该对象就是垃圾。

1.2.5.1 finalize() 方法

finalize() 方法会在对象 第一次 进行垃圾回收时调用。

但是,如果对象没有重写 finalize() 方法,或者 JVM 已经调用过 finalize() 方法了,那么就没必要在第一次回收时再调用 finalize() 方法了。

如果 finalize() 方法在第一次回收时调用了,那么我们可以在对象的 finalize() 方法中进行 对象的自救

注意:finalize() 方法并不靠谱,Java 中不建议使用 finalize() 方法。

1.2.5.2 示例:通过 finalize() 方法实现对象的自救

在这里插入图片描述

1.3 如何回收垃圾

1.3.1 GC 类型
1.3.1.1 MinorGCYoungGC

发生在新生代的收集动作(GC)。

1.3.1.2 MajorGCOldGC

发生在老年代的 GC

目前只有 CMS 收集器会有单独收集老年代的行为。

1.3.1.3 MixedGC

收集整个新生代以及部分老年代。

目前只有 G1 收集器会有这种行为。

1.3.1.4 FullGC

收集整个 Java 堆和方法区的 GC

注意:方法区中也可以进行垃圾回收。

1.3.2 STWStop-The-World

STWJava 中的一种全局暂停的现象,多半是由 GC 引起的。

所谓全局暂停就是 Java 代码停止运行。不过 native 代码可以执行,但不能和 JVM 交互。

STW 的危害是长时间服务停止,没有响应。对于 HA 系统,可能引起主备切换,严重危害生产环境。

对虚拟机的优化,应该要尽量避免 STW,或者尽量缩减 STW 的耗时时长。

1.3.3 判定类无用的条件

判定类无用的条件有:

  1. JVM 中该类的所有实例都已经被回收;
  2. 加载该类的 ClassLoader 已经被回收;
  3. 没有任何地方引用该类的 Class 对象;
  4. 无法在任何地方通过反射访问这个类。
1.3.4 垃圾收集类型
1.3.4.1 串行收集

单个 GC 线程进行内存回收,会暂停所有的用户线程。

如:Serial

1.3.4.2 并行收集

多个 GC 线程进行内存回收,会暂停所有的用户线程。

如:Parallel

1.3.4.3 并发收集

GC 线程和用户线程同时执行(不一定是并行,可以是交替执行),不需要暂停用户线程。

如:CMS

1.3.5 垃圾回收算法
1.3.5.1 标记清除算法(Mark-Sweep

标记清除算法分为标记和清除两个阶段:

  1. 先标记出要回收的对象;
  2. 然后统一回收这些对象。

在这里插入图片描述

标记清除算法的优缺点:

  1. 优点:简单。

  2. 缺点:

    1. 效率不高,标记和清除的效率都不高;

    2. 标记清除后会产生大量不连续的内存碎片,从而导致在分配大对象时触发 GC

      被标记的对象可能存放在各个不连续的内存空间中,那么清除这些内存空间后,就会产生多个不连续的未使用的内存空间(即 内存碎片)。由于未使用的内存空间是不连续的,因此无法存放下需要连续内存空间的大对象,从而导致在分配大对象时仍然会触发 GC

1.3.5.2 复制算法(Copying,一般在新生代中使用)

复制算法:把内存分成两块完全相同的区域,每次使用其中一块。当一块使用完了,就把这块上还存活的对象拷贝到另一块中,然后把这块清除掉。

新生代中,存活区内的 from spaceto space 就是采用了复制算法。

在这里插入图片描述

复制算法的优缺点:

  1. 优点:实现简单,运行高效,避免了内存碎片的产生。

  2. 缺点:由于复制算法中,有一半的内存区域是保留的,所以造成了内存浪费。

JVM 实际实现中,是将内存分为一块较大的 Eden 区,和两块较小的 Survivor 区(fromto)。每次使用 Eden 和一块 Survivor。在回收时,把存活的对象复制到另一块 Survivor 中。

HotSpot 虚拟机默认的 EdenSurvivor 的比值是 8:1,也就是每次能用 90% 的新生代空间。

Eden:Survivor = 8:1,即 Eden:from:to = 8:1:1,
于是,可使用的新生代空间为:(Eden+from)/(Eden+from+to) = 9/10 = 90%

在回收时,把存活的对象复制到另一块 Survivor 时,如果发现另一块 Survivor 空间不够,就要依赖老年代进行 分配担保,让放不下的对象直接进入老年代。

1.3.5.2.1 分配担保

分配担保就是指:当新生代进行垃圾回收后,新生代的存活区放置不下,那么需要把这些对象放置到老年代中,此时,就需要担保老年代中有足够的内存空间来放置这些对象。

“新生代的存活区放置不下这些对象” 有两种情况:

  1. eden 区 + 一个 Survivor 区(from space),对这两个区进行回收后,其中存活下来的对象过多,无法全部放到另一个 Survivor 区(to space)中;

  2. eden 区 + 一个 Survivor 区(from space),对这两个区进行回收后,当新生代为新的对象分配内存空间时,无法在新生代中找到足够大的空间来存放这个较大的新对象。

以上两种情况中,无法放置到新生代中的这些对象,都需要将它们放置到老年代中去。

分配担保是一种策略,其过程如下:

  1. 在发生 MinorGC 前,JVM 会检查老年代的最大可用连续空间是否大于新生代中所有对象的总空间:

    1. 如果大于,那么可以确保 MinorGC 是安全的;

    2. 如果小于,那么 JVM 会继续检查是否设置了允许担保失败:

      1. 如果允许担保失败,那么继续检查老年代的最大可用连续空间是否大于历次放置到老年代中的对象的平均大小:

        1. 如果大于,那么尝试进行一次 MinorGC

        2. 如果不大于,那么改做一次 Full GC

1.3.5.3 标记整理算法(Mark-Compact,一般在老年代中使用)

由于复制算法在存活对象比较多的时候,效率较低、且有空间浪费,因此老年代一般不会选用复制算法,而是多选用标记整理算法。

标记整理算法的标记过程跟标记清除算法的标记过程一样,但标记整理算法的后续不是直接清除可回收的对象,而是让所有存活的对象都向一端移动,于是,端边界以外的内存中只留下了可回收的对象,此时直接清除端边界以外的内存即可。

在这里插入图片描述

2. 垃圾收集器

2.1 概述

垃圾收集算法是内存回收所采用的方法。而垃圾收集器则是垃圾收集算法的具体实现,通过垃圾收集器实现对内存的回收。

不同厂商、不同虚拟机版本中,垃圾收集器的实现有很大差别。

HotSpot 虚拟机中包含的垃圾收集器如下图所示:

在这里插入图片描述

其中:

  1. SerialSerial old 都是串行收集器;

  2. ParNewParallel old 都是并行收集器;

  3. G1 是新生代和老年代都可以使用的收集器;

  4. 新生代中的 Serial 收集器可以和老年代中的 Serial old 收集器组合使用;

  5. 新生代中的 ParNew 收集器可以和老年代中的 CMS 收集器组合使用;

  6. 新生代中的 Parallel Scavenge 收集器可以和老年代中的 Serial oldParallel old 收集器组合使用;

  7. 老年代中的 CMS 收集器可以和 Serial old 收集器配合使用。

    如:当 CMS 收集器无法正常使用时,使用 Serial old 收集器接替工作。

  8. G1 收集器既可以处理新生代的垃圾回收,又可以处理老年代的垃圾回收。

    所以 G1 收集器不需要和其他收集器组合使用。

2.2 串行收集器(Serial(新生代)/ Serial old(老年代))

在这里插入图片描述

串行收集器的说明:

  1. 新生代中的 Serial 收集器和老年代中的 Serial old 收集器都是串行收集器,串行收集器就是单线程的收集器;

  2. 在垃圾回收时,串行收集器会 STWStop-the-World)。

  3. 串行收集器的优点是简单,对于单 CPU 来说,由于没有多线程的交互开销,可能更高效。

  4. Serial 收集器是默认的 Client 模式下的新生代收集器。

  5. 通过设置参数 -XX:+UseSerialGC 开启串行收集器,此时,会采用 Serial + Serial old 的收集器组合。

  6. 新生代中的串行收集器 Serial 采用复制算法;老年代中的串行收集器 Serial old 采用标记整理算法。

2.2.1 示例:Serial + Serial old 组合的配置(-XX:+UseSerialGC

在这里插入图片描述

2.3 并行收集器(ParNew(新生代))

在这里插入图片描述

并行收集器 ParNew 的说明:

  1. ParNew 收集器是新生代中的并行收集器,与 Serial 收集器的最大区别是:ParNew 收集器会使用多个 GC 线程进行新生代中的垃圾回收;

  2. ParNew 收集器在垃圾回收时会 STWStop-the-World)。

  3. 在并发能力好的 CPU 环境中,ParNew 并行收集器的停顿时间要比 Serial 串行收集器短;但对于单 CPU 或并发能力较弱的 CPU,由于多线程的交互开销,ParNew 并行收集器可能比 Serial 串行收集器更差。

  4. 并行收集器 ParNewServer 模式下首选的新生代收集器;

  5. 新生代中的并行收集器 ParNew 通常与老年代中的 CMS 收集器组合使用

    当设置参数 -XX:+UseConcMarkSweepGC,指定老年代使用 CMS 收集器时,新生代就会使用 ParNew 收集器。

    (不再使用参数 -XX:+UseParNewGC 来单独开启 ParNew 收集器)

  6. 通过设置参数 -XX:ParallelGCThreads,指定并行收集器 ParNewGC 线程数。(最好与 CPU 数量一致)

  7. 并行收集器 ParNew 只在新生代中使用,采用复制算法。

2.3.1 示例:ParNew + CMS 组合的配置(-XX:+UseConcMarkSweepGC

在这里插入图片描述

2.4 并行收集器(Parallel Scavenge(新生代)/ Parallel old(老年代))

在这里插入图片描述

说明:

  1. 并行收集器 Parallel Scavenge 只在新生代中使用,采用复制算法;并行收集器 Parallel old 只在老年代中使用,采用标记整理算法。

  2. 新生代中的 Parallel Scavenge 并行收集器跟 ParNew 很类型,但 Parallel Scavenge 更关注吞吐量,能最高效率的利用 CPU,适合运行后台应用。

  3. 通过设置参数 -XX:+UseParallelGC,或者设置参数 -XX:+UseParallelOldGC,都会使用 Parallel Scavenge + Parallel old 的收集器组合。

  4. 通过设置参数 -XX:MaxGCPauseMillis,配置 GC 的最大停顿时间。

2.4.1 示例:Parallel Scavenge + Parallel old 组合的配置(-XX:+UseParallelGC-XX:+UseParallelOldGC

在这里插入图片描述

在这里插入图片描述

2.5 并发收集器(CMSConcurrent Mark and Sweep 并发标记清除)

前面介绍的收集器(SerialSerial oldParNewParallel ScavengeParallel old)都是串行收集器或并行收集器,而这里介绍的老年代中的 CMS 收集器则是 并发 收集器。其中:

  1. 所谓串行,就是指垃圾回收时,暂停用户线程,只开启一个 GC 线程进行垃圾回收;

  2. 所谓并行,就是指垃圾回收时,暂停用户线程,开启多个 GC 线程进行垃圾回收。

  3. 所谓并发,就是指垃圾回收时,不暂停 用户线程,可以开启多个 GC 线程进行垃圾回收。

    也就是说,“并发” 就意味着用户线程和 GC 线程可以同时运行。

另外,前面介绍的老年代中的 Serial oldParallel old 收集器都是采用标记整理算法,而老年代中的 CMS 收集器则是采用 标记清除算法

CMS 收集器的工作过程可划分为四个阶段:

  1. 初始标记阶段:只标记 GC Roots 能直接关联到的对象。

  2. 并发标记阶段:进行 GC Roots Tracing 的过程。

  3. 重新标记阶段:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象。

  4. 并发清除阶段:并发回收垃圾对象。

在这里插入图片描述

注意:

  1. CMS 的工作过程中,只有并发标记阶段和并发清除阶段中的线程是与用户线程同时运行的。在初始标记阶段和重新标记阶段中还是会暂停用户线程,即这两个阶段中可能会发生 STWStop-the-World)。

  2. 运行示意图中,最后的重置线程,指的是清空跟收集相关的数据并重置,为下一次收集做准备。

CMS 收集器的优缺点:

  1. 优点:低停顿、并发执行。

  2. 缺点:

    1. 并发执行,对 CPU 资源压力大;
    2. 无法处理在处理过程中产生的垃圾,可能导致 FullGC
    3. 采用的标记清除算法会导致大量的内存碎片,从而在分配大对象时可能触发 FullGC

CMS 收集器的相关参数配置:

  1. -XX:+UseConcMarkSweepGC:使用 ParNew + CMS + Serial old 的收集器组合。

    其中,Serial old 收集器将作为 CMS 出错时的后备收集器。

  2. -XX:CMSInitiatingOccupancyFraction:设置在老年代的内存空间被使用多少后触发 CMS 收集器进行垃圾回收。

    默认是 80%

2.6 G1 收集器(Garbage-First

2.6.1 G1 收集器的特点

G1 收集器是一款面向服务端应用的收集器,与其他收集器相比,具有如下特点:

  1. G1 收集器把内存划分为多个独立的区域(Region)。

  2. G1 收集器仍采用分代思想,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分 Region 的集合。且不需要 Region 是连续的。

    前面介绍的垃圾收集器都是采用分代思想,且在内存的物理结构上,将内存 划分为新生代和老年代。

  3. G1 收集器能充分利用多 CPU、多核环境硬件优势,尽量缩短 STW

  4. G1 收集器整体上采用标记整理算法,局部采用复制算法,不会产生内存碎片。

  5. GC 收集器的停顿可预测,能明确地指定:在一个时间段内,消耗在垃圾收集上的时间不能超过多长时间。

  6. G1 收集器跟踪各个 Region 里面垃圾堆的价值大小,在后台维护一个优先列表,每次根据允许的时间来回收价值最大的区域,从而保证在有限时间内的高效收集。

    所谓 “回收价值最大” 的区域,就是指垃圾对象存放最多的区域。因为回收这样的区域中的垃圾对象,释放后得到的可用内存空间最多。

  7. G1 收集器一般不会进行 FullGC。通常是进行整个新生代区域的回收,以及部分回收价值最大的老年代区域的回收。也就是进行 Mixed GC

2.6.2 G1 收集器定义的五种类型的区域(Region

G1 收集器把内存划分为多个独立的区域(Region),并定义了五种不同类型的 Region

  1. Eden regions:新生代的 Eden 区。

  2. Survivor regions:新生代的 Survivor 区。

  3. Old regions:老年代。

  4. Humongous regions:巨型对象区域,专门用来存储大对象。通常也被认为是老年代的一部分。

    G1 收集器认为只要对象的大小超过了一个 Region 容量的 1/2,即可判定为大对象。

在这里插入图片描述

2.6.3 G1 收集器的工作过程(四个阶段)

CMS 类似,G1 收集器的工作过程可划分为四个阶段:

  1. 初始标记阶段:只标记 GC Roots 能直接关联到的对象。

  2. 并发标记阶段:进行 GC Roots Tracing 的过程。

  3. 最终标记阶段:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象。

  4. 筛选回收阶段:根据时间来进行价值最大化的回收。

在这里插入图片描述

2.6.4 G1 收集器的相关参数配置
2.6.4.1 -XX:+UseG1GC(使用 G1 收集器)

开启并使用 G1 收集器。

JDK 13 中默认使用 G1 收集器。

2.6.4.2 -XX:MaxGCPauseMillis=<n>(最大 GC 停顿时间)

这里设置的最大 GC 停顿时间只是软件的目标,JVM 将尽可能让(但不保证)停顿小于这个时间。

2.6.4.3 -XX:InitiatingHeapOccupancyPercent=<n>

占用了多少 Java 堆内存时触发 GC,默认为 45

2.6.4.4 -XX:NewRatio=<n>

默认为 2

2.6.4.5 -XX:SurvivorRatio=<n>

默认为 8

2.6.4.6 -XX:MaxTenuringThreshold=<n>

新生代到老年代的岁数。默认是 15

15 表示新生代中的某个对象在经历了 15GC 后,仍然保留在新生代的存活区中,那么就会将该对象放到老年代中保存。

2.6.4.7 -XX:ParallelGCThreads=<n>

并行 GC 的线程数。默认值会根据平台不同而不同。

2.6.4.8 -XX:ConcGCThreads=<n>

并发 GC 使用的线程数。

2.6.4.9 -XX:G1ReservePercent=<n>

设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险。默认值是 10%

2.6.4.10 -XX:G1HeapRegionSize=<n>

设置 G1 区域的大小。值是 2 的幂,范围是 1MB32MB

目标是根据最小的 Java 堆大小划分出约 2048 个区域。

2.7 ZGC 收集器

ZGC 收集器是 JDK 11 加入的低延迟收集器(处于试验阶段)。

ZGC 收集器的设计目标是:

  1. 支持 TB 级内存容量;
  2. 暂停时间低(<10ms);
  3. 对整个程序吞吐量的影响小于 15%

ZGC 收集器中的新技术:

  1. 着色技术:
  2. 读屏障。

3. GC 性能指标

一款优秀的 GC 收集器,应该使得:

  1. 吞吐量很大;
  2. GC 负荷很小;
  3. 暂停时间几乎没有。

交互式应用通常希望暂停时间越少越好。

3.1 吞吐量

吞吐量 = 应用代码执行的时间 / 运行的总时间

其中,运行的总时间 = 应用代码执行时间 + GC 时间。

3.2 GC 负荷

与吞吐量相反,GC 负荷 = GC 时间 / 运行的总时间

3.3 暂停时间

暂停时间就是发生 STWStop-the-World)的总时间。

3.4 GC 频率

GC 频率就是 GC 在一个时间段内发生的次数。

3.5 反应速度

反应速度就是从对象成为垃圾到被回收的时间。

并不是说反应速度越快越好。有时,反应速度快,可能意味着频繁的 GC

4. JVM 内存分配原则

JVM 内存分配原则如下:

  1. 新生代尽可能设置得大点,如果太小会导致:

    1. YGC(新生代的垃圾回收)次数更加频繁;
    2. 可能导致 YGC 后的对象进入老年代,如果此时老年代满了,会触发 FGC(即 FullGC)。
  2. 对老年代,针对响应时间优先的应用:由于老年代通常采用并发收集器,因此老年代的大小要综合考虑并发量和并发持续时间等参数:

    1. 如果老年代的大小设置小了,可能会造成内存碎片,高回收频率会导致应用暂停;
    2. 如果老年代的大小设置大了,会需要较长的回收时间。
  3. 对老年代,针对吞吐量优先的应用:通常设置较大的新生代和较小的老年代。

    这样可以尽可能回收大部分短期对象,减少中期对象,而老年代尽量存放长期存活的对象。

  4. 依据对象的存活周期进行分类,对象优先分配在新生代中,长时间存活的对象则分配在老年代中。

  5. 根据不同代的特点,选取合适的收集算法:

    1. 少量对象存活,适合复制算法;
    2. 大量对象存活,适合标记清除算法,或标记整理算法。

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

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

相关文章

docker容器下php框架laravel的使用问题与解决方案

DB_CONNECTIONmysqlDB_HOSTlocalhost DB_CONNECTIONmysqlDB_HOSTdocker33-mysql-1 容器中只有数据库结构 进入MySQL容器内&#xff0c;创建表结构&#xff0c;添加数据 代码层面需要转换成数组 $query->get([*])->toArray(); 分页数据框架会返回带有data的数据&#xf…

【QT+QGIS跨平台编译】之六:【LZMA+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、lzma介绍二、文件下载三、文件分析四、pro文件五、编译实践一、lzma介绍 LZMA(Lempel-Ziv-Markov chain-Algorithm的缩写),是一个Deflate和LZ77算法改良和优化后的压缩算法。 libLzma是基于LZMA压缩算法封装的开源库。2001年被首次应用于7-Zip压缩工具中,是 …

vscode设置terminal的最大行数

今天跑代码出现一个问题&#xff0c;就是整个程序跑完&#xff0c;整个程序的输出信息过多&#xff0c;最开始输出的信息已经被vscode的缓存冲掉了&#xff0c;只能看到最后的一部分&#xff0c;具体的原因是vscode的terminal默认只能保存1000行的信息&#xff0c;所以如果想保…

C# 获取QQ会话聊天信息

目录 利用UIAutomation获取QQ会话聊天信息 效果 代码 目前遇到一个问题 其他解决办法 利用UIAutomation获取QQ会话聊天信息 效果 代码 AutomationElement window AutomationElement.FromHandle(get.WindowHwnd); AutomationElement QQMsgList window.FindFirst(Tr…

大模型实战营Day5笔记

大模型部署背景 大模型部署是指将训练好的模型在特定的软硬件环境中启动的过程&#xff0c;使模型能够接收输入并返回预测结果。大模型的内存开销巨大&#xff0c;7B模型仅权重需要14G内存。另外大模型是自回归生成&#xff0c;需要缓存Attention的 k/v。 LMDeploy 简…

HCIA——23DNS层次域名空间、域名服务器、域名解析的原理的选择、解答

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

Oracle 19c 配置并启用PDB数据库

一、问题发现 在Oracle 19c多租户环境中&#xff0c;默认情况下&#xff0c;使用startup命令启动数据库实例后&#xff0c;你会发现PDB数据库的状态为MOUNT状态,PDB不会随着CDB启动而启动。 二、查看当前使用的数据库类型 使用sqlplus以管理员身份登录数据库 sqlplus / as s…

Git教程-搭建服务器上GitBlit代码仓库(超详细)

在日常编码时&#xff0c;我常常会有将代码保存到云端的需求&#xff0c;因此我选择将 Git 安装在我的 云服务器上&#xff0c;通过在服务器上搭建 GitBlit 服务&#xff0c;配合本地使用 TortoiseGit 上传代码&#xff0c;实现跨设备的云端代码版本管理。本篇文章将介绍如何在…

Camera 基础知识

一、Camera 基础知识 1.1 Camera 工作原理 外部光线穿过镜头Lens 后&#xff0c; 经过 Color Filter 滤波后照射到CMOS Sensor 上&#xff0c; CMOS Sensor 将从 Lens 上传导过来的光线转换为电信号&#xff0c;再通过内部的AD模数转换&#xff0c;转换为数字信号。如果 Sens…

unity项目《样板间展示》开发:火焰和UI设计

第二章&#xff1a;火焰和UI设计 前言一、火焰模型管理灶台火焰壁炉火焰 二、电视机播放三、UI设计结语 前言 这次带大家从0到1做一个unity项目&#xff1a;《样板间展示》。 顾名思义&#xff0c;项目内容是展示样板间&#xff0c;即玩家可以与房间中的物体、家具进行交互。 至…

【网络安全 -> 防御与保护】信息安全概述

目录 一、信息安全现状及挑战 二、信息安全脆弱性及常见安全攻击 1、网络环境的开放性 2、协议栈的脆弱性及常见攻击 3、操作系统的脆弱性及常见攻击 4、终端的脆弱性及常见攻击 5、其他常见攻击 三、信息安全要素 四、整体安全解决方案 一、信息安全现状及挑战 &…

51单片机LED点阵屏

LED点阵屏 LED点阵屏是一种由许多小型LED灯组成的矩阵式显示屏。这些LED灯可以是单色、双色或全彩的&#xff0c;它们排列成行和列的网格&#xff0c;可以根据需要点亮来显示图像、文字或动画等内容。LED点阵屏广泛应用于户外广告牌、室内显示、交通信号灯、电子价格标签和其他…

gradle打包分离依赖jar

正常打包的jar是包含项目所依赖的jar包资源&#xff0c;而且大多数场景下的依赖资源是不会频繁的变更的&#xff0c;所以实际把项目自身jar和其所依赖的资源分离可以实现jar包瘦身&#xff0c;减小上传的jar包总大小&#xff0c;能实现加速部署的效果 一 原本结构 二 配置buil…

CSS 蜡烛效果

<template><view class="holder"><!-- 身子 --><view class="candle"><!-- 光源 --><view class="blinking-glow"></view><!-- 火星子 --><view class="thread"></view>…

【Python时序预测系列】基于Holt-Winters方法实现单变量时间序列预测(源码)

一、引言 Holt-Winters是一种经典的时序序列预测方法&#xff0c;用于对具有季节性和趋势性的数据进行预测。在这种方法中&#xff0c;使用三个组件来建模时序数据&#xff1a;趋势&#xff08;Trend&#xff09;、季节性&#xff08;Seasonality&#xff09;和残差&#xff0…

项目实战——Qt实现FFmpeg音视频转码器

文章目录 前言一、移植 FFmpeg 相关文件二、绘制 ui 界面三、实现简单的转码四、功能优化1、控件布局及美化2、缩放界面3、实现拖拽4、解析文件5、开启独立线程6、开启定时器7、最终运行效果 五、附录六、资源自取 前言 本文记录使用 Qt 实现 FFmepg 音视频转码器项目的开发过…

k8s的helm

1、在没有helm之前&#xff0c;部署deployment、service、ingress等等 2、helm的作用&#xff1a;通过打包的方式&#xff0c;deployment、service、ingress这些打包在一块&#xff0c;一键部署服务、类似于yum功能 3、helm&#xff1a;官方提供的一种类似于仓库的功能&#…

空气净化器or宠物空气净化器?五款猫用空气净化器优质推荐!

作为一个养猫家庭的主人&#xff0c;每天都要面对清理猫砂盘的挑战&#xff0c;这种令人难以形容的气味实在让人难以忍受。尤其是家里有小孩和老人&#xff0c;他们可能会出现过敏性鼻炎等问题&#xff0c;而抵抗力较差的人更容易受到影响。此外&#xff0c;换毛季节到来时&…

Android状态栏布局隐藏的方法

1.问题如下&#xff0c;安卓布局很不协调 2.先将ActionBar设置为NoActionBar 先打开styles.xml 3.使用工具类 package com.afison.newfault.utils;import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graph…