说说JVM的垃圾回收机制

简介

垃圾回收机制英文为Garbage Collection, 所以我们常常称之为GC。那么为什么我们需要垃圾回收机制呢?如果大家有了解过Java虚拟机运行时区域的组成(JVM运行时存在,本地方法栈,虚拟机方法栈,程序计数器,堆,方法区五个区域),我们知道本地方法栈,虚拟机方法栈和程序计数器是线程独有的,即随着线程的存在而存在,随着线程的消亡而消亡,其内存分配和回收都具备确定性所以不用过多考虑回收问题。但是Java堆(主要存放对象)和方法区(存放常量和静态的属性,方法,对象)由于只有在运行时才知道有多少对象,所以这部分内存的分配和回收时动态的,此时就需要我们的垃圾回收机制来进行处理。

Point 1:如何判断对象是否存活

目前有两种方法来判断对象是否存活,第一种是引用计数算法,第二种是可达性分析算法。接下来我们对他们逐一进行讲解:

  • 引用计数算法:在对象中添加一个引用计数器,每当该对象被引用,计数器值就加一;引用失效就减一;当计数器为0,该对象就是不可使用的。虽然占用了一些额外空间,但是优点是效率高,但是存在一个问题,即:当两个对象互相引用时,虽然他们都不能被访问了,但是由于互相引用的原因,导致计数器为1,所以会成为漂浮垃圾,无法被回收。
  • 可达性分析算法:即在程序进行到某一点时,判断该对象能否被引用。我们可以举例结合图像来说明,本质上我们通过GC Root Set 来维护一个根集合,其中存在很多根对象节点,从这些节点向下走,经过的路径称为引用链,如若链中节点对根节点不可达,那么说明该对象不可使用了。该算法不能在应用程序活跃运行时执行对象追踪,因为执行上下文和对象图都在持续变化中,应用程序执行与可达性分析是一个竞态条件
  • 固定可做GC Roots 对象包括以下几种:
    • 在虚拟机栈中引用的对象
    • 在方法区中静态属性引用的对象
    • 在方法区中常量引用的对象
    • 在本地方法栈中JNI引用的对象
    • Java虚拟机内部的引用
    • 所有被同步锁持有的对象
    • 反射Java虚拟机内部情况的 JMXBean, JVMTI中注册的回调、本地代码缓存等

引用分类:

  1. 强引用
  2. 软引用
  3. 弱引用
  4. 幻影引用

JVM判断对象死亡过程

要真正宣布一个对象死亡需要精力两次标记过程,如若一个对象发现对于 GC Root 是不可达的,那么它将会被第一次标记。如若对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过了,那么虚拟机将这两者情况视作“没必要执行”。如若对象被确定为需要进行finalize()方法,那么该对象会被放到一个称为F-Queue的队列之中,并由一个新线程去执行finalize()方法。此时虚拟机还会对F-Queue队列中的对象进行一次小规模标记,如若对象在finalize()方法中成功自救,即连接上可达GC Root 对象的节点,即可逃脱回收。

需要注意的一点是:任何一个对象的finalize()方法都只会被系统自动调用一次,意味着如果第一次逃脱了,那么第二次如果被标记就会直接回收,不在进行finalize()方法。

Point 2:垃圾收集算法

主要有两类:“引用计数式” -- 直接垃圾收集 & “追踪式垃圾收集” -- 间接垃圾收集

分代收集

根据弱分代假说强分代假说,我们的收集器应该将Java堆划分出不同的区域,然后将回收对象依据其熬过垃圾收集过程的次数将其分配到不同区域中存储。所以目前我们的Java虚拟机一般存在新生代老年代。根据跨代引用假说我们可以得知跨代引用虽然存在,但是较于同代引用只占少数,因此我们引出了一个新的数据结构——记忆集,该数据结构用于吧老年代划分为若干小块,标记老年代哪一块内存存在跨代引用,如若新生代发生了GC的话,只用将对应小块内存加入到GC Root进行扫描就ok了。

