Java 垃圾回收

在这里插入图片描述
一、概述

GC

GC(Garbage Collection),在程序运行过程中内存空间是有限的,为了更好的的使用有限的内存空间,GC会将不再使用的对象清除然后将其所占用的内存释放出来。

java的垃圾回收机制

Java的垃圾收集(Garbage Collection, GC)是Java虚拟机(JVM)提供的一种自动管理机制,用于回收和再分配无任何引用的对象所占用的内存
Java的垃圾收集机制是Java语言一个显著的特点,它可以有效防止内存泄漏,保证内存的有效复用。
Java的垃圾收集机制最初由Sun在JDK 1.0中提出,经过多次更新和优化,现在的JVM(如HotSpot)采用了多种算法,如标记-清除(Mark-Sweep)、(Copying)等。

各版本默认GC

# cmd命令行查看Java8的GC
java -XX:+PrintCommandLineFlags -version# 输出:
-XX:InitialHeapSize=132397312 // JVM默认初始化堆大小
-XX:MaxHeapSize=2118356992 //JVM堆的默认最大值
-XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC //Java8使用的GC类型
java version "1.8.0_20" //使用的java版本
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)

结果分析:由结果可以看出Java8的GC情况是:-XX:+UseParallelGC,即Parallel Scavenge(新生代) + Parallel Old(老生代),实际上几个主流Java版本的GC情况如下:

  • jdk1.7
    默认垃圾收集器Parallel Scavenge(新生代【标记-复制算法】)+Parallel Old(老年代【标记整理算法】)
  • jdk1.8
    默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
  • jdk1.9 及以上
    默认垃圾收集器G1【从局部(两个Region之间)来看是基于"标记—复制"算法实现,从整体来看是基于"标记-整理"算法实现】

GC详细情况

# cmd命令行查看Java8的GC详细情况
java -XX:+PrintGCDetails -version# 输出:
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
HeapPSYoungGen      total 38400K, used 2678K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)eden space 33280K, 8% used [0x00000000d5e00000,0x00000000d609dbc0,0x00000000d7e80000)from space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)to   space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)ParOldGen       total 87552K, used 0K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)object space 87552K, 0% used [0x0000000081a00000,0x0000000081a00000,0x0000000086f80000)Metaspace       used 2257K, capacity 4480K, committed 4480K, reserved 1056768Kclass space    used 244K, capacity 384K, committed 384K, reserved 1048576K

二、对象回收判定方式

当一个对象被创建时,虚拟机会优先分配到堆空间中,当对象不再被使用了,虚拟机会对其进行回收处理,以便释放内存空间,这个过程也被称为垃圾对象回收。

那么如何找到对象是否可以进行回收呢?一般有两种方式。

  • 引用计数法

  • 可达性分析法

下面我们一起来了解下相关知识。

2.1、引用计数法

这个方法的实现思路是:在对象中维护一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。当对象的计数器值为 0,表示这个对象不再被使用了,可以被回收。

这种方法使用场景很多,但很少有垃圾收集器会使用这种方式。

原因在于:这种方式存在一个致命的缺陷,比如堆中的两个对象相互引用,此时他们的计数器值是 1,但这两个对象并没有被外部使用,因此不会被回收,容易造成内存泄露。

2.2、可达性分析法

这个方法的实现思路是:从“GC Roots”(这个 GC Roots 可以是栈中的引用变量,也可以是方法区的引用变量或常量)开始扫描堆中的对象,沿着 GC Roots 一路扫描,被扫描的所有对象全部标记为存活对象;扫描完成之后,没有被标记的视为垃圾对象,可以被回收。

比如对象 A 被线程占中的变量 a 引用着,对象 A 中引用着对象 B,对象 B 又引用着 C 等,沿着 a 开始扫描,会扫描到对象 A,B,C 等,并把它们标记为存活对象。全部扫描完成之后,当一个对象到 GC Roots 没有任何引用链时,表示此对象是不可用的,等待被 GC 回收。
在这里插入图片描述

