说说你对JVM的垃圾回收机制的理解?

Java 虚拟机(JVM)的垃圾回收(Garbage Collection,GC)机制是自动管理内存的核心,其核心目标是识别并回收不再被使用的对象所占用的内存,避免内存泄漏和溢出。以下从垃圾判断方法垃圾回收算法具体垃圾收集器三个层面详细说明:

一、垃圾判断方法:如何识别 "垃圾" 对象

垃圾回收的前提是判断哪些对象已经不再被使用(即 "垃圾")。JVM 主要采用两种判断方式:

1. 引用计数法(Reference Counting)
  • 原理:每个对象维护一个 "引用计数器",当对象被引用时计数器 + 1,引用失效时 - 1;当计数器为 0 时,认为对象是垃圾。
  • 优点:实现简单,判断效率高。
  • 缺点:无法解决循环引用问题(如 A 引用 B,B 引用 A,两者计数器均不为 0,但实际已无外部引用)。
  • 现状:Java 虚拟机未采用这种方式(因循环引用问题),Python 等语言使用。
2. 可达性分析算法(Reachability Analysis)
  • 原理:以 "GC Roots" 为起点,通过引用链遍历对象;若对象无法通过任何引用链连接到 GC Roots,则被判定为垃圾。
  • GC Roots:指一系列 "根对象",包括:
    • 虚拟机栈(栈帧中的局部变量表)中引用的对象;
    • 方法区中类静态属性引用的对象;
    • 方法区中常量引用的对象;
    • 本地方法栈中 JNI(Native 方法)引用的对象。
  • 优点:解决了循环引用问题,是 Java 虚拟机的核心判断方式。
  • 扩展:Java 中的 "引用" 被细分为 4 种类型(强引用、软引用、弱引用、虚引用),不同引用类型影响对象被回收的时机(如软引用在内存不足时才会被回收)。

二、垃圾回收算法:如何回收垃圾

确定垃圾对象后,需要通过具体算法回收其内存。常见的基础算法包括:

1. 标记 - 清除算法(Mark-Sweep)
  • 步骤
    1. 标记:通过可达性分析标记所有存活对象(非垃圾);
    2. 清除:遍历堆内存,回收所有未被标记的对象(垃圾)。
  • 优点:实现简单,无需移动对象。
  • 缺点
    • 效率低:标记和清除过程都需要遍历整个堆,耗时较长;
    • 内存碎片:回收后会产生大量不连续的内存碎片,可能导致大对象无法分配内存。
2. 标记 - 复制算法(Mark-Copy)
  • 步骤
    1. 将堆内存分为大小相等的两块(如 A 和 B),仅使用 A 块;
    2. 标记:标记 A 块中的存活对象;
    3. 复制:将 A 块中所有存活对象复制到 B 块(按顺序连续放置);
    4. 清除:清空 A 块,后续内存分配仅使用 B 块(下次回收时交换角色)。
  • 优点
    • 效率高:复制存活对象的成本低于清除大量垃圾;
    • 无内存碎片:存活对象连续放置,内存分配简单(指针碰撞即可)。
  • 缺点
    • 内存利用率低:仅能使用一半内存;
    • 不适合存活对象多的场景(复制成本高)。
  • 适用场景:Java 新生代(因新生代对象存活时间短,存活对象少)。
3. 标记 - 整理算法(Mark-Compact)
  • 步骤
    1. 标记:标记所有存活对象;
    2. 整理:将所有存活对象向内存一端移动,按顺序排列;
    3. 清除:直接清除边界外的所有内存(垃圾)。
  • 优点:解决了标记 - 清除的内存碎片问题,且内存利用率 100%。
  • 缺点:整理阶段需要移动对象,成本较高(尤其是老年代对象存活时间长,移动成本大)。
  • 适用场景:Java 老年代(因老年代对象存活时间长,存活对象多,需避免碎片)。
4. 分代收集算法(Generational Collection)
  • 原理:根据对象存活周期将堆内存分为新生代老年代,针对不同区域采用不同算法(结合上述基础算法的优势):
    • 新生代:对象存活时间短(朝生夕死),适合标记 - 复制算法(只需复制少量存活对象);
    • 老年代:对象存活时间长(存活概率高),适合标记 - 清除或标记 - 整理算法(避免频繁移动对象)。
  • 细节:新生代进一步分为 Eden 区(80%)和两个 Survivor 区(From、To 各 10%),分配对象时先在 Eden 区,回收时将存活对象复制到 Survivor 区,多次存活后进入老年代。
  • 现状:几乎所有 Java 虚拟机都采用分代收集算法作为基础框架。