注:新生代收集(Minor GC/ Young GC) / 老年代收集(Major GC/ Old GC) / 混合收集(Mixed GC) / 整堆收集(Full GC)

说了这么多关于收集器的分代概念,大家是否对虚拟机如何进行收集有疑问呢?接下来就来说说关于虚拟机的收集算法。主要分为三种:标记清除法,标记复制法,标记整理法。

  • 标记——清除算法:顾名思义,该算法分为两步,首先标记所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
    • 缺点:1. 执行效率不太稳定,会随着对象数量增长降低回收(标记和回收)效率。 2. 存在内存空间碎片化问题,由于每次标记和清楚都是碎片化的操作,这样就会引出一个问题,当我们需要分配一个大的对象的时候,就无法在新生代找到一个合适的区域放置这个对象,这时会触发另外一次垃圾回收动作。
  • 标记——复制算法:为了解决上一算法存在的缺点1,我们提出了该算法,又被称为半区复制算法,即将内存容量分为两个等大区域,每次只使用其中一块,如若被使用那块被用完了就将还存活的对象迁移至另一块内存空间,这个算法也避免了上述算法的缺点2。
    • 但是同样的这个算法也存在缺点:1. 大量的内存复制开销。2. 针对大多数对象都是存存活的状况,使用标记复制算法就欠妥了。3. 将可用内存空间变为内存的一半。
    • 所以针对新生代,朝生夕灭的特点,我们可以判断出标记复制算法可以用于新生代而且由于对象存活时间短数量少,我们可以将内存空间的划分做一些改变,比如按照 8:1 的比例进行分配,选取一个占比为 1/ 9 的空间作为固定的存放存货对象的空间。但是这样就会引出一个新问题,如果超过了 1/9 空间的对象存活,那么就存放不下了。这时我们就需要内存分配担保机制将新生代无法存放的存活对象直接存入老年代。
  • 标记——整理算法:由上文可知,标记复制算法的变型用于新生代回收特别方便,那么对于我们的老年代呢?根据老年代特性,我们知道其中大部分对象都是存活的,只会有少量对象需要回收。我们可以使用标记——整理的方法来解决,即标记活的对象,然后将存活对象移至一端,然后将须回收对象移至一端,我们就只清除这一端就ok了。但是这样也带来一个问题,既然老年代大多数对象都是存活的,那么每次这样移动操作对系统消耗较大而且也会导致"Stop The World" 发生。所以我们有了一种新的办法来处理老年代对象,即让虚拟机在大多数时间都采用标记——清除算法,知道内存碎片已经影响到对象分配了,我们就采用标记——整理算法对空间进行一次整理,得到一个规整的内存空间。

Point 3:HotSpot 算法实现细节

根节点枚举

由于需要查询对象引用关系,所有的收集器在根节点枚举这一步骤都是要暂停用户线程的。由于当前主流Java虚拟机采用的都是准确式垃圾收集,用户线程停顿后不需要逐个检查执行上下文,只需得到哪些地方存储着对象引用。例如HostSopt采用的就是OopMap来记录对象信息。

安全点

在OopMap的协助下,HotStop可以快速准确的完成GC Root枚举,但是导致OopMap变化的指令很多,如若每个都生成OopMap那么将会需要大量额外空间。事实上HotSopt也只在安全点进行OopMap生成,即只有到了安全点,才可以暂停用户线程进行GC。中断也有两种方式:

  1. 抢断式中断:抢断式中断不需要线程执行代码主动配合,发生GC时,会中断所有的用户线程,如果发现有用户线程中断的地方不在安全点上,就恢复这条线程知道他到达安全点再度中断。
  2. 主动式中断:不直接对线程操作,仅仅简单的设置一个标志位,每个线程都不停的轮询这个标志位,一旦发现标志位为中断就主动挂起。这个标志位和安全点是重合的。

安全区域

可以理解为加长版的安全点。如果程序在执行,它可以运行到安全点,如果程序不执行,如线程处于Sleep或者Blocked状态,这时候线程无法响应虚拟机中断请求,自然就无法走到安全点去挂起自己。所以安全区就是在某一段代码中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集是安全的。线程进入安全区域会标识自己,离开时会检查虚拟机是否完成了根结点枚举,如若没有收到就会被要求一直等待直到收到可以离开的信号。