在 JVM 中,可以作为 GC Roots 的对象包括:

  • 虚拟机栈中引用的对象

  • 方法区中静态属性引用的对象

  • 方法区中常量引用的对象

  • 本地方法栈中 JNI(即 Native 方法)引用的对象

三、垃圾回收算法

当一个对象被判定为垃圾对象之后,剩下的工作就是如何进行回收了。

下面我们一起来看看常见的几种垃圾回收算法的思想。

3.1、标记-清除算法

标记-清除算法如同它的名字一样,分为“标记”和“清除”两个阶段,也是最基础的算法。

首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

这个算法也有很多的不足,主要体现在效率和空间。

  • 从效率的角度讲,标记和清除两个过程的效率都不高;

  • 从空间的角度讲,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致后面的程序运行过程中分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作

标记-清除算法执行过程,可以用如下图来概括:

在这里插入图片描述

3.2、复制算法

复制算法是为了解决效率问题而出现的,它将可用的内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

这样每次只需要对整个半区进行内存回收,内存分配时也不需要考虑内存碎片等复杂情况,只需要移动指针,按照顺序分配即可。

这个算法也有缺点,操作的时候内存会缩小为了原来的一半,代价很高;其次,持续复制长生存期的对象会导致回收效果不佳,效率较低。

一般的商用虚拟机会采用这种算法来回收新生代(也称为年轻代)的对象,不过研究表明1:1的比例不是很科学,因此新生代的内存空间被细划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor;每次回收时,将 Eden 和 Survivor 空间中还存活的对象一次性复制到另外一块 Survivor 空间上,最后清理掉之前的 Eden 和 Survivor 空间。

HotSpot 虚拟机默认 Eden 和 Survivor 区的比例是8 : 1 : 1,期望每次回收后只有不到 10% 的对象存活,如果出现 Survivor 空间不够用时,需要依赖老年代进行分配担保。

复制算法执行过程,可以用如下图来概括:

在这里插入图片描述

3.3、标记-压缩算法

在上面我们提到了复制算法的优点和缺点,针对对象存活率较高的场景,进行大量的复制操作时,效率很低下。如果不想浪费 50% 的空间,当对象 100% 存活时,那么需要有额外的空间进行分配担保。

在 HotSpot 虚拟机中,堆空间划分成两个不同的区域:新生代老年代,目的是为了更有效率的回收对象。新生代的对象存活率低,会优先被回收,如果多次执行依然没有被回收,就会转移到老年代。老年代都是不易被回收的对象,对象存活率高,因此一般不能直接选用复制算法。

根据老年代的特点,有人提出了另外一种标记-整理算法,也称为标记-压缩算法,过程与标记-清除算法一样,不过不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界外的内存。

标记-整理算法执行过程,可以用如下图来概括:

在这里插入图片描述

3.4、分代收集算法

分代收集算法,可以看成以上内容的延伸。它的实现思路是根据对象的生命周期的不同,将内存划分为几块,比如把堆空间划分为新生代老年代,然后根据各块的特点采用最适当的收集算法。

在新生代中,存在大批对象死去、少量对象存活的特点,会采用复制算法,只需要付出少量存活对象的复制成本就可以完成垃圾对象收集,效率高;在老年代中,存在对象存活率高、没有额外空间对它进行分配担保的特点,会采用**“标记-清理”或者“标记-整理”**算法来进行回收。

可以用如下图来概括堆内存的空间布局:

在这里插入图片描述

四、垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

不同的虚拟机所提供的垃圾收集器可能会有很大差异,以 HotSpot 虚拟机为例,所包含的垃圾收集器可以用如下图来概括。
在这里插入图片描述

上图中的连线表示,不同分代的收集器可以搭配使用。

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

  • 老年代收集器:Serial Old、CMS、Parallel Old

  • 通用收集器: G1

在虚拟机中,没有所谓的万能收集器,只有根据具体的业务场景,选择最合适的收集器。这也是为什么 HotSpot 实现了这么多收集器的原因。

下面我们一起来看看相关的具体实现。

4.1、Serial 和 Serial Old收集器

