likes
comments
collection
share

JVM-垃圾收集器及垃圾回收算法

作者站长头像
站长
· 阅读数 16

本文是JVM系列第四篇

垃圾收集器的分类

Java虚拟机中的垃圾收集器可以按照不同的标准进行分类,常见的分类方式有以下几种:

根据分代

根据Java堆内存分代的情况,将垃圾收集器分为新生代垃圾收集器和老年代垃圾收集器。 新生代垃圾收集器主要负责回收新生代内存区域中的垃圾,而老年代垃圾收集器则主要负责回收老年代内存区域中的垃圾。 常见的新生代垃圾收集器有Serial、ParNew、Parallel Scavenge等,常见的老年代垃圾收集器有Serial Old、Parallel Old、CMS等。

根据算法

根据垃圾收集算法的不同,将垃圾收集器分为标记-清除、复制、标记-整理等不同类型。

  • 标记-清除算法主要用于清理老年代内存区域中的垃圾
  • 复制算法主要用于清理新生代内存区域中的垃圾
  • 标记-整理算法则主要用于清理老年代内存区域中的垃圾。

常见的垃圾收集器有Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1等。

根据区域

根据Java虚拟机内存区域的不同,将垃圾收集器分为新生代垃圾收集器老年代垃圾收集器和永久代垃圾收集器。在JDK 8及以后的版本中,永久代被元数据区(Metaspace)所取代,因此没有针对永久代的垃圾收集器。 JVM-垃圾收集器及垃圾回收算法 不同的垃圾收集器在回收垃圾时,具有不同的优缺点和适用场景,可以根据具体的应用场景和需求选择合适的垃圾收集器。

垃圾回收算法的分类

ava虚拟机中常见的垃圾回收算法可以按照不同的标准进行分类,常见的分类方式有以下几种:

标记-清除算法

标记-清除算法(Mark-Sweep)是一种最基本的垃圾回收算法。 它将垃圾回收分为两个阶段:标记阶段和清除阶段。在标记阶段,垃圾回收器会标记所有仍然被引用的对象;在清除阶段,垃圾回收器会清除所有没有被标记的对象。该算法的缺点是会产生大量的碎片空间,影响内存的利用率。

复制算法

复制算法(Copying)是一种常用于新生代的垃圾回收算法。它将新生代划分为两个大小相等的空间,每次只使用其中的一个空间。当一个空间被用满后,垃圾回收器会将其中存活的对象复制到另一个空间中,并将原空间中的所有对象一次性清除。该算法的优点是实现简单、运行高效,缺点是需要两倍的内存空间。

标记-整理算法

标记-整理算法(Mark-Compact)是一种用于老年代的垃圾回收算法。 它将垃圾回收分为三个阶段:标记阶段、整理阶段和清除阶段。

  • 在标记阶段,垃圾回收器会标记所有仍然被引用的对象;
  • 在整理阶段,垃圾回收器会将所有存活的对象向一端移动,然后释放端边界之外的所有空间;
  • 在清除阶段,垃圾回收器会清除所有没有被标记的对象。

该算法的优点是可以避免碎片空间的产生,缺点是需要移动存活对象,影响垃圾回收的效率。

分代算法

分代算法(Generational)是一种常用的垃圾回收算法。它基于一个经验:大部分对象很快就会被回收,只有少部分对象会长期存活。因此,将Java堆分为新生代和老年代两个区域,分别采用不同的垃圾回收算法,以达到优化垃圾回收性能的目的。

不同的垃圾回收算法在实现上有各自的优缺点和适用场景,可以根据具体的应用场景和需求选择合适的垃圾回收算法。

垃圾收集器的实现

Java虚拟机中的垃圾收集器的实现是基于垃圾回收算法的具体实现。垃圾收集器的实现通常包括以下几个方面:

  1. 内存分配:内存分配是垃圾收集器的基本功能之一。垃圾收集器需要实现内存分配算法,用于在Java堆中分配内存空间。常见的内存分配算法有指针碰撞算法和空闲列表算法等。
  2. 垃圾回收:垃圾收集器需要实现垃圾回收算法,用于清理Java堆中的垃圾对象。常见的垃圾回收算法有标记-清除算法、复制算法、标记-整理算法等。
  3. 内存管理:垃圾收集器需要管理Java堆中的内存空间,包括空间的分配、释放和合并等。垃圾收集器还需要实现内存的压缩和整理功能,以避免内存碎片的产生。
  4. 线程安全:垃圾收集器需要保证线程安全,以避免多个线程同时操作Java堆中的对象导致的竞争和错误。
  5. 性能优化:垃圾收集器需要实现性能优化功能,包括并发垃圾收集、增量垃圾收集、压缩垃圾收集等。这些功能可以提高垃圾收集器的效率和吞吐量,降低垃圾收集带来的系统开销和影响。