三、垃圾收集器:算法的具体实现

垃圾收集器是垃圾回收算法的具体实现,不同收集器针对不同场景(如吞吐量、延迟)优化。Java 虚拟机中常见的收集器包括:

1. Serial GC(串行收集器)
  • 特点:单线程执行垃圾回收,回收时暂停所有用户线程("Stop The World",STW)。
  • 算法
    • 新生代:标记 - 复制;
    • 老年代:标记 - 整理。
  • 优点:实现简单,内存占用少,适合单核 CPU 环境。
  • 缺点:STW 时间长,不适合多线程、大堆内存应用。
  • 适用场景:客户端应用(如桌面程序),JVM 默认客户端模式下的收集器。
2. ParNew GC(并行新生代收集器)
  • 特点:Serial GC 的多线程版本,仅作用于新生代,老年代仍需配合 Serial Old 或 CMS。
  • 算法:新生代采用标记 - 复制(多线程并行标记和复制)。
  • 优点:利用多 CPU 加速新生代回收,减少 STW 时间。
  • 缺点:仍有 STW,老年代若配合 Serial Old 会导致长停顿。
  • 适用场景:多 CPU 环境下的服务端应用,常作为 CMS 收集器的新生代搭档。
3. Parallel Scavenge GC(并行清除收集器)
  • 特点:注重吞吐量(吞吐量 = 用户代码执行时间 /(用户代码时间 + GC 时间)),属于 "吞吐量优先" 收集器。
  • 算法
    • 新生代:标记 - 复制(多线程并行);
    • 老年代:Parallel Old(标记 - 整理,多线程并行)。
  • 优点:可自动调节 GC 参数(如新生代大小、晋升老年代阈值)以追求最高吞吐量。
  • 缺点:STW 时间可能较长,不适合对延迟敏感的应用。
  • 适用场景:后台计算(如数据分析)等对吞吐量要求高、可接受一定停顿的场景。
4. CMS(Concurrent Mark Sweep,并发标记清除)
  • 特点:以低延迟为目标,尽可能减少 STW 时间,老年代收集器(需配合 ParNew 作为新生代收集器)。
  • 步骤(核心是 "并发",即 GC 线程与用户线程同时执行):
    1. 初始标记:标记 GC Roots 直接关联的对象(STW,时间短);
    2. 并发标记:从初始标记的对象出发,遍历引用链(与用户线程并发,无 STW);
    3. 重新标记:修正并发标记期间因用户线程操作导致的引用变化(STW,时间较短);
    4. 并发清除:回收所有未标记的对象(与用户线程并发,无 STW)。
  • 优点:并发收集,STW 时间短,适合对延迟敏感的应用(如 Web 服务)。
  • 缺点
    • CPU 敏感:并发阶段会占用 CPU 资源,影响用户线程;
    • 内存碎片:基于标记 - 清除算法,老年代易产生碎片;
    • 需预留内存:并发清除时用户线程仍在分配内存,需保证内存不耗尽。
  • 现状:JDK 9 中被标记为 deprecated,JDK 14 中移除,被 G1 等收集器替代。
5. G1(Garbage-First)
  • 特点:区域化分代式收集器,兼顾吞吐量和延迟,适用于大堆内存(如 4GB 以上)。
  • 内存布局:将堆分为多个大小相等的 Region(1MB~32MB),每个 Region 可动态标记为新生代(Eden/Survivor)或老年代,无需物理隔离。
  • 核心思想:优先回收 "垃圾最多的 Region"(Garbage-First),减少 GC 时间。
  • 步骤
    1. 初始标记:标记 GC Roots 直接关联的对象(STW);
    2. 并发标记:遍历引用链,计算每个 Region 的垃圾占比(与用户线程并发);
    3. 最终标记:修正并发标记的偏差(STW,使用 SATB 算法高效处理);
    4. 筛选回收:根据 Region 的垃圾占比排序,优先回收垃圾多的 Region(多线程并行,STW,采用标记 - 复制算法避免碎片)。
  • 优点
    • 灵活处理大堆内存,延迟可控(可设置最大 STW 时间);
    • 无内存碎片(筛选回收时采用复制算法)。
  • 适用场景:JDK 9 及以上默认收集器,适合中大型应用(如服务器、云原生应用)。