Serial 系列的垃圾收集器是 JVM 的第一款收集器,它的设计思路很简单,在新生代,使用单线程采用复制算法进行收集对象;在老年代,使用单线程采用标记整理算法进行收集对象;垃圾收集的过程中会暂停用户线程,直到垃圾收集完毕。

因为当时的硬件环境配置都不高,内存都是几十兆,CPU 也都是单核的,不像现在这样处处都是高并发的应用场景。限于当时的硬件资源和应用场景,这个收集器优势很突出,简单高效、消耗资源也很少。

唯一的不足在于,在用户不可见的情况下要把用户正常工作的线程全部停掉,这对很多应用比较难以接受。不过实际上到目前为止,Serial 收集器依然是虚拟机在 Client 模式下运行的默认新生代收集器,因为它简单而高效。客户端应用模型下,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代对象,停顿时间平均在几十毫秒,只要不是频繁收集,完全可以接受。

整个流程,可以用如下图来概括。
在这里插入图片描述

总结下来,收集器特点如下:

  • 收集区域: Serial(新生代),Serial Old(老年代)

  • 收集算法: Serial(复制算法),Serial Old(标记整理算法)

  • 收集方式:单线程

  • 优势:简单高效,内存资源占用少,单核 CPU 环境最佳选项

  • 劣势:整个搜集过程需要停顿用户线程,多核 CPU、大内存的环境,资源优势无法发挥起来

4.2、ParNew收集器

ParNew 收集器,可以看成是 Serial 收集器的多线程版本。除了使用多线程进行垃圾收集外,其余行为和 Serial 收集器完全一样,包括使用的也是复制算法,垃圾收集时暂停用户线程。在多核 CPU 资源环境下,可以显著提升整个垃圾收集的性能,也是虚拟机在 Server 模式下运行的首选新生代收集器。

能让 ParNew 出名的一个核心因素是,它是除了 Serial 收集器外,目前唯一一个能与 CMS 收集器配合一起使用的新生代收集器,因为 CMS 优秀所以 ParNew 也出名了,有点类似碰上了大款的感觉,其中 CMS 收集器是一款几乎可以认为有划时代意义的垃圾收集器,下文我们再讲。

其次,ParNew 收集器在单个 CPU 的环境中绝对不会有比 Serial 收集器更好的效果,甚至由于线程交互的开销,该收集器在两个 CPU 的环境中都不能百分之百保证可以超越 Serial 收集器。当然,随着可用 CPU 数量的增加,它对于垃圾收集的效率提升还是很有帮助的。

整个流程,可以用如下图来概括。

在这里插入图片描述

总结下来,收集器特点如下:

  • 收集区域:新生代

  • 收集算法:复制算法

  • 收集方式:多线程

  • 优势:多线程收集,多核 CPU 环境下效率要比 serial 高,新生代中,除了 Serial 收集器外目前唯一一个能与 CMS 配合的收集器

  • 劣势:整个搜集过程需要停顿用户线程

4.3、Parallel Scavenge 和 Parallel Old收集器

Parallel Scavenge 和 ParNew 收集器很类似,也是一款使用多线程采用复制算法的新生代收集器;Parallel Old 收集器是一款使用多线程采用标记整理算法的老年代收集器;垃圾收集过程中也会暂停用户线程,直到整个垃圾收集过程结束。

不同的是,Parallel 收集器更关注系统的吞吐量,也被称为“吞吐量优先收集器”。

所谓吞吐量的意思就是 CPU 用于运行用户代码时间与 CPU 总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如虚拟机总运行 100 分钟,垃圾收集 1 分钟,那吞吐量就是 99%。高吞吐量可以高效率的利用 CPU 资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

自适应调节策略也是 Parallel Scavenge 与 ParNew 的一个重要区别,用户可以通过参数来打开自适应调节策略,比如-XX:+UseAdaptiveSizePolicy参数,打开之后就不需要手动指定新生代大小、Eden 区和 Survivor 参数等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。如果对于垃圾收集器运作原理不太了解,优化比较困难的情况下,使用 Parallel 收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成也是一个不错的选择。

另外,Parallel 收集器是虚拟机在 Server 模式下运行的默认垃圾收集器。