新生代垃圾收集器

Serial

Serial是Java虚拟机中最基本、最古老的垃圾收集器之一,它是一种单线程、串行执行的垃圾收集器。它的主要特点是简单、高效,适用于小型应用和客户端应用程序,也适用于单处理器系统和低内存环境。

Serial垃圾收集器主要用于新生代的垃圾回收。它采用复制算法进行垃圾回收,将新生代内存分为Eden区、Survivor0区和Survivor1区三个部分。在垃圾回收过程中,Serial垃圾收集器会暂停应用程序的执行,扫描Eden区和Survivor区中的对象,将存活的对象复制到另一个Survivor区中,并将原来的Survivor区清空。当一个Survivor区被用满后,Serial垃圾收集器会将其中的存活对象复制到老年代中。

JVM-垃圾收集器及垃圾回收算法

ParNew

ParNew是一种新生代并行垃圾收集器,是Serial垃圾收集器的改进版。它采用复制算法进行垃圾回收,将新生代内存分为Eden区、Survivor0区和Survivor1区三个部分,与Serial垃圾收集器类似。与Serial垃圾收集器不同的是,ParNew垃圾收集器可以并行地进行垃圾回收,利用多线程来提高垃圾回收的效率和吞吐量。

ParNew垃圾收集器的主要特点如下:

  1. 并行执行:ParNew垃圾收集器可以使用多个线程并行地执行垃圾回收,提高垃圾回收的效率和吞吐量。
  2. 可控制的暂停时间:ParNew垃圾收集器可以通过控制线程数和垃圾回收阈值等参数来控制垃圾回收的暂停时间,从而减少对应用程序的影响。
  3. 与CMS配合使用:ParNew垃圾收集器通常与CMS垃圾收集器配合使用,用于新生代的垃圾回收。
  4. 支持增量收集:ParNew垃圾收集器支持增量垃圾收集,可以在垃圾回收过程中不断地执行应用程序,减少对应用程序的影响。

需要注意的是,ParNew垃圾收集器只适用于多核CPU和大内存的环境,对于单核CPU和小内存的环境可能会影响应用程序的性能。同时,ParNew垃圾收集器也有一定的局限性,例如无法避免内存碎片的产生等。因此,开发人员需要根据具体的应用场景和需求选择合适的垃圾收集器。

JVM-垃圾收集器及垃圾回收算法

Parallel Scavenge

Parallel Scavenge是一种新生代并行垃圾收集器,它是JDK 5.0中引入的一种垃圾收集器,用于取代早期版本中的Serial收集器。与Serial收集器相比,Parallel Scavenge具有更高的吞吐量和更短的垃圾收集时间,并且可以利用多核CPU的优势进行并行垃圾收集。

Parallel Scavenge垃圾收集器的主要特点如下:

  1. 并行执行:Parallel Scavenge垃圾收集器可以使用多个线程并行地执行垃圾回收,提高垃圾回收的效率和吞吐量。
  2. 分代收集:Parallel Scavenge垃圾收集器适用于新生代的垃圾回收,它采用复制算法进行垃圾回收,将新生代内存分为Eden区、Survivor0区和Survivor1区三个部分。
  3. 可控制的吞吐量:Parallel Scavenge垃圾收集器可以通过控制垃圾回收时间和吞吐量等参数来控制垃圾回收的效率和吞吐量。它适用于对响应时间不敏感,但对吞吐量有要求的应用程序。
  4. 自适应调节:Parallel Scavenge垃圾收集器可以根据应用程序的负载情况自动调节垃圾回收的参数,以达到最佳的性能和吞吐量。

需要注意的是,Parallel Scavenge垃圾收集器对内存的要求较高,它需要大量的内存空间来存储对象,并且对内存的使用和分配有一定的限制。在内存不足或分配不当的情况下,Parallel Scavenge垃圾收集器可能会导致Full GC的频繁发生,影响应用程序的性能。因此,在选择垃圾收集器时,需要根据具体的应用场景和需求进行选择。

JVM-垃圾收集器及垃圾回收算法

老年代收集器

Serial Old

Serial Old是一种单线程的老年代垃圾收集器,它是Serial垃圾收集器的扩展,用于对老年代的垃圾进行收集。与Serial垃圾收集器类似,Serial Old垃圾收集器采用标记-整理算法进行垃圾回收。

Serial Old垃圾收集器的主要特点如下:

  1. 单线程执行:Serial Old垃圾收集器只使用一个线程进行垃圾回收,因此无法进行并发垃圾回收,需要暂停应用程序的执行。
  2. 简单高效:Serial Old垃圾收集器的实现简单、高效,适用于小型应用和客户端应用程序,也适用于单处理器系统和低内存环境。
  3. 分代收集:Serial Old垃圾收集器适用于老年代的垃圾回收,它与Serial垃圾收集器一起组成了一种串行的垃圾收集器组合,用于对整个堆进行垃圾回收。
  4. 可预测的暂停时间:Serial Old垃圾收集器可以预测垃圾回收的暂停时间,因此可以在一定程度上减少对应用程序的影响。

