JVM虚拟机系统性学习-对象存活判断算法、对象引用类型和垃圾清除算法

news2024/9/19 10:52:40

垃圾回收

在 JVM 中需要对没有被引用的对象,也就是垃圾对象进行垃圾回收

对象存活判断算法

判断对象存活有两种方式:引用计数法、可达性分析算法

引用计数法

引用计数法通过记录每个对象被引用的次数,例如对象 A 被引用 1 次,就将 A 的引用计数器加 1,当其他对象对 A 的引用失效了,就将 A 的引用计数器减 1

  • 优点:
    • 实现简单,判定效率高
  • 缺点:
    • 需要单独的字段存储计数器,增加存储空间开销
    • 每次赋值都要更新计数器,增加时间开销
    • 无法处理循环引用的情况,致命问题!即 A 引用 B,B 引用 A,那么他们两个的引用计数器永远都为 1

可达性分析算法

可达性分析算法可以有效解决循环引用的问题,Java 选择了这种算法

可达性分析算法以根对象集合(GC Roots)为起使点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达,通过可达性分析算法分析后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索过程所走过的路径称为引用链,如果目标对象没有任何引用链相连,则是不可达的,就可以标记为垃圾对象

GC Roots 主要包含以下几类元素:

  • 虚拟机栈中引用的对象

    如:各个线程被调用的方法中所使用的参数、局部变量等

  • 本地方法栈内的本地方法引用的对象

  • 方法区中引用类型的静态变量

  • 方法区中常量引用的对象

    如:字符串常量池里的引用

  • 所有被 synchronized 持有的对象

  • Java 虚拟机内部的引用

    如:基本数据类型对应的 Class 对象、异常对象(如 NullPointerException、OutOfMemoryError)、系统类加载器

垃圾回收过程

在 Java 中对垃圾对象进行回收需要至少经历两次标记过程:

  • 第一次标记:如果经过可达性分析后,发现没有任何引用链相连,则会第一次被标记
  • 第二次标记:判断第一次标记的对象是否有必要执行 finalize() 方法,如果在 finalize() 方法中没有重新与引用链建立关联,则会被第二次标记

第二次被标记成功的对象会进行回收;否则,将继续存活

对象的 finalization 机制:

Java 提供了 finalization 机制来允许开发人员 自定义对象被销毁之前的处理逻辑,即在垃圾回收一个对象之前,会先调用这个对象的 finalize() 方法,该方法允许在子类中被重写,用于在对象被回收时进行资源释放的工作

对象引用

在 JDK1.2 之后,Java 对引用的概念进行了扩张,将引用分为强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference)四种,这四种引用强度依次逐渐减弱

  • 强引用-不回收:强引用是最普遍的对象引用,也是默认的引用类型,强引用的对象是可触及的,垃圾回收器永远不会回收被引用的对象,因此强引用是造成Java内存泄漏的主要原因之一

    • 当使用new操作创建一个新对象时,并且将其赋值给一个变量时,这个变量就成为该对象的一个强引用
  • 软引用-内存不足回收:在即将发生内存溢出时,会将这些对象列入回收范围进行第二次回收,如果回收之后仍然没有足够的内存,则会抛出内存溢出异常

    • 软引用通常用来实现内存敏感的缓存,例如高速缓存使用了软引用,如果内存足够就暂时保留缓存;如果内存不足,就清理缓存

      // 创建弱引用
      SoftReference<User> softReference = new SoftReference<>(user);
      // 从软引用中获取强引用对象
      System.out.println(softReference.get());
      
  • 弱引用-发现即回收:被弱引用关联的对象只能存活在下一次垃圾回收之前,在垃圾回收时,无论空间是否足够,都会会受掉被弱引用关联的对象

    • 弱引用常用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的 isEnQueued 方法判断对象是否被垃圾回收器标记

      Object obj = new Object();
      WeakReference<Object> wf = new WeakReference<Object>(obj);
      obj = null;
      // System.gc();
      // 有时候会返回null
      Object o = wf.get(); 
      // 返回是否被垃圾回收器标记为即将回收的垃圾
      boolean enqueued = wf.isEnqueued(); 
      System.out.println("o = " + o);
      System.out.println("enqueued = " + enqueued);
      
  • 虚引用:垃圾回收时,直接回收,无法通过虚引用获取对象实例

    • 为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾回收时收到一个系统通知

      Object obj = new Object();
      PhantomReference<Object> pf = new PhantomReference<Object>(obj, new
      ReferenceQueue<>());
      obj=null;
      // 永远返回null
      Object o = pf.get();
      // 返回是否从内存中已经删除
      boolean enqueued = pf.isEnqueued();
      System.out.println("o = " + o);
      System.out.println("enqueued = " + enqueued);
      

垃圾清除算法

GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。

  • 标记-清除算法:在标记阶段,从 GC Roots 开始遍历,标记所有被引用的对象,标记为可达对象,再对堆内存从头到尾遍历,回收没有标记为可达对象的对象(标记清除算法可以标记存活对象也可以标记待回收对象)

    • 这里并不是真正清除,而是将清除对象的地址放在空闲的地址列表中
    • 缺点
      • 效率不高
      • GC 时需要停止整个应用进程,用户体验不好
      • 会产生内存碎片

    在这里插入图片描述

  • 复制算法:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉

    现在商用的 Java 虚拟机大多都优先采用这种收集算法去回收新生代,如果将内存区域划分为容量相同的两部分太占用空间,因此将复制算法进行了优化,优化后将新生代分为了 Eden 区、Survivor From 区、Survivor To 区,Eden 和 Survivor 的大小比例为 8:1:1,每次分配内存时只使用 Eden 和其中的一块 Survivor 区,在进行垃圾回收时,将 Eden 和已经使用过的 Survivor 区的存活对象转移到另一块 Survivor 区中,再清理 Eden 和已经使用过的 Survivor 区域,当 Survivor 区域的空间不足以容纳一次 Minor GC 之后存活的对象时,就需要依赖老年代进行分配担保(通过分配担保机制,将存活的对象放入老年代即可)

    • 优点
      • 实现简单,运行高效
      • 复制之后,保证空间的连续性,不会出现“内存碎片”
    • 缺点
      • 存在空间浪费
    • 应用场景
      • 在新生代,常规的垃圾回收,一次可以回收大部分内存空间,剩余存活对象不多,因此现在的商业虚拟机都是用这种收集算法回收新生代

    在这里插入图片描述

  • 标记-压缩算法:标记过程仍然与“标记-清除”算法一样,之后将所有的存活对象压到内存的一端,按顺序排放,之后,清理边界外的内存

    • 优点
      • 解决了标记-清除算法出现内存碎片的问题
      • 解决了复制算法中空间浪费的问题
    • 缺点
      • 效率上低于复制算法
      • 移动对象时,如果对象被其他对象引用,则还需要调整引用的地址
      • 移动过程中,需要暂停用户应用程序。即 STW

    在这里插入图片描述

  • 分代收集算法:把 Java 堆分为新生代和老年代,这样就可以对不同生命周期的对象采取不同的收集方式,以提高回收效率

    当前商业虚拟机都采用这种算法

    • 新生代中的对象生命周期短,存活率低,因此适合使用复制算法(存活对象越少,复制算法效率越高)
    • 老年代中对象生命周期长,存活率高,回收没有新生代频繁,一般使用标记-清除或者是标记-压缩

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

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

相关文章

被迫搬家,宽带迁移怎么办?

广州一栋违建烂尾楼&#xff0c;13年里从未停止出租&#xff0c;年年住满人。这栋楼没有贴外墙&#xff0c;裸露的水泥表面都被雨水腐蚀&#xff0c;很多阳台没有建好&#xff0c;只是简单加装了护栏&#xff0c;存在巨大安全隐患。 为什么烂尾楼年年满人呢&#xff1f; 因为它…

STL--关联式容器底层实现

关联式容器从底层实现分为两类&#xff1a;①红黑树&#xff1a;set、map、multiset和multimap ②哈希&#xff1a;unordered_set、unordered_map、unordered_multiset和unordered_multimap。 一、红黑树 它是⼀种特殊的⼆叉查找树。红⿊树的每个节点上都有存储位表示节点的颜…

Java入门项目--蚂蚁爱购

简介 这是一个靠谱的Java入门项目实战&#xff0c;名字叫蚂蚁爱购。 从零开发项目&#xff0c;视频加文档&#xff0c;十天就能学会开发JavaWeb项目&#xff0c;教程路线是&#xff1a;搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Ja…

vue3项目中使用vue-cropper实现截图效果

前言&#xff1a; 自用&#xff01;&#xff01;&#xff01; 插件文档链接&#xff1a; https://www.npmjs.com/package/vue-cropper 使用步骤&#xff1a; 1、安装 npm install vue-cropper 2、项目中引入&#xff08;全局引入&#xff09;&#xff1b;文档中有介绍如何局…

深度学习——第3章 Python程序设计语言(3.7 matplotlib库)

3.7 matplotlib库 目录 1 matplotlib库简介 2 pyplot的plot函数 3 matplotlib基础绘图函数示例 数据可视化有助于深度理解数据。 本节介绍绘制图形的基本方法。 1. matplotlib库简介 matplotlib官网 1.1 matplotlib库概述 matplotlib是Python优秀的数据可视化第三方库&a…

ChatGPT/GPT4+AI绘图+论文高效写作结合到底有多强大?你可以躺赢了

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

LLM之RAG实战(三):LlamaIndex解锁自定义LLM关键步骤分块、索引、查询介绍