整个执行流程,跟 ParNew 收集器类似。

总结下来,收集器特点如下:

  • 收集区域:Parallel Scavenge(新生代),Parallel Old(老年代)

  • 收集算法:Parallel Scavenge(复制算法),Parallel Old(标记整理算法)

  • 收集方式:多线程

  • 优势:多线程收集,多核 CPU 环境下效率要比 serial 高

  • 劣势:整个搜集过程需要停顿用户线程

4.4、CMS收集器

CMS 收集器是一种以获取最短回收停顿时间为目标的老年代收集器

与前面几个收集器不同,它采用了一种全新的策略可以在垃圾回收过程中的某些阶段用户线程和垃圾回收线程一起工作,从而避免了因为长时间的垃圾回收而使用户线程一直处于等待之中。

目前很大一部分 Java 应用集中在互联网站或者 B/S 系统的服务端上,这类应用尤其注重服务的响应速度,希望系统停顿时间最短,比如在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过多少毫秒,以期给用户带来较好的体验,其中 CMS 收集器就非常符合这类应用的需求。

CMS 的英文全程是:Concurrent Mark-Sweep Collector,从名字上就能看出 CMS 收集器是基于“标记-清除”算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为如下 4 个步骤:

  • 初始标记

  • 并发标记

  • 重新标记

  • 并发清除

CMS 会根据每个阶段不同的特性来决定是否停顿用户线程。整个流程,可以用如下图来概括。(图片来自于勤劳的小手 - 垃圾收集器文章)

在这里插入图片描述

4.4.1、阶段一:初始标记

初始标记阶段的工作主要是标记一下 GC Roots 能直接关联到的对象,这个过程会短暂的停顿用户线程,因为并不会对整个 GC Roots 的引用进行遍历,因此速度很快。

4.4.2、阶段二:并发标记

并发标记阶段的工作主要是把阶段一标记好的 GC Roots 对象进行深度的遍历,找到所有与 GC Roots 关联的对象并进行标记,这个过程会采用多线程的方式进行遍历标记,因为非常耗时,CMS 考虑到为了尽量不停顿用户线程,因此这个阶段不会暂停用户线程,也就是说,此时 JVM 会分配一些资源给用户线程执行任务,通过这样的方式减少用户线程的停顿时间。

4.4.3、阶段三:重新标记

重新标记阶段的工作主要是修补阶段二用户线程运行期间产生新的垃圾对象,进行重新标记,同样也是采用多线程方式进行,此阶段数量不会很多,会短暂的停顿用户线程,速度也很快。

4.4.4、阶段四:并发清除

并发清除阶段的工作主要是对那些被标记为可回收的对象进行清理,在一般情况下,并发清除阶段是使用的是“标记-清除”算法,因为这个过程不会牵扯到对象的地址变更,所以 CMS 在并发清除阶段是不需要停止用户线程的,对象回收效率非常高。

与此同时,正因为并发清除阶段用户线程也可以同时运行,所以在用户线程运行的过程中自然也会产生新的垃圾对象,这也就是导致 CMS 收集器会产生“浮动垃圾”的原因,此时也会产生很多的空间碎片,当空间碎片到达了一定程度时,此时 CMS 就会使用“标记-整理”算法来解决空间碎片的问题。

在上文的垃圾回收算法中我们有说到,“标记-整理”算法会将对象的位置进行挪动并更新对象的引用的指向地址,在这个过程中,如果用户线程同时运行的话会产生并发问题,因此当 CMS 进行碎片整理的时候必须得停止用户线程。所以,在某些情况下,并发清除阶段 CMS 也会停顿用户线程。

CMS 收集器作为一个全新思路的垃圾收集器,虽然很优秀,但一直没有被 Hospot 虚拟机纳入为默认的垃圾收集器。时至今日,JDK1.8 使用的默认收集器都还是 Parallel scavenge 和 Parallel old 收集器,主要原因在于 CMS 存在一些比较头疼的问题,比如浮动垃圾、空间碎片整理时会造成系统卡顿、在并发清除阶段可能会出现系统长时间的假死。

