JVM——垃圾收集策略

GC的基本问题

什么是GC?

GC 是 garbage collection 的缩写,意思是垃圾回收——把内存(特别是堆内存)中不再使用的空间释放掉;清理不再使用的对象。

为什么要GC?

堆内存是各个线程共享的空间,不能无节制的使用。服务器运行的时间通常都很长。累积的对象也会非常多。这些对象如果不做任何清理,任由它们数量不断累加,内存很快就会耗尽。所以GC就是要把不使用的对象都清理掉,把内存空间空出来,让项目可以持续运行下去。

如何判断对象已死?(GC触发的条件

引用计数算法

简单来说,引用计数算法就是在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。

但这时会出现一个问题,即当两个对象相互进行引用时,也就意味着,它永远都无法”死亡“,就无法进行回收。

Java虚拟机并不是通过引用计数算法来判断对象是否存活的。

可达性分析算法

通过一系列被称为 “GC Roots” 的对象作为起始点,从这些节点向下搜索,搜索走过的路径叫引用链。当一个对象到 GC Roots 没有任何引用链相连时,证明此对象不可用,可被判定为 “死亡” 。

在 Java 语言中,可作为 GC Roots 的对象包括:

  1. 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。

  2. 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。

  3. 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。

  4. 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。

  5. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

  6. 所有被同步锁(synchronized关键字)持有的对象。

  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

再谈引用

引用大至分为四种:

  • 强引用:new出来的对象 内存溢出也不会进行回收
  • 软引用:只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。
  • 弱引用:只要有垃圾收集就会回收
  • 虚引用:回收时收到系统通知

生存还是死亡?(是否真正进行回收)

即使在可达性分析算法中判定为不可达的对象,也不是“非死不可”的,这时候它们暂时还处于“缓 刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:

  • 第一次标记:是否有与GC Roots相连接的引用链
  • 第二次筛选:此对象是否有必要执行finalize()方法(判断对象能否通过finalize()方法实现自我拯救(避免被回收 )

 finalize()是java.lang.Object类的方法,所有对象默认继承这个方法。方法体内是空的,说明如果子类不重写这个方法,那么不执行任何逻辑。

回收方法区(永久代)

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。

废弃常量回收比较简单,就是看是否仍有其他地方引用了此字面量,若无引用,则被回收。而判断一个类是否需要被回收条件比较苛刻,需要同时满足下面三个条件才能算是“无用的类”:

  1. 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例
  2. 加载该类的ClassLoader已经被回收
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

分代收集理论

弱分代假说(新生代)

绝大多数对象都是朝生夕灭的。

强分代假说(老年代)

熬过越多次垃圾收集过程的对象就越难以消亡。

垃圾收集器的设计原则

将Java堆根据年龄(年龄即对象熬过垃圾收集过程的次数)划分出不同的区域进行存储。当一个对象存活时间越长,年龄越大,我们就将其移动到老年代区域,这样将新生代和老年代分开管理。每次回收时只关注如何保留少量存活而不是去标记那些大量将要被回收的对 象,就能以较低代价回收到大量的空间;

 跨代引用假说

跨代引用相对于同代引用来说仅占极少数。

当新生代和老年代互相引用,是应该倾向于同时生存或者同时消亡的。如果某个新生代对象存在跨代引用,由于老年代对象难以 消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时 跨代引用也随即被消除了。

如何记录每一个对象是否存在及存在哪些跨代引用?

在新生代上建立一个全局的数据结构(记忆集),这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。

垃圾收集算法

标记-清除算法(老年代

首先标记出需要回收的对象,在标记完成后统一回收掉所有的被标记对象。

缺点

  1. 执行效率不稳定:标记和清除两个过 程的执行效率都随对象数量增长而降低;
  2. 内存空间的碎片化问题:标记、清除之后会产生大 量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找 到足够的连续内存而不得不提前触发另一次垃圾收集动作。 

标记-复制算法(新生代)

它将可用内存按需要分成两块(实际情况不一定2块,块的大小比例不一定),每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

新生代通常使用复制算法,因为新生代中对象大多 “朝生夕灭”,每次垃圾收集时有大量对象死去,只有少量存活,复制算法能高效地将少量存活对象复制到另一块空间,实现快速回收。

优点:每次只对其中一块进行GC,不用考虑内存碎片的问题,并且实现简单,运行高效。(适用于新生代,效率高)

缺点:内存缩小了一半。复制的目标空间需要依赖其他空间进行分配担保(将超出的部分直接放入到老年代区域中)。效率随对象存活率升高而降低:当对象存活率较高时,需要进行较多复制操作,效率将会变低。

Appel式回收

把新生代分为一块较大的Eden空间和两块较小的 Survivor空间(8:1:1),每次分配内存只使用Eden和其中一块Survivor。

发生垃圾搜集时,将Eden和Survivor中仍 然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor空间。

当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(将超出的部分直接放入到老年代区域中)。

标记-整理算法(老年代)

使用“标记-整理”算法:先标记,再把所有存活的对象向一端移动,然后直接清理端边界意外的内存。

老年代中对象存活率高,没有额外空间对其进行分配担保。如果在老年代使用复制算法,由于存活对象多,复制操作会比较频繁,效率低下且成本较高。

优点:不会像复制算法、效率随对象存活率升高而变低。不会像标记-清除算法,产生内存碎片(因为清除前,进行了整理,存活对象都集中到空间一侧)。

缺点:主要是效率问题:除像标记-清除算法的标记过程外,还多了需要整理的过程,效率更低。 

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

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

相关文章

用Java模拟打字:深入解析 java.awt.Robot 的键盘控制艺术

作为开发者,我们有时会遇到需要自动化用户界面交互的场景,比如自动化测试、脚本编写、或者制作一些辅助工具。而模拟键盘输入,尤其是“打字”,是这类自动化任务中非常基础且常见的一环。 在 Java 中,实现这一目标的利…

JavaScript 入门全讲解

JavaScript 入门全讲解 一、前言:为什么学习 JavaScript?二、JavaScript 简史与发展三、JavaScript 基础语法3.1 变量声明:var、let、const3.2 数据类型3.3 类型判断3.4 类型转换 四、运算符与表达式五、流程控制5.1 条件判断5.2 switch 语句…

python练习:求数字的阶乘

求数字的阶乘 eg:5的阶乘 54321 """ 求数字的阶乘 eg:5的阶乘 5*4*3*2*1 """count 1 for i in range(1,6):count count * iprint(count)运行结果:

传统农耕展陈如何突破?数字多媒体能否重构文化体验边界?

农耕文化是中华民族悠久历史的重要组成部分,它不仅承载着古代先民与自然和谐相处的智慧,也体现了人们对土地和自然的深厚情感。而今,如何有效地传承和展示这一传统文化,成为了一个重要的课题。今日,便让我们聚焦于农耕…

nginx代理websocket时ws遇到仅支持域名访问的处理

最终改造点 proxy_set_header Host 这一行 未改之前遇到的问题: nginx 日志显示 https://aaa.bbbb.cn:7413 被解析成了 IP 地址,这通常是因为 DNS 解析的结果被缓存或某些中间层(如负载均衡器、防火墙等)将域名替换为 IP 地址。…

YUM/DNF管理工具

YUM (Yellow dog Updater, Modified) , RHEL8 中默认使用的软件批量管理工具由原版本的 yum 换成了速度更快的 dnf ( DNF Dandified YUM ),原有的 yum 命令仅为 dnf 的软链接,当然依旧可以使用。 [root…

易基因:何川团队开发新m6A测序方法 可温和条件下高分辨率/低背景噪声检测m6A修饰|Nature子刊

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 RNA和DNA中的化学修饰在多种生物过程中发挥着关键作用,包括转录调控、RNA降解、蛋白质翻译和免疫调节等。这些修饰已被新的测序方法以单碱基分辨率定量地绘制出来&#xff0c…

前后端分离: vue3+SpringBoot+ElementPlus+Axios+MyBatisPuls

前后端分离: vue3SpringBoot 项目介绍搭建Vue前端工程axios请求响应拦截跨域 搭建后端TableId,TableName分页显示配置Druid数据源带条件的分页查询后端校验lambda表达式说明 项目介绍 🌟项目页面 🌟技术栈: 1.前端技术栈: Vue3AxiosElementPlus 2.后端技…

序列密码算法ShanLooog512设计原理详解

序列密码算法ShanLooog512设计原理详解 ShanLooog512(闪龙512)为序列密码算法,内部状态为512比特,密钥长度为128或256比特,轮函数为FFFFFFFF,循环轮数为24轮,输出密钥流为512比特的状态。与Salsa20类似,内…

Matplotlib可视化基础

1. 折线图 matplotlib.pyplot.plot() # 主要参数: x,y -- 接收array,表示X轴和Y轴对应的数据,无默认 color -- 接收特定string,指定线条的颜色,默认为None linestyle -- 接收特定string,指定线条的类型…

阿里云直接对系统云盘扩容

阿里云直接对系统云盘扩容 登录阿里云控制台,进入ECS实例管理页面,检查目标磁盘的容量是否已更新为扩容后的数值。通过SSH远程连接服务器,使用命令 lsblk 或 fdisk -l 查看当前磁盘分区和容量,确认扩容后的物理磁盘已被系统识别。…

OpenResty深度解析:从卓伊凡的”隐形主流”论看其深度原理与应用生态-卓伊凡

OpenResty深度解析:从卓伊凡的”隐形主流”论看其深度原理与应用生态-卓伊凡 一、OpenResty技术概述:悄然成为基础设施的”隐形冠军” 1.1 OpenResty的”附带安装”现象 正如技术观察者卓伊凡在其《现代Web基础设施的隐形架构》一文中首次提出的观点:”OpenResty正在以一…

健康养生:开启品质生活的密钥

健康是人生最宝贵的财富,养生则是守护这份财富的关键。科学合理的养生方式,能让我们以更饱满的状态拥抱生活。 合理饮食是健康养生的基石。遵循 “食物多样、谷类为主” 的原则,保证每日摄入足够的蛋白质、碳水化合物、脂肪、维生素和矿物质。…

湖北理元理律师事务所:债务优化的法律机制与民生实践

在债务纠纷日益增多的社会背景下,合法、规范的债务管理服务成为民生需求的重要环节。湖北理元理律师事务所作为经国家司法局注册登记的债事服务机构,以法律为工具,探索出一套覆盖债务咨询、规划与风险防控的服务体系。 1.法律服务的专业化框…

AI日报 - 2025年04月29日

🌟 今日概览(60秒速览) ▎🤖 AGI突破 | 巨头CEO预测AGI时间线,5年内或达人类认知水平;Yann LeCun强调多模态训练重要性。 关于AGI定义和实现时间的讨论升温,对超越纯文本训练的需求成为共识。 ▎💼 商业动向…

【C++】类和对象(4)

目录 1. 类型转换 非explicit的单参数构造函数 示例 explicit的单参数构造函数 示例 不同版本的行为 示例 (单参数) 示例(多参数且其余参数有默认值 ) 示例(多参数且无默认值) 2. static成员变量…

苍穹外卖10

WebSocket WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信----浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。 HTTP协议和WebSocket协议对比: HTTP是短链接 WebSocke…

STM32的Flash映射双重机制

在STM32微控制器中,存在一个重要的内存映射特性:Flash存储器可以同时出现在两个不同的地址区域,而且可以通过重映射功能改变CPU启动时从哪个地址获取初始指令。 STM32的Flash映射双重机制 当描述"通常起始于地址0x00000000&#xff0c…

在 Spring Boot 中实现异常处理的全面指南

在现代 Web 应用开发中,异常处理是确保系统健壮性和用户体验的关键环节。Spring Boot 作为一个功能强大的 Java 框架,提供了灵活的异常处理机制,能够统一管理应用程序中的错误,提升代码可维护性和响应一致性。2025 年,…

学习记录:DAY19

Docker 部署与项目需求分析 前言 人总是本能地恐惧未知,令生活陷入到经验主义的循环之中。但我们终将面对。今天的目标是把 Docker 部署学完,然后对项目进行需求分析。 日程 下午 4:30:Docker 部署项目部分学完了,做下笔记。晚…