JVM工作原理与实战(二十六):堆的垃圾回收-垃圾回收器

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了垃圾回收器、Serial垃圾回收器、SerialOld垃圾回收器、ParNew垃圾回收器、CMS垃圾回收器、Parallel Scavenge垃圾回收器、Parallel Old垃圾回收器等内容。

一、垃圾回收器介绍

知识点回顾:

在Java等虚拟机中,垃圾回收(GC)是一个重要的内存管理机制。为了更有效地进行垃圾回收,分代GC算法将堆内存分为年轻代和老年代。这种划分的依据是对象的存活周期,大部分对象在创建后很快就不再使用,可以被回收。例如,用户获取订单数据,订单数据返回给用户之后就可以被释放。这种短暂存活的对象被放置在年轻代中。另一方面,老年代中存放的是长期存活的对象。例如,Spring框架中的大部分bean对象,在程序启动后就不会被回收。这些对象由于存活时间长,通常较大且稳定,因此被放置在老年代中。

将堆分成年轻代和老年代的主要原因是:

  • 灵活性:通过调整年轻代和老年代的比例,可以更好地适应不同类型的应用程序,从而提高内存的利用率和性能。
  • 算法选择:新生代和老年代使用不同的垃圾回收算法。新生代通常采用复制算法,而老年代可以选择标记-清除或标记-整理算法。这种灵活性为程序员提供了更多选择。
  • 减少STW时间:分代设计允许仅回收新生代(minor gc),如果满足对象分配要求,就不需要对整个堆进行回收(full gc)。这有助于减少STW时间,提高应用的响应性。

垃圾回收器是Java虚拟机(JVM)中的重要组件,负责自动管理内存,回收不再使用的对象所占用的空间。了解垃圾回收器的种类、工作原理以及如何根据应用场景选择合适的垃圾回收器,对于提高应用程序的性能和稳定性至关重要。


垃圾回收器通过自动检测和回收不再被引用的对象,以释放内存空间,避免内存泄漏。为了实现这一目标,垃圾回收器采用了一系列算法来识别和回收无用对象。这些算法主要包括标记清除复制标记整理分代垃圾回收等。


垃圾回收器分为年轻代和老年代,它们各自负责不同生命周期的对象的回收。除了G1垃圾回收器外,其他垃圾回收器必须成对组合使用,以确保整个堆内存的有效管理。

image.gif

二、主要的垃圾回收器

1.年轻代-Serial垃圾回收器

年轻代-Serial垃圾回收器是一种针对年轻代内存区域进行管理的单线程串行回收器。它采用复制算法,将活跃对象从一个内存区域复制到另一个内存区域,从而实现内存空间的回收。

image.gif

Serial垃圾回收器在单CPU处理器环境下表现非常出色,具有较高的吞吐量。它能够有效地管理年轻代内存区域,确保垃圾回收过程的高效执行。由于其单线程特性,Serial垃圾回收器在多CPU环境下可能无法充分利用系统资源,导致吞吐量下降。此外,如果堆内存大小配置不当,可能会导致用户线程长时间的等待,影响程序的性能。


适用场景:Serial垃圾回收器适用于Java编写的客户端程序或者硬件配置有限的场景。对于需要快速响应的应用程序,Serial垃圾回收器可能是一个不错的选择,因为它能够提供较快的垃圾回收速度,从而减少应用程序的停顿时间。此外,在硬件资源有限的环境下,Serial垃圾回收器也能够提供较好的性能表现。

2.老年代-SerialOld垃圾回收器

老年代-SerialOld垃圾回收器是Serial垃圾回收器在老年代内存管理领域的特定实现。它采用单线程串行回收方式,确保老年代内存区域的稳定和高效管理。SerialOld垃圾回收器主要采用标记-整理算法,针对老年代内存区域进行垃圾回收。该算法通过标记无用对象并进行整理,有效地回收内存空间并保持内存区域的稳定。

image.gif

SerialOld垃圾回收器在单CPU处理器环境下表现出色,具有较高的吞吐量。它可以有效地管理老年代内存区域,确保垃圾回收过程的高效执行。然而,在多CPU环境下,由于其单线程特性,性能可能会受到限制,吞吐量不如其他并行垃圾回收器。此外,如果堆内存大小配置不当,可能会导致用户线程长时间的等待,影响程序的性能。

适用场景:SerialOld垃圾回收器通常与Serial垃圾回收器成对使用,以提供完整的年轻代和老年代管理方案。通过使用-XX:+UseSerialGC参数,可以确保新生代和老年代都使用串行回收器进行垃圾回收。在某些特殊情况下,如在CMS垃圾回收器的特殊情况下,也可能需要使用SerialOld来确保老年代内存的有效管理。

-XX:+UseSerialGC