4.4.5、小结

总结下来,收集器特点如下:

  • 收集区域:老年代

  • 收集算法:标记清除算法 + 标记整理算法

  • 收集方式:多线程

  • 优势:多线程收集过程中可以做到不停止用户线程,以获取最短回收停顿时间

  • 劣势:会产生浮动垃圾、空间碎片整理时会造成系统卡顿、并发清除阶段可能会出现系统假死等问题

4.5、G1收集器

G1(Garbage-First)收集器是当今收集器技术发展的最前沿成果之一,从 JDK 7 Update 4 后开始进入商用。

在 G1 收集器出现之前,不管是 Serial 系列,Parallel 系列,还是 CMS 收集器,它们都是基于把内存进行物理分区的形式将 JVM 内存分成新生代、老年代、永久代或 MetaSpace,这种分区模式下进行垃圾收集时必须对某个区域进行整体性的收集,比如整个新生代、整个老年代收集或者整个堆,当内存空间不大的时候,比如几个 G,通过参数优化能取得不错的收集性能。但是,随着硬件资源的发展,JVM 可用内存从几十 G 到几百 G 甚至上 T 时,这种采用传统模式下的物理分区进行收集时,每次扫描内存的区域自然就变大了,进行垃圾清理的时间自然就变得更长了,此时传统的收集器即时再怎么优化,也难以取得令人满意的收集效果,因此需要一款全新的垃圾收集器。

G1 收集器就是在这样的环境下诞生的,它摒弃了原来的物理分区,把整个 Java 堆分成若干个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离,它们都是一部分 Region 的集合。从结构上看,G1 收集器不要求整个新生代或者老年代都是连续的,也不再坚持固定大小和固定数量,它会跟踪各个 Region 里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。这种通过 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限的时间内可以获取尽可能高的收集效率。

G1 收集器内存划分,可以用如下图来概括。(图片来自于勤劳的小手 - 垃圾收集器文章)
在这里插入图片描述

在 G1 收集器里面维护了一个 Collect Set 集合,这个集合里面记录了待回收的 Region 区域信息,同时也包括了每个 Region 区域可回收的大小空间。通过 Collect Set 里面的信息,G1 在进行垃圾收集时,可以根据用户设定的可接受停顿时间来进行分析,在设定的时间范围内优先收集垃圾最多的 Region 区域,以实现高吞吐、低停顿的收集效果。

在工作流程上,G1 收集器也吸收了 CMS 很多优秀的收集思路,整个垃圾收集过程,可以分为如下 4 个步骤:

  • 初始标记

  • 并发标记

  • 重新标记

  • 筛选回收

G1 收集器的垃圾回收流程和 CMS 逻辑大致相同,主要的区别在最后一个阶段,G1 不会直接进行清除,而是会根据设置的停顿时间进行智能的筛选和局部的回收,采用“标记复制”算法来实现。

整个流程,可以用如下图来概括。
在这里插入图片描述

4.5.1、阶段一:初始标记

此阶段的工作内容与上文介绍的 CMS 收集器一样,会先把所有 GC Roots 直接引用的对象进行标记,同时会短暂的停止用户线程,因为不会对整个 GC Roots 的引用进行遍历,因此速度比较快。

4.5.2、阶段二:并发标记

此阶段的工作内容与上文介绍的 CMS 收集器也一样,找到所有与 GC Roots 关联的对象并进行深度遍历标记,会采用多线程的方式进行遍历标记,因为比较耗时,为了尽量不停顿用户线程,这个阶段 GC 线程会和用户线程同时运行,通过这样的方式减少用户线程的停顿时间。

4.5.3、阶段三:重新标记

此阶段的工作内容与上文介绍的 CMS 收集器也是一样,针对阶段二用户线程运行的过程中产生新的垃圾,采用多线程方式进行重新标记,为了避免这个过程再次产生新的垃圾对象,会短暂的停止用户线程,因为数量不会很多,因此速度比较快。

4.5.4、阶段四:筛选回收

