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

【深入理解JVM】垃圾回收相关概念与相关算法

相关概念

System.gc()

显示触发FullGC,但不是立刻调用,并不确定调用的时间

内存溢出(OOM)

没有空闲内存,垃圾回收器也无法提供更多内存时,就会报出OOM 错误

内存泄漏(Memory Leak)

对象不会再被程序再次使用,但是GC不能回收他们的情况,就是内存泄漏

举例:

  • 单例模式中持有对外部对象的引用,因为单例对象的生命周期和应用程序一样长,所以外部对象不能被回收
  • 提供close()接口的资源未关闭,导致资源一直开启无法被回收

Stop The World

分析工作必须在一个能确保一致性的快照中进行,所以要进行STW

在 JVM 中,STW 指的是所有 Java 线程都被暂停执行,只有 JVM 自己的后台线程(如 GC 线程)在工作的状态。

垃圾回收的并行与并发

并行:

  • 多条垃圾收集线程并行工作,但此时用户线程处于等待状态

串行:

  • 单线程执行
  • 如果内存不够,则程序暂停,启动JVM垃圾回收器GC,回收完在启动程序的线程

安全点与安全区域

只有在特定的位置才能听来下GC,这个点就是安全点

选择一些执行时间较长的程序作为safe point,如方法调用,循环跳转,异常跳转

在GC发生时,如何检查所有线程都到安全点停顿下来?

线程响应停顿请求并进入安全点
  • 运行中线程:正在执行字节码的线程会在到达 “安全点”(如方法调用、循环跳转、异常抛出等预设位置)时,检测到全局停顿标志,随后暂停执行并进入安全点状态。
  • 阻塞 / 等待线程:处于sleepwait或阻塞状态的线程,由于未执行字节码,默认已处于 “安全区域”(Safe Region),无需额外操作即可被视为已进入安全点。
  • native 方法线程:若线程正在执行本地方法(非 Java 字节码),JVM 会等待其返回 Java 代码后,在下次安全点位置停顿。
检查所有线程是否已进入安全点
  • 线程状态遍历:JVM 维护一个线程列表(如Threads_list),GC 线程会遍历列表,检查每个线程的状态标志(如_at_safepoint)。
  • 等待机制:若存在未进入安全点的线程(如仍在执行字节码的线程),GC 线程会进入等待状态,直到所有线程的_at_safepoint标志被置位。
  • 超时处理:极少数情况下,若线程长期无法到达安全点(如死循环且无安全点),JVM 可能会强制中断(具体行为依赖 JVM 实现)。

引用

强引用(Strong Reference)

  • 不会被回收
  • 造成内存泄露的主要原因之一

软引用(Soft Reference)

  • 内存溢出是进行回收,回收完如果内存还不够,第二次回收就会回收软引用的对象。第二次结束内存还是不够就会抛出OOM
  • 用于实现内存敏感的缓存
  • 可选引用队列

弱引用(Weak Reference)

  • 当垃圾收集器工作时就会被回收
  • 可以实现可有可无的缓存数据
  • 可选引用队列

虚引用(Phantom Reference)

  • 设置虚引用的对象被回收时会收到一个系统通知。
  • 不会对对象生存时间构成影响,也无法通过虚引用获得对象实例
  • 必须要引用队列

相关算法

垃圾标记阶段

引用计数算法

对每个对象保存一个整形的引用计数器属性,用于记录对象被引用的情况

  • 需要单独的字段存储计数器,增加了存储空间的开销
  • 每次赋值都要更新计数器,增加了时间开销
  • 无法处理循环引用
可达性分析算法(根搜索算法,追踪性垃圾收集)
  • Java使用
  • 以跟对象集合为起始点,按照从上至下的方式搜索被根对象集合(GC Roots)所链接的目标对象是否可达(根对象集合就是一组必须活跃的引用)
  • 搜所经过的路径是引用链

GC Roots的元素:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区类静态属性引用的对象
  • 方法区常量引用的对象
  • 被synchronized持有的对象
  • JVM内部的引用
  • 还有其他对象临时加入,比如分代收集和局部回收

可达性分析算法必须在能保证一致性的快照中进行,枚举根节点时是必须停顿(STW)的

finalization机制

垃圾回收垃圾对象之前,总会调用finalize()方法,用于在对象被回收前进行资源释放

不要主动调用finalize():

  • 可能导致对象复活
  • 执行时间没有保障,如果没有GC,finalize()也不会执行
  • 糟糕的finalize()严重影响GC性能

由于finalize()方法存在,对象一般处于三种可能的状态:

  • 可触及的:从根节点开始可以到达这个对象
  • 可复活的:对象所有音乐都被释放,有可能在finalize()中复活
  • 不可触及的:finalize()方法被调用且没有复活

判断是否可回收,经历两次标记过程:

  1. 到GC Root没有引用链,进行第一次标记
  2. 进行筛选:

1. 如果finalize()没有被重写,或者已经被调用过,对象就会被视为不可触及的

