当前位置: 首页 > news >正文

Java 垃圾回收机制(GC)概览

简介

Java垃圾收集、堆和运行时编译器默认选择

  • jdk1.9开始,默认使用G1收集器,
  • GC Threads的最大数量受堆大小和可用CPU资源限制
  • 初始堆大小为物理内存的1/64
  • 最大堆大小为物理内存的1/4
  • 分层编译器,同时使用C1和C2

JVM 垃圾收集器可以为配置优先满足两个目标之一:最大暂停时间(maximum pause-time) 和应用程序吞吐量(application throughput)

  • 最大暂停时间:通过-XX:MaxGCPauseMillis= 指定,垃圾收集器会调整 Java 堆大小和其他与垃圾收集相关的参数,以试图使垃圾收集暂停时间短于毫秒。最大暂停时间目标的默认值因收集器而异。这些调整可能会导致垃圾收集更频繁地发生,从而降低应用程序的整体吞吐量。但在某些情况下,无法满足所需的暂停时间目标。

  • 吞吐量目标:吞吐量目标是通过收集垃圾所花费的时间来衡量的,垃圾收集之外所花费的时间就是应用程序时间。通过-XX:GCTimeRatio=nnn指定垃圾收集时间与申请时间之比为1/(1+nnn)。例如,-XX:GCTimeRatio=19设定垃圾收集总时间的 1/20 或 5% 为目标。

几种GC类型

  • 串行收集器(Serial Collector):单线程收集,适用于单核处理器,无法利用多处理器硬件。启用参数:-XX:+UseSerialGC
  • 并行收集器(Parallel Collector):也称为吞吐量收集器(jdk8默认收集器) 区别于串行收集器主要具有多线程,启用参数:-XX:+UseParallelGC
  • CMS(并发标记清除)收集器:适用于响应时间比吞吐量重要,要求较短GC暂停时间并能与垃圾收集共享处理器资源的应用程序。启用参数:-XX:+UseConcMarkSweepGC。从JDK9开始,CMS收集器已被弃用,并使用G1代替。
  • G1收集器:Garbage-First (G1),此收集器旨在从小型机器扩展到具有大量内存的大型多处理器机器。它能够以高概率满足暂停时间目标,同时实现高吞吐量。从JDK9开始,G1为默认收集器。启用参数:-XX:+UseG1GC
  • ZGC:ZGC 提供的最大暂停时间不到一毫秒,但会牺牲一些吞吐量。它适用于需要低延迟的应用程序,ZGC 适用于从几百兆字节到 16TB 的堆大小。从 JDK 11 开始,ZGC 作为一项实验性功能推出。启用参数:-XX:+UseZGC

如何选择GC

  • 如果应用程序的数据集较小(最多约 100 MB),则选择带有选项的串行收集器-XX:+UseSerialGC。
  • 如果应用程序将在单个处理器上运行并且没有暂停时间要求,则选择带有选项的串行收集器-XX:+UseSerialGC。
  • 如果 (a) 应用程序的峰值性能是第一优先级,并且 (b) 没有暂停时间要求或者可以接受一秒或更长时间的暂停,那么让 VM 选择收集器或选择并行收集器-XX:+UseParallelGC。
  • 如果响应时间比总体吞吐量更重要,并且垃圾收集暂停必须保持更短,那么请选择最并发的收集器-XX:+UseG1GC(jdk8以上)或CMS(8以下)。
  • 如果响应时间是高优先级,那么选择一个完全并发的收集器 -XX:+UseZGC。

G1 GC

G1 旨在使用当前的目标应用程序和环境在延迟和吞吐量之间实现最佳平衡,其特性包括:

  • 适用于堆大小高达数十 GB 或更大,其中 50% 以上的 Java 堆被实时数据占用。
  • 适用于对象分配和提升的速度可能会随着时间的推移而发生很大变化。
  • 适用于堆内存中存在大量碎片的应用。G1通过整理存活对象来减少碎片。
  • 可预测的暂停时间目标不超过几百毫秒,避免长时间的垃圾收集暂停。

G1 主要通过对象迁移(Evacuation)来回收空间:在选定的要收集的内存区域内找到的存活对象被复制到新的内存区域,并在过程中对其进行压缩。迁移完成后,先前被存活对象占用的空间将被重新用于应用程序的分配。

G1堆布局

G1将堆划分为一组相等的heap regions,region是内存分配和回收的基本单位。region可以是空的(浅灰色),或者分配给年轻代(红色)或老年代(蓝色)。年轻代包含Eden区(红色)和survivor区(红色带s),对于跨多个区域的对象,老生代区域可能非常大(浅蓝带s)

在这里插入图片描述