筛选回收阶段的工作主要是把存活的对象复制到 Region 空闲区域,同时会根据 Collect Set 记录的可回收 Region 信息进行筛选,计算 Region 回收成本,接着根据用户设定的停顿时间值制定回收计划,最后根据回收计划筛选合适的 Region 区域进行垃圾回收。

从局部来看,G1 使用的是复制算法,将存活对象从一个 Region 区域复制到另一个 Region 空闲区域;但从整个堆来看,G1 使用的逻辑又相当于标记整理算法,每次垃圾收集时会把存活的对象整理到对应可用的 Region 区域,再把原来的 Region 区域标记为可回收区域并记录到 Collect Set 中,因此 G1 的每一次回收都可以看作是一次标记整理过程,两者都不会产生空间碎片问题。

4.5.5、小结

总结下来,收集器特点如下:

  • 收集区域:整个堆内存

  • 收集算法:复制算法

  • 收集方式:多线程

  • 优势:停顿时间可控,吞吐量高,不会产生空间碎片,不需要额外的收集器搭配

  • 劣势:目前而言,相较于 CMS,G1 还不具备全方位、压倒性优势,G1 在收集过程中内存占用和执行负载都偏高;其次,在小内存应用上 CMS 的表现大概率会优于 G1,而 G1 在大内存应用上会比较有优势,6G 以上的内存可以考虑使用 G1 收集器

4.6、常用的收集器组合

最后我们对以上介绍的垃圾收集器进行一次汇总,同时介绍一下服务器端常用的组合模式,内容如下。

服务器组合新生代收集器老年代收集器备注
组合一SerialSerial OldSerial 是一个使用单线程采用复制算法的新生代收集器;Serial Old 是一个使用单线程采用标记整理算法的老年代收集器,GC 时会暂停所有应用线程,可以使用-XX:+UseSerialGC选项来开启
组合二ParNewSerial OldParNew 是一个使用多线程采用复制算法的新生代收集器,GC 时会暂停所有应用线程,可以使用-XX:+UseParNewGC选项来开启
组合三Parallel ScavengeSerial OldParallel Scavenge 是一个使用多线程采用复制算法的新生代收集器,GC 时会暂停所有应用线程,可以使用-XX:+UseParallelGC选项来开启;需要注意的是,在jdk1.7及之前的版本中,这个参数默认采用 Serial Old 作为老年代收集器;在jdk1.8及之后的版本中,默认采用 Parallel Old 作为老年代收集器
组合四Parallel ScavengeParallel OldParallel Old是 Serial Old 的多线程版收集器,可以使用-XX:+UseParallelOldGC选项来开启
组合五SerialCMS + Serial OldCMS 是一个使用多线程采用标记清楚算法的老年代收集器,可以实现 GC 线程和用户线程并发工作,不需要暂停所有用户线程;另外,可以将 Serial Old 收集器作为备选,当 CMS 进行 GC 失败时,会自动使用 Serial Old 进行 GC;可以使用-XX:+UseConcMarkSweepGC选项来开启
组合六ParNewCMS + Serial OldParNew 是除了 Serial 以外,唯一一个能搭配 CMS 的新生代收集器;可以使用-XX:+UseConcMarkSweepGC开启,默认使用 ParNew 作为新生代收集器,也可以通过-XX:+UseParNewGC强制指定 ParNew
组合七G1G1G1 是一个新一代的垃圾收集器,摒弃了原来的物理分区,把整个 Java 堆分成若干个大小相等的独立区域(Region),针对局部区域使用多线程采用复制算法进行筛选回收,可以使用-XX:+UseG1GC选项来开启

五、方法区回收

以上介绍的都是对象的回收过程,在之前的 JVM 内存结构的文章中我们介绍到,Java 应用程序运行时,除了堆空间会存在垃圾数据以外,方法区同样也存在。

虽然虚拟机规范中没有明确要求方法区一定要实现垃圾回收,主要原因在于这个区域的垃圾回收效率非常低,但是 HotSpot 虚拟机对方法区也会进行回收的,主要回收的是废弃常量无用的类两部分。