Point 4:记忆集与卡表

在前文中我们提到了记忆集的概念,即用于解决查询跨代引用代价过大问题而引申出来的数据结构。

定义:记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。

有三种精度,分别是:1.字节精度,2. 对象精度, 3. 卡精度。三者中最常用的就是卡精度,顾名思义,采用卡表来实现记忆集,它定义了记忆集的记录精度与堆内存映射关系。可以联想为Java中Map。一个卡页通常包括不止一个对象,如若卡页里面有超过一个对象字段的跨代指针,那么就将对应卡表的数组元素的值设置为1,称为这个卡表变脏(dirty) 否则就是0。

卡表变脏的实际就是有其他分代区域中的对象引用了本区域的对象时,卡表就会变脏。

Point 5:写屏障

我们知道了卡表变脏时机,那么我们将如何来将卡表更新呢?由于编译过程是机器码,所以虚拟机就不方便介入,于是在我们的HotSpot中存在一个写屏障,可以看作虚拟机层面对“引用类型字段赋值”这个动作的AOP切面,在引用对象赋值时会产生一个环形通知,赋值前后都在写屏障覆盖范畴内。

一些收集器

垃圾回收器我就不过多赘述了,本质上就是对垃圾回收算法的实践。有很多版本比如 CMS, ZGC等等,如读者有兴趣可自行搜索学习。

参考资料:

《深入理解Java虚拟机》

《虚拟机设计与实现,以JVM为例》

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

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

相关文章

MLC-LLM框架的安卓应用部署实战

这几天根据官网教程把MLC-LLM在安卓端部署了一下,中间遇到了不少问题,也搜集了不少解决方案,同时也结合了别人的实践经历,现分享总结如下。 感谢博主tao_spyker的文章基于MLC LLM将Llama2-7B模型部署至Android手机运行&#xff0c…

程序人生——Java异常使用建议

目录 引出异常建议110:提倡异常封装;建议111:采用异常链传递异常 建议112:受检异常尽可能转化为非受检异常建议113:不要在finally块中处理返回值 建议114:不要在构造函数中抛异常建议115:使用Th…

【Exception系列】SocketTimeoutException

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

学习vue3第五节(reactive 及其相关)

1、定义 reactive() 创建一个响应式代理对象,不同于ref()可以创建任意类型的数据,而reactive()只能是对象,会响应式的深层次解包任何属性,将其标注为响应式 响应式是基于ES6的proxy实现的代理对象,该proxy对象与原对象…

单片机第四季-第一课:RTOS

1,RTOS来龙去脉 操作系统是什么? 以人类社会类比,小公司三四个人都是干活的,大公司有几万人其中有几千人从事管理工作,他们的工作是让其他人的干活效率更高。 51单片机为什么没有操作系统,因为51的性能太…

人脸检测的5种实现方法

众所周知,人脸识别是计算机视觉应用的一个重大领域,在学习人脸识别之前,我们先来简单学习下人脸检测的几种用法。 常见的人脸检测方法大致有5种,Haar、Hog、CNN、SSD、MTCNN: 相关构造检测器的文件:opencv…

服务器机器学习环境搭建(包括AanConda的安装和Pytorch的安装)

服务器机器学习环境搭建 1 服务器与用户 在学校中,我们在学校中是以用户的身份进行访问学校的服务器的。整体框架大致如下: 我们与root用户共享服务器的一些资源,比如显卡驱动,Cuda以及一些其他的公共软件。 一般情况下&#…

力扣新思路题:字符串轮转