6. 低延迟收集器(ZGC、Shenandoah)
  • ZGC(JDK 11 引入):

    • 目标:STW 时间不超过 10ms,支持 TB 级堆内存。
    • 特点:基于 Region,采用 "着色指针" 和 "读屏障" 技术,几乎全程并发(仅初始标记和最终标记有极短 STW)。
  • Shenandoah(OpenJDK 引入,非 Oracle JDK 默认):

    • 目标:低延迟,支持大堆。
    • 特点:采用 "并发整理" 算法,在并发阶段移动对象(通过转发指针和写屏障实现),几乎无 STW。
  • 适用场景:对延迟要求极高的应用(如高频交易、实时数据处理)。

总结

Java 虚拟机的垃圾回收机制是一个 "判断垃圾 - 选择算法 - 具体实现" 的完整体系:

  • 通过可达性分析判断垃圾对象;
  • 基于分代思想,结合标记 - 清除、复制、整理等基础算法;
  • 不同垃圾收集器(如 Serial、CMS、G1、ZGC)针对吞吐量、延迟等不同目标优化,需根据应用场景选择。

实际开发中,需通过监控工具(如 JConsole、VisualVM)分析 GC 日志,选择合适的收集器并调优参数(如堆大小、新生代比例),以平衡性能需求。

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

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

相关文章

兑换汽水瓶

实现代码:public static void main(String[] args) {Scanner in new Scanner(System.in);while (in.hasNextInt()) {int n in.nextInt();if (n 0) {break;}System.out.println(n / 2);}}

结合 Flutter 和 Rust 的跨平台开发方案

结合 Flutter 和 Rust 的跨平台开发方案 1. 核心思想 本方案的核心思想是Flutter 负责 UI,Rust 负责逻辑 。Flutter 作为一个成熟的 UI 框架,专注于渲染流畅、跨平台一致的用户界面。而将那些对性能、安全和并发有高要求的复杂业务逻辑、计算密集型任务或底层系统操作,全部…

理想汽车智驾方案介绍 2|MindVLA 方案详解

一、引言 MindVLA 主要包括空间智能模块、语言智能模块、动作策略模块、强化学习模块,这些模块分别有以下功能: 空间智能模块:输入为多模态传感器数据,使用 3D 编码器提取时空特征,然后将所有传感器与语义信息融合成…

计算机网络基础(三) --- TCP/IP网络结构(运输层)

运输层1. 概述和运输服务运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信功能, 运输层协议是在端系统中而不是路由器中实现的, 网络应用程序可以调用多种运输层协议, 如因特网的两种协议: TCP 和 UDP ,每种协议都能为调用的应用程序提供一组不同的运输层服务1.1 运输…

JdbcTemplate和MyBatis的区别

在 Java 后端开发中,JdbcTemplate(Spring 框架提供)和 MyBatis(持久层框架)都是用于简化数据库操作的工具,但它们的设计理念、使用方式、灵活性和适用场景有显著差异。下面从核心定位、核心特性、使用方式、…

埃氏筛|树dfs|差分计数

lc525把数组里的0换成-1&#xff0c;求子数组和为零的最长长度用哈希表记录前缀和首次出现的位置通过找相同前缀和的位置差得出最长的0和1数量相等的子数组长度。class Solution { public:int findMaxLength(vector<int>& nums) {unordered_map<int,int>hashta…

(JVM)Java 对象创建的完整过程

在日常开发和面试中&#xff0c;经常会被问到 “Java 中对象是如何被创建的&#xff1f;” 表面上只是一个 new 关键字&#xff0c;但 JVM 在幕后完成了一系列复杂操作。 可以总结为以下 六大步骤&#xff1a;类加载检查 → 分配内存 → 内存清零 → 设置对象头 → 执行构造函数…

数据库优化提速(三)JSON数据类型在酒店管理系统搜索—仙盟创梦IDE

在 MySQL 中&#xff0c;JSONB 类型&#xff08;MySQL 中实际为 JSON 类型&#xff0c;功能类似 PostgreSQL 的 JSONB&#xff0c;支持高效的 JSON 数据存储和查询&#xff09;非常适合存储半结构化数据&#xff0c;例如酒店入住客人的复杂信息&#xff08;包含客人基本信息、入…

小程序全局状态管理:使用MobX进行跨组件数据共享详解(九)