一、LLamaIndex介绍 LLamaIndex是一个由Jerry Liu创建的Python库&#xff0c;它可以使用语言模型对大型文档集进行高效的文本搜索和摘要。开发人员可以使用LLamaIndex快速添加私有/自定义数据&#xff0c;以增强现有的LLM。它不需要对大模型进行再训练&#xff0c;即可提供个性…

Todesk、向日葵等访问“无显示器”主机黑屏问题解决

我的环境是 ubuntu 22.04 安装 要安装 video dummy&#xff0c;请在终端中运行以下命令&#xff1a; sudo apt install xserver-xorg-video-dummy配置 video dummy 的配置文件请自行搜索 使用任何文本编辑器打开此文件。 我的是 /etc/X11/xorg.conf 默认配置文件包含以下内…

每日一练【长度最小的子数组】

一、题目描述 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0 。 二、题目解析 经…

在我们日常生活有哪些东西是激光切割机做的

激光加工技术是当前最先进的切割技术&#xff0c;在工业生产中&#xff0c;能解决许多常规方法无法解决的难题&#xff0c;而且应用非常广。让人意想不到的是&#xff0c;很多生活中常见的物品&#xff0c;都是靠激光切割完成的。 它具备精密制造、柔性切割、异形加工、一次成型…

数据取证工具MemProcFS

MemProcFS 是一种将物理内存视为虚拟文件系统中的文件的简便方法。简单的点击内存分析&#xff0c;无需复杂的命令行参数&#xff01;通过安装的虚拟文件系统中的文件或通过功能丰富的应用程序库访问内存内容和工件以包含在您自己的项目中。 链接GitHub - ufrisk/MemProcFS: M…

nodejs+vue+微信小程序+python+PHP的外卖数据分析-计算机毕业设计推荐django

构建一种完全可实现、可操作的开放源代码信息收集系统&#xff0c;帮助记者完成工作任务。采编人员仅需输入所收集到的网址及题目即可迅速启动收集工作并进行信息归类。 2.根据新的数据收集要求&#xff0c;采用云计算技术实现新的收集器的迅速部署。对于资料采集点的改版&…

深度学习 Day11——T11优化器对比实验

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 文章目录 前言一、我的环境二、代码实现与执行结果1.引入库2.设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;3.导入数据4.查…

WordPress插件你好多莉( Hello Dolly )可否卸載

什么是你好多莉Hello Dolly WordPress插件 你好多莉是 WordPress插件 简单的预装在 WordPress 上。 如果您激活后者&#xff0c;它将显示出名曲的歌词“ 你好&#xff0c;多莉 “要 路易斯阿姆斯特朗. 您可能已经注意到&#xff0c;在阅读插件说明时&#xff0c;还不够清楚。 …

系列十五、搭建redis集群

一、概述 上篇文章介绍了redis集群的相关知识&#xff0c;本章实战演示redis的集群环境的详细搭建步骤。如果帮助到了你&#xff0c;请点赞 收藏 关注&#xff01;有疑问的话也可以评论区交流。 二、搭建步骤 2.1、预备知识 判断一个集群中的节点是否可用&#xff0c;是集群…

办公教程分享:网址/文本/电子名片如何批量生成二维码?

日常办公时&#xff0c;我们经常会需要批量生成很多二维码&#xff0c;比如做会议问卷调查、为同事制作名片&#xff0c;再比如策划市场活动、制作设备标签…等都会需要用到大量的二维码。这时候&#xff0c;如果手动一个一个生成二维码&#xff0c;会非常浪费时间和精力。 为…

香港高才通计划申请被拒了?很可能是因为这五个原因!

香港高才通计划申请被拒了&#xff1f;很可能是因为这五个原因&#xff01; 据统计&#xff0c;截止今年10月31日&#xff0c;香港各项输入人才计划共收到超过18万宗申请&#xff0c;超过11万宗已被批准&#xff0c;已有7万人到港。其中&#xff0c;高才通计划一共收到约55,000…

在Java中如何实现一个呼吸动画效果

前言 这里用到了数学中的正弦函数&#xff0c; y s i n ( x ) y sin(x) ysin(x)&#xff0c;sin的周期为2pi 下面的类中实现了一个圆&#xff0c;圆的半径根据 s i n ( x ) sin(x) sin(x)函数的值不断变化和修改&#xff0c;x取值范围[0-2pi]&#xff0c;onTick方法负责修改…

17.(vue3.x+vite)组件间通信方式之作用域插槽

前端技术社区总目录(订阅之前请先查看该博客) 示例效果 作用域插槽 父组件中的插槽内容是无法访问到子组件中的数据的,而作用域插槽就是解决获取子组件数据。 父组件代码 <template><div><div>父组件--Hello World!</div><Child>

短视频怎么批量添加水印logo

在现代数字化时代&#xff0c;视频内容已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;当我们辛辛苦苦制作的视频在网络上分享时&#xff0c;常常会遇到被他人盗用或未经授权使用的情况。为了保护我们的创作成果&#xff0c;给视频添加水印logo成为了一种常见的手段…