G1回收阶段

  • Concurrent Start(并发启动) 在 Concurrent Start 阶段,G1 会并发标记老年代的存活对象,并进行正常的年轻代回收(Normal Young Collection)
  • Remark(标记完成阶段):当并发标记阶段结束后,G1 会暂停应用线程执行 Remark 阶段。这一阶段会完成标记,确保所有存活对象都被标记,并进行一些额外的处理,如类卸载、引用处理等。
  • Cleanup(清理阶段):这个阶段的主要目的是完成空间回收准备工作,判断是否会执行 Space-Reclamation 阶段。
  • Space-Reclamation Phase(空间回收阶段):此阶段由多个年轻代收集组成,除了年轻代区域外,还会回收老生代区域的存活对象, 这些收集也称为 Mixed Collections(混合回收)。
  • Full GC(完全垃圾回收):如果在 G1 回收过程中,G1 在收集活跃对象的过程中发生了内存不足的情况,它将执行 Full GC

SATB Snapshot-At-The-Beginning (开始快照)

SATB是一种垃圾回收标记算法,常用于 并发垃圾回收器 ,特别是在 G1 和 ZGC 等收集器中。
它的目的是解决并发标记阶段中,如何处理在标记过程中可能会修改的对象引用的问题。SATB 是一种 并发标记算法,在标记开始时通过记录一个“快照”,确保在标记过程中,即使有对象的引用发生变化,也能保证准确的标记结果。

Remembered Set

Remembered Set 是一种用于垃圾回收的数据结构,特别在 G1 GC 和 其他分代垃圾回收器(如 CMS)中使用,用来优化 跨代引用 的处理。它的主要作用是帮助 标记 和 回收 阶段处理 年轻代(Young Generation)和 老年代(Old Generation)之间的引用,减少跨代引用带来的性能问题。

Collection Set

Collection Set 是一个集合,它记录了在当前垃圾回收周期中需要被回收的区域。这些区域通常是存放了 垃圾对象 或 跨代引用 的区域。根据垃圾回收的类型和阶段,Collection Set 可以包括 年轻代 或 老年代 的区域,或者两者的组合。

G1参数

  • -XX:-G1UseAdaptiveIHOP 这个参数用于 禁用 G1 的自适应 IHOP 调整机制。默认情况下,G1 收集器会根据堆的使用情况自动调整 Initiating Heap Occupancy Percentage(IHOP) 的值。自适应 IHOP 允许 G1 根据实际运行时的内存使用情况,动态调整触发混合回收的堆占用阈值。

  • -XX:InitiatingHeapOccupancyPercent 这个参数用于 设置触发混合回收(Mixed GC)的阈值,即 老年代堆空间的使用比例。它表示 当老年代堆的使用比例达到该值时,G1 将启动混合垃圾回收。

  • -XX:G1PeriodicGCInterval 周期性全堆回收的时间间隔,使得 G1 收集器能够定期执行 全堆垃圾回收(Full GC),默认为0,意味着不会执行周期性全堆回收

  • -XX:MaxGCPauseMillis=200 最大暂停时间目标,默认200ms

G1 建议避免使用像-Xmn 或 -XX:NewRatio 这样的选项来限制年轻代的大小。年轻代的大小是 G1控制停顿时间的主要手段之一。如果将年轻代的大小固定为某个特定值,可能会限制 G1 的灵活性,使其无法有效地调整以满足设定的停顿时间目标。

ZGC

Z 垃圾收集器(ZGC)是一种可扩展的低延迟并发垃圾收集器。ZGC 可同时执行所有高成本工作,且不会使应用程序线程的执行停止超过一毫秒。它适用于需要低延迟的应用程序。暂停时间与正在使用的堆大小无关。ZGC 适用于从几百兆字节到 16TB 的堆大小。

配置 ZGC 使用大页面通常会带来更好的性能(就吞吐量、延迟和启动时间而言),并且没有真正的缺点,只是设置起来稍微复杂一些。设置过程通常需要 root 权限,这就是为什么默认情况下不启用它的原因。

在 Linux x86 上,大页面(也称为“巨页”)的大小为 2MB。

配置系统的大页池以拥有所需的页面数量(需要root权限):
echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

其他事项