需要注意的是,Serial Old垃圾收集器适用于小型应用和客户端应用程序,对于大型应用和服务器端应用程序来说,由于它只使用一个线程进行垃圾回收,可能导致垃圾回收时间过长,影响应用程序的响应时间和吞吐量。因此,在选择垃圾收集器时,需要根据具体的应用场景和需求进行选择。 在服务器模式下应用场景:

  • 与 Parallel Scavenge 收集器搭配使用
  • 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用

Parallel Old

Parallel Old是一种并行的老年代垃圾收集器,它是Parallel Scavenge垃圾收集器的扩展,用于对老年代的垃圾进行并行垃圾回收。与Parallel Scavenge垃圾收集器类似,Parallel Old垃圾收集器采用标记-整理算法进行垃圾回收。

Parallel Old垃圾收集器的主要特点如下:

  1. 并行执行:Parallel Old垃圾收集器可以使用多个线程并行地执行垃圾回收,利用多核CPU的优势提高垃圾回收的效率和吞吐量。
  2. 高效率:Parallel Old垃圾收集器采用复制算法进行垃圾回收,可以在短时间内清理大量的垃圾,适用于对吞吐量有要求的应用程序。
  3. 可控制的暂停时间:Parallel Old垃圾收集器可以通过控制垃圾回收时间和吞吐量等参数来控制垃圾回收的暂停时间,从而减少对应用程序的影响。
  4. 分代收集:Parallel Old垃圾收集器适用于老年代的垃圾回收,它与Parallel Scavenge垃圾收集器一起组成了一种并行的垃圾收集器组合,用于对整个堆进行垃圾回收。

需要注意的是,Parallel Old垃圾收集器对内存的要求较高,它需要大量的内存空间来存储对象,并且对内存的使用和分配有一定的限制。在内存不足或分配不当的情况下,Parallel Old垃圾收集器可能会导致Full GC的频繁发生,影响应用程序的性能。因此,在选择垃圾收集器时,需要根据具体的应用场景和需求进行选择。

CMS

CMS(Concurrent Mark Sweep)是一种并发的垃圾收集器,用于对老年代的垃圾进行收集。CMS垃圾收集器采用标记-清除算法进行垃圾回收,可以在不暂停应用程序的情况下进行垃圾回收,以减少对应用程序性能的影响。 CMS(Concurrent Mark Sweep)垃圾收集器的工作流程通常包括以下四个步骤:

  1. 初始标记(Initial Mark):在该阶段中,CMS垃圾收集器会暂停应用程序的执行,标记所有老年代中的可达对象,以确定哪些对象需要进行垃圾回收。
  2. 并发标记(Concurrent Mark):在该阶段中,CMS垃圾收集器会与应用程序并发执行,继续标记所有老年代中的可达对象,以确定哪些对象需要进行垃圾回收。由于该阶段与应用程序并发执行,因此不会对应用程序的性能产生明显的影响。
  3. 重新标记(Remark):在该阶段中,CMS垃圾收集器会暂停应用程序的执行,重新标记所有在并发标记阶段中发生修改的对象,以保证回收的对象是准确的。由于该阶段的时间通常比初始标记阶段短,因此暂停时间也相对较短。
  4. 并发清除(Concurrent Sweep):在该阶段中,CMS垃圾收集器会与应用程序并发执行,清除所有未标记的老年代对象,以释放空间。由于该阶段与应用程序并发执行,因此不会对应用程序的性能产生明显的影响。

需要注意的是,CMS垃圾收集器在进行垃圾回收时需要占用一定的CPU资源和内存空间,因此在资源有限的情况下可能会影响应用程序的性能。此外,由于CMS垃圾收集器采用标记-清除算法进行垃圾回收,可能会产生内存碎片,需要定期进行Full GC来进行内存整理。因此,在选择垃圾收集器时,需要根据具体的应用场景和需求进行选择。

G1