一、定义全局数据共享&#xff08;又叫&#xff1a;状态管理&#xff09;是为了解决组件之间数据共享的问题&#xff1b;全局数据共享方案&#xff1a;VueX、Redux、MobX等&#xff1b;二、小程序全局数据共享方案使用mobx-miniprogram配合mobx-miniprogram-bindings实现全局数…

生成模型 | DDPM -> Imrpoved DDPM -> DDIM

DDPM: Denoising Diffusion Probabilistic Models 采样过程中的迭代计算为&#xff1a; xt−11αt(xt−1−αt1−αˉϵθ(xt,t))β~tzx_{t-1} \frac{1}{\sqrt{\alpha_t}}(x_t - \frac{1 - \alpha_t}{\sqrt{1 - \bar{\alpha}}}\epsilon_{\theta}(x_t, t)) \sqrt{\tilde{\beta…

Linux驱动之DMA(三)

目录一、驱动内容1. 核心结构体解析2. 关键模块解析3. 驱动初始化流程4. 关键寄存器操作5. 典型工作流程6. 代码特点7. 重要函数列表8. 使用示例二、驱动中DMA的使用1. DMA通道初始化&#xff08;imx_uart_dma_init&#xff09;2. DMA发送流程&#xff08;imx_uart_dma_tx&…

MongoDB 分片集群把非分片集合转成分片集合

记得关注一下博主&#xff0c;博主每天都会更新IT技术&#xff0c;让你有意想不到的小收获哦^_^ 文章目录*记得关注一下博主&#xff0c;博主每天都会更新IT技术&#xff0c;让你有意想不到的小收获哦^_^*一、MongDB集群启停止1、MonogoDB分片集群服务启动顺序(三台主机都要操作…

mybatis过渡到mybatis-plus过程中需要注意的地方

将 MyBatis 升级为 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个平滑过渡的过程&#xff0c;因为 MP 是 MyBatis 的增强工具&#xff08;而非替代&#xff09;&#xff0c;但仍有一些关键注意事项需要关注&#xff0c;以确保升级后功能兼容且能充分利用 MP 的特性&#…

openEuler系统中如何将docker安装在指定目录

在 openEuler 中&#xff0c;Docker 的默认数据存储目录为 /var/lib/docker&#xff08;程序文件通常安装在系统默认路径&#xff0c;一般无需修改&#xff09;。若需将 Docker 数据&#xff08;镜像、容器、卷等&#xff09;存储到指定目录&#xff0c;可通过修改 Docker 配置…

2.4 我国金融市场的监管体制

1、国务院金融发展委员会职责 2、中国人民银行职责

PHP - 实例属性访问与静态方法调用的性能差异解析

观察到了一个看似矛盾的现象&#xff1a;实例属性访问更快&#xff0c;但静态方法调用更快。这实际上是两种不同的操作&#xff0c;下面我将详细解释其中的原理和差异。1. 实例属性访问为什么快访问机制class MyClass {public $instanceProp 1; }$obj new MyClass(); $value …

音视频面试题集锦第 31 期

音视频面试题集锦第 31 期&#xff1a; 1、I 帧、P 帧和 B 帧的概念及区别&#xff1f;2、视频编码中的码率控制技术有哪些&#xff1f;3、音频采样参数有哪些&#xff1f;4、RTMP 和 HLS 协议各有什么特点&#xff1f;如何选择&#xff1f;5、WebRTC 中的 ICE、STUN、TURN 各…

企业视频库管理高效策略

内容概要本文全面探讨企业视频库管理的高效策略&#xff0c;旨在帮助组织优化视频资源处理。首先&#xff0c;我们将介绍企业视频库管理的基本概念和核心价值。接着&#xff0c;深入分析智能分类核心技术如何通过AI算法实现视频自动归类。之后&#xff0c;阐述云集成实现路径&a…

WebSocket和跨域问题

WebSocket 特点 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。简单来说&#xff0c;它就像是在客户端和服务器之间建立了一条"专用通道"&#xff0c;双方可以随时主动发送消息给对方&#xff0c;而不需要像HTTP那样总是由客户端发起请求。 同一时间、双向…

微服务-19.什么是网关

一.网关曾经我们的项目是单体项目&#xff0c;前端只需要请求8080端口&#xff0c;就可以获取所有需要的数据和服务并进行渲染。但是拆分成微服务后&#xff0c;会面临几大问题&#xff1a;1.但是现在我们将该单体项目拆分成了微服务项目&#xff0c;每个项目都有自己独立的端口…