如何判断一个常量是否为“废弃常量”呢?其实很简单,只要当前系统中没有任何一处引用该常量,就会被判定为废弃常量。

如何判断一个类是否为“无用的类”呢?条件非常苛刻,需要同时满足以下三点。

  • 1.该类所有实例都已经被回收,也就是说 Java 堆中不存在该类的任何实例

  • 2.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

  • 3.加载该类的 ClassLoader 已经被回收,也就是说这个类的类加载器被卸载回收了

满足以上三个条件则表示这个类再也无用了,HotSpot 虚拟机会对此类进行回收。例如在大量使用反射、动态代理、CGLib 等 ByteCode 框架,并自定义 ClassLoader 创建的类,为了保证方法区不会溢出,虚拟机会在适当的情况下对无用的类进行回收。

在 JDK1.7 及以前的版本中,用永久代来作为方法区的实现,当永久代的空间不足时会触发 Full GC。

在 JDK1.8 及之后的版本中,用元空间来作为方法区的实现,元空间的内存空间默认使用的是操作系统的内存空间,它的垃圾回收不再由 Java 来控制,元空间的内存管理由元空间虚拟机来完成。

好文分享,一起加油!

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

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

相关文章

嵌入式Linux复制剪切删除指令详解

指令操作 1. cp 复制指令 a. 用法:cp [ 选项 ] [ 源文件或目录 ] [ 目标文件或目录 ]; b. 用途:用于复制文件或目录; c. 通常情况下,复制的都不是空文件夹,所以直接使用 cp 复制空文件会失败&#xff0…

臭氧浓度传感器在食品厂与制药厂中的应用

在食品厂和制药厂的生产过程中,消毒是一个至关重要的环节。有效的消毒可以确保产品免受微生物污染,从而保障消费者的健康。近年来,臭氧作为一种广谱杀菌剂,因其强效的消毒能力和低污染性,在食品厂和制药厂的消毒过程中…

SpringMVC:创建一个简单的SpringMVC框架

目录 一、框架介绍 两个重要的xml文件 SpringMVC执行流程 二、Vscode搭建SpringMVC框架 1、maven创建webapp原型项目 2、pom.xml下添加springmvc的相关依赖 3、在web.xml配置 4、springmvc.xml的配置 5、编写Controller控制器类 6、 编写JSP界面 7、项目结构图 一…

VS2017中使用qt翻译家,除ui界面外其他用tr包裹的字符串在翻译家中显示为乱码

1、ui界面中的中文,可以正常显示 2、其他用tr包裹的字符串,显示为乱码 3、解决 改为utf8保存。 然后更新翻译文件,重新打开发现已经ok了。 参考博客: https://blog.csdn.net/zhou714534957/article/details/124948822 https://blog.csdn.net/weixin_52689816/article/d…

PAT-1004 成绩排名(java实现)

这一关感觉还没第三关难,思路很清晰 题目 1004 成绩排名 读入 n(>0)名学生的姓名、学号、成绩,分别输出成绩最高和成绩最低学生的姓名和学号。 输入格式: 每个测试输入包含 1 个测试用例,格式为 第 1 行…

【算法】宵暗的妖怪

✨题目链接: 宵暗的妖怪 ✨题目描述 露米娅作为宵暗的妖怪,非常喜欢吞噬黑暗。这天,她来到了一条路上,准备吞噬这条路上的黑暗。这条道路一共被分为n 部分,每个部分上的黑暗数量为ai 。露米娅每次可以任取 连续的 未被…

赚钱其实没有秘密,多琢磨一下不丢人

为什么学了很多知识还是挣不到钱? 挣不到钱,是因为你不够稀缺;挣钱太少,是因为你不懂杠杆,用杠杆撬动稀缺,个人价值自然水涨船高。 学富五车,为何财库依旧空空?怎样才能提高挣钱的…

在全志H616核桃派开发板上配置SSH远程终端方法详解

熟悉指令用户可以对已经联网的核桃派进行局域网SSH远程终端控制,方便使用自己的PC对核桃派远程进行各种指令操作。 普通用户(默认) 账号:pi ; 密码:pi管理员账户 账号:root ; 密码:root 在这之…