G1(Garbage First)垃圾收集器是一种面向服务端应用程序的并发垃圾收集器,它旨在为大型堆(通常大于6GB)提供高吞吐量和可预测的低延迟。G1垃圾收集器采用分代收集和标记-整理算法进行垃圾回收,可以在不暂停应用程序的情况下进行垃圾回收。 G1(Garbage First)垃圾收集器与传统的垃圾收集器有以下几个不同点:

  1. 分代收集:G1垃圾收集器采用分代收集的方式,将整个堆分成多个大小相等的区域(Region),每个区域既可以用于存储新生代对象,也可以用于存储老年代对象。这种分代的方式可以更有效地管理多个区域,以提高垃圾回收效率。
  2. 并发执行:G1垃圾收集器可以在不暂停应用程序的情况下进行垃圾回收,采用并发标记和并发整理的方式进行垃圾回收,以减少对应用程序性能的影响。
  3. 可控制的暂停时间:G1垃圾收集器可以通过控制垃圾回收的目标时间和吞吐量等参数来控制垃圾回收的暂停时间,从而减少对应用程序的影响。
  4. 基于区域的回收:G1垃圾收集器采用基于区域(Region)的回收策略,每次只回收一部分区域,以减少回收时对整个堆的扫描和整理时间。同时,G1垃圾收集器会优先回收垃圾最多的区域,以提高垃圾回收的效率。
  5. 低延迟:G1垃圾收集器具有低延迟的特点,可以在短时间内完成垃圾回收,适用于对响应时间有要求的应用程序。

ZGC

ZGC(Z Garbage Collector)是一种用于JVM的新型垃圾收集器,它旨在为大型堆(多达数TB)提供低延迟和高吞吐量。ZGC垃圾收集器采用了分代收集可达性分析算法,可以在不到10ms的时间内完成垃圾回收,同时最大程度地减少了应用程序的停顿时间和吞吐量损失。 ZGC垃圾收集器采用了分代收集和可达性分析算法进行垃圾回收,主要分为以下几个步骤:

  1. 初始标记(Initial Mark):在该阶段中,ZGC垃圾收集器会暂停应用程序的执行,标记所有根对象和部分可达对象,以确定哪些对象需要进行垃圾回收。
  2. 并发标记(Concurrent Mark):在该阶段中,ZGC垃圾收集器会与应用程序并发执行,继续标记所有可达对象,以确定哪些对象需要进行垃圾回收。由于该阶段与应用程序并发执行,因此不会对应用程序的性能产生明显的影响。
  3. 并发预清理(Concurrent Preclean):在该阶段中,ZGC垃圾收集器会与应用程序并发执行,扫描所有存活对象的引用,以准备下一步的并发标记。
  4. 最终标记(Final Mark):在该阶段中,ZGC垃圾收集器会暂停应用程序的执行,标记所有在并发标记阶段中发生修改的对象,以保证回收的对象是准确的。由于该阶段的时间相对较短,因此暂停时间也相对较短。
  5. 并发清理(Concurrent Sweep):在该阶段中,ZGC垃圾收集器会与应用程序并发执行,清理所有未标记的对象,以释放空间。由于该阶段与应用程序并发执行,因此不会对应用程序的性能产生明显的影响。
  6. 并发整理(Concurrent Defragmentation):在该阶段中,ZGC垃圾收集器会与应用程序并发执行,对不连续的内存空间进行整理,以减少内存碎片。由于该阶段与应用程序并发执行,因此不会对应用程序的性能产生明显的影响。

JDK版本默认垃圾收集器

jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)。 jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)。 jdk1.9 默认垃圾收集器G1。

垃圾收集器JVM参数设置

JDK提供了多种垃圾收集器可供选择,同时也提供了一些相关的参数用于调整垃圾收集器的行为。下面是一些常见的JDK垃圾收集器相关参数设置:

  1. -XX:+UseSerialGC:使用Serial收集器,它是一种单线程的垃圾收集器,适用于小型应用程序或者客户端应用程序。
  2. -XX:+UseParallelGC:使用Parallel收集器,它是一种并行的垃圾收集器,适用于多CPU的服务器应用程序。
  3. -XX:+UseParallelOldGC:使用Parallel Old收集器,它是Parallel收集器的老年代版本,适用于多CPU的服务器应用程序。
  4. -XX:+UseConcMarkSweepGC:使用CMS收集器,它是一种基于标记-清除算法的并发垃圾收集器,适用于对响应时间有要求的应用程序。
  5. -XX:+UseG1GC:使用G1收集器,它是一种面向服务端应用程序的垃圾收集器,适用于大型堆和多CPU的服务器应用程序。
  6. -XX:+UseZGC:使用ZGC收集器,它是一种面向大型堆和低延迟的垃圾收集器。

除了上述垃圾收集器的参数设置外,还有一些其他的相关参数可以用于调整垃圾收集器的行为,例如:

  1. -Xmx:设置Java堆的最大大小。
  2. -Xms:设置Java堆的初始大小。
  3. -XX:NewSize:设置新生代的初始大小。
  4. -XX:MaxNewSize:设置新生代的最大大小。
  5. -XX:SurvivorRatio:设置新生代中Eden区域与Survivor区域的比例。
  6. -XX:MaxTenuringThreshold:设置对象进入老年代的年龄阈值。

参考: 5、垃圾收集器 深入理解Java中的18种JVM垃圾收集器原理【一万字】