引用类型

  • Strong References(强引用):普通的对象引用都是强引用,只要有强引用指向某个对象,垃圾回收器不会回收该对象。
  • Soft References(软引用): 软引用与弱引用类似,都是用来引用不应该被强制持有的对象。不同之处在于,只有在内存不足时,软引用所指向的对象才会被回收。因此,它们适合用于缓存,尤其是当我们希望尽量保留缓存但又不影响程序的内存使用时。
  • Weak References(弱引用) :弱引用是一种不强制保持对象存在的引用。当垃圾回收器运行时,如果一个对象只被弱引用引用,它将被回收。弱引用通常用于缓存或可以被回收的对象。
  • Phantom References(虚引用):虚引用是最弱的引用类型。与弱引用和软引用不同,虚引用并不会影响对象的生命周期。当一个对象只被虚引用引用时,它仍然可以被垃圾回收。虚引用最大的作用是提供一种机制,在对象被回收时得到通知。虚引用通常与引用队列结合使用。

The Cleaner API

Finalization 在 JDK 9 中已被弃用,并在JDK18中已被删除。
如果需要关闭资源可以使用,try-with-Resources或Cleaner API(jdk9引入)

使用场景

与 try-with-resources 不同,Cleaner 可以用于那些不能在构造时立即进行清理的对象。在对象被垃圾回收后,Cleaner 将会执行指定的清理代码。

  1. 复杂生命周期场景:当对象的使用与其清理之间的关系非常复杂时,Cleaner 可以帮助管理这些清理任务。例如,管理一个需要在多个地方使用但必须确保最终被正确关闭的资源。
  2. 非显式资源释放:在某些情况下,资源的释放可能并不在单一位置完成。这时,使用 Cleaner 可以确保在对象被回收时自动释放资源。
  3. 作为对象的清理回调:当对象完成其用途后,Cleaner 可以被注册为清理操作的回调,而不必依赖于用户代码来执行清理。

Cleaner API核心方法

  • create():创建一个Cleaner对象,并启动一条新线程用于监听目标对象是否已经被回收。
  • register(Object obj, Runnable task):注册一个需要被清理的对象和一个清理任务。当对象被垃圾回收器回收时,清理任务将被执行。

Cleaner示例

import java.lang.ref.Cleaner;public class CleanerExample {// This Cleaner is shared by all CleanerExample instancesprivate static final Cleaner CLEANER = Cleaner.create();private final State state;public CleanerExample(String id) {state = new State(id);CLEANER.register(this, state);}// Cleaning action class for CleanerExampleprivate static class State implements Runnable {final private String id;private State(String id) {this.id = id;System.out.println("Created cleaning action for " + this.id);}//对象不可达时,run方法将被调用@Overridepublic void run() {System.out.println("Cleaner garbage collected " + this.id);}}public static void main(String[] args) {CleanerExample myObject1 = new CleanerExample("myObject1");// Make myObject1 unreachablemyObject1 = null;System.out.println("-- Give the GC a chance to schedule the Cleaner --");for (int i = 0; i < 100; i++) {// Calling System.gc() in a loop is usually sufficient to trigger// cleanup in a small program like this.System.gc();try {Thread.sleep(1);} catch (InterruptedException e) {}}System.out.println("-- Finished --");}
}

文章来源:https://docs.oracle.com/en/java/javase/23/gctuning/introduction-garbage-collection-tuning.html

http://www.lryc.cn/news/484980.html

相关文章:

  • Kafka节点服役和退役
  • Git如何简单使用
  • 酒水分销积分商城小程序开发方案php+uniapp
  • MTU-内核态(数据链路层或网络接口上能够传输的最大数据包大小)
  • React的基础API介绍(一)
  • 【Electron】总结:如何创建Electron+Element Plus的项目
  • 从依托指标字典到 NoETL 自动化指标平台,指标口径一致性管理的进阶
  • 嵌入式面试题练习 - 2024/11/15
  • 分析http话术异常挂断原因
  • 云岚到家 秒杀抢购
  • 【WPF】Prism库学习(一)
  • 0 -vscode搭建python环境教程参考(windows)
  • Uniapp 引入 Android aar 包 和 Android 离线打包
  • 10款高效音频剪辑工具,让声音编辑更上一层楼。
  • Javascript——设计模式(一)
  • Hybird和WebView
  • c++实现中缀表达式 转换为后缀表达式
  • Cisco FMC重置SmartLicense到Evaluatin mode步骤
  • 多表查询综合归纳
  • 【5.线性表-链式表示-王道课后算法题】
  • 存储过程及练习
  • 【在Linux世界中追寻伟大的One Piece】多路转接epoll
  • 设计模式-参考的雷丰阳老师直播课
  • Python +Pyqt5 简单视频爬取学习(一)
  • Python Requests模块全面教程
  • PyQt入门指南六十 与Python其他库的集成方法
  • Android15之解决:Dex checksum does not match for dex:framework.jar问题(二百三十九)
  • 车企自动驾驶功能策略 --- 硬件预埋(卷传感器配置)
  • 【已为网站上传证书,却显示不安全】
  • docker busybox作为initContainers