2. 如果重写了finalize()且没有被执行,对象会被插入到F-Queue队列中,由虚拟机自动创建的低优先级的 Finalizer线程触发其finalize方法

3. 稍后进行第二次标记,如果对象与引用链上任意一个对象建立了联系,对象就会被移除”即将回收“+集合

垃圾清除阶段

标记-清除算法(Mark-Sweep)

当有效内存空间被耗尽的时候,STW期间进行两项工作:标记、清除

标记从根节点开始遍历,标记可达的对象,在对象的Header中记录

清除:对堆内存从头到尾进行线性的遍历,回收对象Header中没有被标记的对象(把需要清除的对象的地址放在空闲列表之中)

缺点

  • 效率不高
  • 进行GC时STW
  • 清理出来的空间内存不连续,需要维护一个空闲列表
复制算法(Copying)

将内存空间分为两块,垃圾回收时将存活的对象复制到未使用的内存块中,之后清除当前使用的内存块中的所有对象,然后交换两个内存块的角色

  • 空间换时间
  • 保证空间连续性
  • 是复制而不是移动,GC需要维护对象之间的引用关系。内存占用、时间开销较大
标记-压缩算法(Mark-Compact)

基于老年代回收,标记-清除算法效率低下,内存回收后还会产生内存碎片。复制算法因为老年代大部分对象都是存活对象,复制成本过高。因此需要其他算法

第一阶段与标记-清除算法一样

第二阶段将所有的存活对象压缩到内存的一段,按顺序排放

之后清除边界外所有的空间

  • 效率低于复制算法,最慢
  • 如果移动的对象被其他对象引用,还需要调整引用的地址
  • 移动过程STW
分代收集算法

不同生命周期对象可以采取不同的收集方式,以便提高回收效率

年轻代:区域较小,对象生命周期短、存活率低,回收频繁——使用复制算法

老年代:区域较大,对象生命周期长、存活率高,回收不频繁——标记-清除 或者 标记-清除与标记-压缩混合实现

  • Mark阶段开销与存活对象数量成正比
  • Sweep阶段开销与管理区域大小成正比
  • Compact阶段开销与存活对象的数量成正比
增量收集算法

在STW状态下,如果垃圾回收时间过长,严重影响用户体验和系统的稳定性

基本思想:

        垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。反复至垃圾收集完成

缺点:

        线程切换和上下文的转换会使垃圾回收的总体成本上升,造成系统吞吐量的下降

分区算法
  • 将整个堆空间划分为连续的不同小区间。
  • 每一个小区间独立使用,独立回收
  • 每次合理地回收若干小区间
http://www.dtcms.com/a/405845.html

相关文章:

  • 文档抽取技术:金融保险行业数字化转型的核心驱动力之一
  • 神秘魔法?耐达讯自动化Modbus TCP 转 Profibus 如何为光伏逆变器编织通信“天网”
  • 做庭院的网站佛山网站专业制作
  • wordpress开启多站点营销云官网
  • 企业AI 智能体(AI_Agent)落地开源方案:Dify、n8n、RAGFlow、FastGPT、AutoGen和OAP深度梳理与对比分析
  • Day51 时钟系统与定时器(EPIT/GPT)
  • Django 搭配数据库开发智慧园区系统全攻略
  • 前端基础知识---10 Node.js(三)
  • article.3345645398
  • 国内如何使用GPT-5-Codex
  • Xcode 26 could not locate developer disk image for this device 无法定位开发者磁盘镜像
  • 用Python打造离线语音控制浏览器:基于VOSK的实用案例
  • 【ARDUINO】在arduino ide中下载安装开发包失败了,如何手动安装开发包
  • 上架 App 全流程解析,iOS 应用上架步骤、App Store 审核流程、ipa 文件上传与测试分发经验
  • 网站审核要多久老铁外链
  • 网站建设公司的服务公司湖南做网站 在线磐石网络
  • Linux的写作日记:Linux基础开发工具(二):vim编辑器
  • nginx缓存、跨域 CORS与防盗链设置(2)
  • 多级缓存架构:性能与数据一致性的平衡处理(原理及优势详解+项目实战)
  • 今天我们开始学习nginx缓存功能,CORS以及nginx防盗链
  • 前端缓存好还是后端缓存好?缓存方案实例直接用
  • 小九源码-springboot050-基于spring boot的苏蔚家校互联管理系统
  • 陕西西安网站建设公司大学生网页设计
  • Redis 面试常考问题(高频核心版)
  • 开发时如何彻底禁用浏览器表单自动填充缓存
  • 零基础新手小白快速了解掌握服务集群与自动化运维(七)Nginx模块--Nginx反向代理与缓存功能(二)
  • 【项目实战 Day7】springboot + vue 苍穹外卖系统(微信小程序 + 微信登录模块 完结)
  • python+springboot+uniapp基于微信小程序的停车场管理系统 弹窗提示和车牌识别
  • -bash: ssh-copy-id: command not found的两种解决方法
  • 电商网站新闻怎么做即速应用小程序官网