非常简单的思路:将两个字符串s1接起来,并判断s2字符串是否是加长版s1字符串的子串 bool isFlipedString(char* s1, char* s2){if (strlen(s1) ! strlen(s2)) {return false;}int len strlen(s1);int i 0;char* arr (char*)malloc(sizeof(char) * len…

Netty之@Sharable注解

1.背景 熟悉Netty的人都了解,netty为每个连接的客户端都会创建一个channel用于通信相关。每个channel都绑定到一条channelpipeline上。而channelpipeline里由一个一个的节点channelhandler组成(严格来说是channelhandlercontext)。如果为所有…

【蓝桥杯选拔赛真题38】C++判断数字 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解析

目录 C判断数字 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C判断数字 第十四届蓝桥杯青少年创意编程大赛C选拔赛真题 一、题目要求 1、编程实现 给定一个正整数N(100≤N<100000)…

nginx实时流量拷贝ngx_http_mirror_module

参考&#xff1a; Module ngx_http_mirror_module Nginx流量拷贝ngx_http_mirror_module模块使用方法详解 ngx_http_mirror_module用于实时流量拷贝 请求一个接口&#xff0c;想实时拷贝这个请求转发到自己的服务上&#xff0c;可以使用ngx_http_mirror_module模块。 官网好像…

(done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW

一个视频&#xff1a;https://www.bilibili.com/video/BV1mb4y1y7EB/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 这里有个视频&#xff0c;讲解得更加生动形象一些 总得来说&#xff0c;词袋模型(Bow, bag-of-words) 是最简…

PHP+golang开源办公系统CRM管理系统

基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约&#xff0c;易于功能扩展&#xff0c;方便二次开发。 服务器运行环境要求 PHP > 7.…

手搭手RocketMQ发送消息

消息中间件的对比 消息中间件 ActiveMQ RabbitMQ RocketMQ kafka 开发语言 java erlang java scala 单击吞吐量 万级 万级 10万级 10万级 时效性 ms us ms ms 可用性 高(主从架构) 高(主从架构) 非常高(主从架构) 非常高(主从架构) 消息中间件: acti…

LabVIEW湍流等离子体束热效率优化

LabVIEW湍流等离子体束热效率优化 利用LabVIEW虚拟仪器技术&#xff0c;对湍流等离子体束的热效率进行了实时监测与优化&#xff0c;提高其在材料处理领域的应用效率和精度。通过双进气湍流等离子体发生器&#xff0c;实现了在不同工作参数下对热效率的实时在线监测&#xff0…

Luckysheet + Exceljs:H5实现Excel在线编辑、导入、导出及上传服务器的示例代码(完整版demo)

创建xeditor.html <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>Hello World!</title><!-- <link relstylesheet href./luckysheet/plugins/css/pluginsCss.css /><link relstylesheet href./luck…

idea找不到或无法加载主类

前言 今天在运行项目的时候突然出了这样一个错误&#xff1a;IDEA 错误 找不到或无法加载主类,相信只要是用过IDEA的朋友都 遇到过它吧&#xff0c;但是每次遇到都是一顿焦头烂额、抓耳挠腮、急赤白咧&#xff01;咋整呢&#xff1f;听我给你吹~ 瞧我这张嘴~ 问题报错 找不…

vscode通过多个跳板机连接目标机(两种方案亲测成功)

1、ProxyJump&#xff08;推荐使用&#xff09; 需要OpenSSH 7.3以上版本才可使用&#xff0c;可用下列命令查看&#xff1a; ssh -V ProxyJump命令行使用方法 ssh -J [email protected]:port1,[email protected]:port2 一层跳板机&#xff1a; ssh dst_usernamedst_ip -…

ARP和DDOS攻击防御介绍

学习目标&#xff1a; 1. 如何利用ARP漏洞进行攻击&#xff1f; 2. 怎样有效地防御ARP攻击&#xff1f; 3. 如何应对DDOS攻击&#xff1f; ARP攻击如何产生&#xff1f; ARP如何进行有效防御&#xff1f; ARP基础工作原理&#xff1a; 交换机会根据mac地址表&#xff0c;进行转…

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向&#xff1a;计算机视觉&#xff0c;自然语言&#xff0c;语音识别。 机器学习核心组件&#xff1a;1 数据集(data)&#xff0c;2 前向传播的model(net)&#xff0c;3 目标函数(loss)&#xff0c; 4 调整模型参数和优化函数的算法…