image.gif

3.年轻代-ParNew垃圾回收器

年轻代-ParNew垃圾回收器是一种针对年轻代内存区域进行管理的垃圾回收器,它在多CPU环境下对Serial垃圾回收器进行了优化。ParNew垃圾回收器采用多线程技术,利用多个处理器核心同时进行垃圾回收操作,以提高垃圾回收的并行性和吞吐量。ParNew垃圾回收器主要采用复制算法,将活跃对象从一个内存区域复制到另一个内存区域,从而实现内存空间的回收。

image.gif

在多CPU环境下,由于其多线程特性,ParNew垃圾回收器能够有效地降低停顿时间,提高系统的响应能力。ParNew垃圾回收器也存在一些缺点,与G1垃圾回收器相比,ParNew在吞吐量和停顿时间方面可能存在一定的不足。因此,在JDK9及之后的版本中,官方已经不建议使用ParNew垃圾回收器。

适用场景:年轻代-ParNew垃圾回收器主要适用于JDK8及之前的版本。通过使用-XX:+UseParNewGC参数,可以指定新生代使用ParNew回收器,老年代则使用串行回收器。在JDK8及之前的版本中,ParNew垃圾回收器通常与CMS老年代垃圾回收器配合使用,提供全面的年轻代和老年代内存管理方案。在某些特定的应用场景下,对于需要较低停顿时间和较高吞吐量的应用程序,ParNew垃圾回收器可能是一个不错的选择。

-XX:+UseParNewGC

image.gif

4.老年代- CMS(Concurrent Mark Sweep)垃圾回收器

老年代- CMS垃圾回收器是一种关注系统暂停时间的垃圾回收器,主要针对老年代内存区域进行管理,允许用户线程和垃圾回收线程在某些步骤中同时执行。它采用标记清除算法,旨在减少用户线程的等待时间,提高系统响应能力。

image.gif

CMS垃圾回收器的执行步骤包括:

  1. 初始标记:这一阶段使用极短的时间标记出GC Roots直接关联的对象,以确定垃圾回收的起始点。
  2. 并发标记:在这一阶段,垃圾回收线程与用户线程同时执行,标记所有存活的对象。这样可以在不影响用户线程执行的情况下完成对象标记工作。
  3. 重新标记:由于并发标记阶段可能存在对象变化的情况,需要进行重新标记,以确保准确的垃圾回收。
  4. 并发清理:清理已经标记为死亡的对象,同时用户线程仍可以继续执行。


CMS垃圾回收器的主要优势在于其关注系统暂停时间,通过并发执行和减少用户线程等待时间来提高用户体验。此外,CMS还具有内存碎片少的特点,能够提供更为连续的内存空间。然而,CMS垃圾回收器也存在一些缺点和问题:

  • 内存碎片问题:CMS使用标记清除算法,会在垃圾收集后产生大量的内存碎片。虽然CMS会在Full GC时进行碎片整理,但整理过程会导致用户线程暂停,影响系统性能。可以通过调整参数-XX:CMSFullGCsBeforeCompaction(默认0)来控制碎片整理的频率(N次Full GC后再整理)。
  • 无法处理浮动垃圾:在并发清理过程中可能产生“浮动垃圾”,这些垃圾无法被完全回收,导致内存空间的浪费。
  • 退化问题:当老年代内存不足无法分配对象时,CMS会退化成Serial Old单线程回收老年代,这可能导致性能下降。
  • 线程资源争抢问题:在并发阶段运行时的线程数是由系统计算得出的,如果CPU核数有限,可能会影响用户线程执行的性能。可以通过调整参数-XX:ConcGCThreads(默认0)来控制并发阶段运行的线程数。计算公式为(-XX:ParallelGCThreads定义的线程数 + 3) / 4, ParallelGCThreads是STW停顿之后的并行线程数,ParallelGCThreads是由处理器核数决定的(当cpu核数小于8时,ParallelGCThreads = CPU核数,否则 ParallelGCThreads = 8 + (CPU核数 – 8 )*5/8 )。

适用场景:老年代- CMS垃圾回收器适用于大型的互联网系统中用户请求数据量大、频率高的场景。通过使用-XX:+UseConcMarkSweepGC参数,可以启用CMS垃圾回收器。在这些场景下,系统对响应时间要求较高,而CMS垃圾回收器能够通过减少用户线程等待时间来提高系统响应能力。

-XX:+UseConcMarkSweepGC

image.gif

5.年轻代-Parallel Scavenge垃圾回收器