在Android Studio中使用谷歌Gemini代码助手

今天在做android开发的时候,一个项目使用到了gradle8.0,但是我的Android Studuio根本不支持,无可奈何只能从小蜜蜂版本升级了水母 | 2023.3.1版本,但突然发现AS已经集成了Gemini助手。 首先我们需要下载这个版本的: h…

2.5Bump Mapping 凹凸映射

一、Bump Mapping 介绍 我们想要在屏幕上绘制物体的细节,从尺度上讲,一个物体的细节分为:宏观、中观、微观宏观尺度中其特征会覆盖多个像素,中观尺度只覆盖几个像素,微观尺度的特征就会小于一个像素宏观尺度是由顶点或…

《java数据结构》--一篇解决二叉搜索树!!

😸二叉搜索树的概念 二叉搜索树又名二叉排序树,一般具有以下性质: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉…

61. UE5 RPG 实现敌人近战攻击技能和转向攻击

在前面,我们实现了敌人的AI系统,敌人可以根据自身的职业进行匹配对应的攻击方式。比如近战战士会靠近目标后进行攻击然后躲避目标的攻击接着进行攻击。我们实现了敌人的AI行为,但是现在还没有实现需要释放的技能,接下来&#xff0…

通过定时器和脉冲控制LED

目录 一、定时器 (一)定时器简介 (二)定时器类型 1、常见定时器 2、定时器的主要功能 3、常规定时器 (三)定时器配置 1、定时器标准外设库接口函数 2、定时器标准外设库配置 二、PWM &#xff08…

匿名函数(lambda)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 匿名函数是指没有名字的函数,应用在需要一个函数,但是又不想费神去命名这个函数的场合。通常情况下,这样的函数只…

【Qt】Qt界面美化指南:深入理解QSS样式表的应用与实践

文章目录 前言:1. 背景介绍2. 基本语法3. QSS 设置方式3.1. 设置全局样式3.2. 从文件加载样式表3.3. 使用 Qt Designer 编辑样式 总结: 前言: 在当今这个视觉至上的时代,用户界面(UI)的设计对于任何软件产…

智能制造案例专题|与MongoDB一起解锁工业4.0转型与增长的无限潜力!

MongoDB 智能制造 数字化技术的洪流在各个产业链的主干和枝节涌现。在工业制造领域,能否通过数字化技术实现各生产要素、生产环节之间的紧密配合,高效规划、管理整个生产流程,是企业提升韧性、赢得竞争的关键。随着工业4.0的深入发展和智能…

高级Java开发者的自我修养:深入剖析JVM垃圾回收机制及面试要点

在探索Java虚拟机(JVM)的奥秘过程中,垃圾回收机制(GC)是一个不可或缺的话题,尤其在面对大型应用和系统优化时显得尤为重要。JVM的自动内存管理是Java编程语言中一项革命性的特性,它大大简化了程…

AI盒子在智慧加油站的应用

方案背景 为规范加油站作业,保障人民生命财产安全,《加油站作业安全规范》(AQ 3010-2007)中第五条规定:卸油作业基本要求,明确防静电、防雷电、防火、人员值守、禁止其他车辆及非工作人员进入卸油区。 痛点…

RocketMQ学习(2) 深入学习

RokcetMQ的介绍和基础知识见这篇博客——RocketMQ学习(1) 快速入门 本篇为上一篇的深入学习,很多基础知识不再赘述。 目录 消息重复消费问题(去重;幂等)布隆过滤器 重试机制死信消息 SpringBoot集成RocketMQ集成SpringBoot发送不同消息模式(同步消息)异步消息单向消…

使用el-tab,el-tab-pane循环使用循环后不显示下划线问题

在vue项目中使用element-UI el-tab里的el-tab-pane是循环出来的&#xff0c;但是循环出来后选中tab不显示下划线了 文章目录 问题问题展示效果问题代码问题原因 解决方案解决后效果解决方案1代码 解决方案2代码 问题 问题展示效果 问题代码 <el-tabs v-model"activeNa…