Parallel Scavenge是JDK8默认的年轻代垃圾回收器,专注于提升系统的吞吐量。它采用多线程并行回收的方式,旨在最大限度地提高应用程序在垃圾回收过程中的执行效率。Parallel Scavenge具备自动调整堆内存大小的特点,可以根据应用程序的需求动态调整内存分配,以获得更好的性能。Parallel Scavenge主要针对年轻代进行管理,采用复制算法进行垃圾回收。在多线程并行回收的机制下,Parallel Scavenge能够显著提高垃圾回收的效率,从而提高系统的吞吐量。

image.gif

Parallel Scavenge的优点在于其高吞吐量以及手动可控的特点。为了提高吞吐量,虚拟机会动态调整堆的参数,以满足应用程序的性能需求。这种自动调整内存大小的功能有助于提高系统的适应性和性能。Parallel Scavenge也存在一些缺点,由于其关注的是系统的吞吐量,而不是单次的停顿时间,因此可能会在某些情况下导致较长的停顿时间。


适用场景:Parallel Scavenge主要适用于后台任务,这些任务不需要与用户频繁交互,并且容易产生大量的对象。例如,大数据的处理、大文件的导出等任务就非常适合使用Parallel Scavenge垃圾回收器。在这些场景下,Parallel Scavenge能够通过提高系统吞吐量来提高任务的执行效率。

Parallel Scavenge允许手动设置最大暂停时间和吞吐量。Oracle官方建议在使用这个组合时,不要设置堆内存的最大值,垃圾回收器会根据最大暂停时间和吞吐量自动调整内存大小。

  • 最大暂停时间:-XX:MaxGCPauseMillis=n设置每次垃圾回收时的最大停顿毫秒数。
  • 吞吐量:-XX:GCTimeRatio=n设置吞吐量为n(用户线程执行时间 = n/n + 1)。
  • 自动调整内存大小:-XX:+UseAdaptiveSizePolicy设置可以让垃圾回收器根据吞吐量和最大停顿的毫秒数自动调整内存大小。
-XX:MaxGCPauseMillis=n
-XX:GCTimeRatio=n
-XX:+UseAdaptiveSizePolicy

image.gif

6.老年代-Parallel Old垃圾回收器

Parallel Old垃圾回收器是为Parallel Scavenge收集器设计的老年代版本,专门用于管理老年代内存区域。它利用多线程并发收集技术,旨在提高老年代垃圾回收的效率和吞吐量。Parallel Old采用标记-整理算法对老年代进行管理。在多线程并发收集的机制下,Parallel Old能够充分利用多核CPU的计算能力,提高垃圾回收的效率。

image.gif

Parallel Old的优点在于其并发收集的特性,使得在多核CPU环境下表现出较高的效率。通过并发收集,Parallel Old能够降低垃圾回收对应用程序的影响,提高系统的吞吐量。Parallel Old也存在一些缺点,由于其采用标记-整理算法,垃圾回收过程中可能需要暂停用户线程,导致较长的停顿时间。此外,Parallel Old可能不太适合内存容量非常大的系统,因为在大内存环境下,垃圾回收的开销可能会增加。

适用场景:Parallel Old通常与Parallel Scavenge收集器配套使用。通过使用-XX:+UseParallelGC-XX:+UseParallelOldGC参数,可以启用Parallel Scavenge和Parallel Old的组合。这种组合适用于后台任务和大数据处理的场景,其中应用程序可以容忍较长的停顿时间,并希望通过提高吞吐量来提高任务的执行效率。通过使用Parallel Scavenge和Parallel Old的组合,应用程序可以在多核CPU环境下获得更好的性能表现。

-XX:+UseParallelGC
-XX:+UseParallelOldGC

image.gif


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了垃圾回收器、Serial垃圾回收器、SerialOld垃圾回收器、ParNew垃圾回收器、CMS垃圾回收器、Parallel Scavenge垃圾回收器、Parallel Old垃圾回收器等内容,下一节将会介绍G1垃圾回收器,希望对大家有所帮助。

相关文章
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
62 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
29天前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
49 2
|
1月前
|
算法 Java
谈谈HotSpot JVM 中的不同垃圾回收器
【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
25 1
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
58 0
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
52 0
|
3月前
|
缓存 监控 Java
"Java垃圾回收太耗时?阿里HBase GC优化秘籍大公开,让你的应用性能飙升90%!"
【8月更文挑战第17天】阿里巴巴在HBase实践中成功将Java垃圾回收(GC)时间降低90%。通过选用G1垃圾回收器、精细调整JVM参数(如设置堆大小、目标停顿时间等)、优化代码减少内存分配(如使用对象池和缓存),并利用监控工具分析GC行为,有效缓解了高并发大数据场景下的性能瓶颈,极大提升了系统运行效率。
75 4
|
3月前
|
算法 Java 应用服务中间件
探索JVM垃圾回收算法:选择适合你应用的最佳GC策略
探索JVM垃圾回收算法:选择适合你应用的最佳GC策略