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

细说 Java 引用(强、软、弱、虚)和 GC 流程(二)

一、前文回顾

在 细说Java 引用(强、软、弱、虚)和 GC 流程(一) 我们对Java 引用有了总体的认识,本文将继续深入分析 Java 引用在 GC 时的一些细节。

还是从我们在前文中提到的引用流程图里说起,这里不清楚的请回 细说Java 引用(强、软、弱、虚)和 GC 流程(一) 中查阅。

在这里插入图片描述

图 0-0—— 引用流程图

本文将重点关注图示{3}这部分细节:

  • GC线程是如何把Reference对象收集到pending队列的?
  • Reference对象和我们正常的对象有什么不同,为啥{1}是强引用,{2}是非强引用?
  • 按照GC可达性算法,就算{1}不存在,但通过{2}依然可以找到我们的对象,那对象就是可达的啊?
    在这里插入图片描述
    图 0-1—— GC可达性分析图

我们先来看引用对象和正常对象里字段的区别,即上图对象weakReference里的referentxx_oo里的 _oo 的秘密?

二、引用可达

按照GC可达性算法,【图 0-1—— GC可达性分析图】中,对象ooo均是可达的,但是GC时,GC线程扫描到引用对象weakReference时,会跳过实例变量referent的扫描,从而导致对象o不可达;接下来让我们一探究竟。

1.1、 OopMapBlock 简析

1.1.1、 OopMapBlock 概述

GC扫描实例对象时,会通过一个叫做OopMapBlock的类(C++写的),这个类里存放了我们java类的引用类型(基本类型不会存放)的成员变量;换言之,OopMapBlock类里存在变量X,那就可以顺藤摸瓜找到X对象,否则,X就是不可达;

我们的Reference类对应的OopMapBlock类中就跳过了变量referent

实际使用时若我们在一个java类里定义了成百上千的引用类型变量,那OopMapBlock岂不是也得存放成百上千的引用型变量,这还了得;所以为了避免这种情况,OopMapBlock里只有2个变量:

  • _offset:连续实例变量中第一个实例变量位置;
  • _count:连续实例变量个数;

注意,这里只记录了一个java类里连续的实例变量(静态变量本来就是GC-Root,所以不需要记录),如下图,直接跳过静态变量,递归遍历实例变量,就可以知道java对象是否可达。

如果实例变量被静态变量隔开,那就再来一个OopMapBlock,把所有OopMapBlock放入数组即可;所以一个java类中存在一个OopMapBlock数组。这个数组是JVM加载类时动态分析后生成的,然后把这个数组存放在了 InstanceKlass 对象中(可以查阅 JVM层面的JAVA类和实例(Klass-OOP) 了解InstanceKlass 的知识)。

java 实例对象头部中有 Klass 类型指针;这样,GC线程扫描堆中实例对象时就可以通过InstanceKlass 对象找到这个OopMapBlock,并据此构造引用链,标记对象是否可达。

  • java中普通类在JVM层面对应的是InstanceKlass 对象;
  • java.lang.ref.Reference对应的是InstanceRefKlass对象;
  • 以上区分就是为了针对类java.lang.ref.Reference做定制化的OopMapBlock,从而跳过变量referent的引用扫描;
  • InstanceKlass 对象和InstanceRefKlass对象均存放再JVM方法区中;

在这里插入图片描述
在这里插入图片描述

1.1.2、 OopMapBlock 代码

// Describes where oops are located in instances of this klass.
class OopMapBlock {public:// Byte offset of the first oop mapped by this block.int offset() const          { return _offset; }void set_offset(int offset) { _offset = offset; }// Number of oops in this block.uint count() const         { return _count; }void set_count(uint count) { _count = count; }private:int  _offset;uint _count;
};

InstanceKlass 使用了 OopMapBlock :
在这里插入图片描述

1.2、 GC时引用对象收集流程

解决完引用对象的可达性问题,我们来看引用对象是怎么被发现和收集到 pendling 队列中的 ,即文章开始提到的【图 0-0—— 引用流程图】中图示{3}的细节。

1.2.1、标记阶段

这个阶段中,GC线程和应用线程并发执行,并没有产生 STW(Stop The world);这个阶段主要做的是找到可以回收的引用对象,并全部收集起来。

  • 如下图所示,我们假设有2个GC-ROOT,分别为GC-ROOT_1GC-ROOT_2

  • 一个_discovered_list 队列用于临时存放 GC 线程在并发标记过程中发现需要回收的 Reference 对象;

    每一个 GC 线程都有一个_discovered_list

    并发标记结束之后,这些 GC 线程就会将各自在 _discovered_list 中收集到的 Reference 对象统一转移到 pending 队列中,以便后续ReferencHandler线程消费;

  • _discovered_list入队条件:

    1. Reference 对象引用的 referent 没有被 GC 标记过,图示 obj_c;
    2. Reference 对象的状态不能是 inactive, 也就是说这个 Reference 对象还没有被应用线程处理过,Reference 之前没有加入过 _discovered_list,图示WeakReference_x;
    3. referent 不存在任何强引用链,图示 obj_c,referent指的就是obj_c;
    4. 内存充足的前提下,referent 不存在任何软引用(若内存不足,就忽略这条);
  • _nonstatic_oop_mapsInstanceKlass 对象的变量,存放就是我们之前提到的 OopMapBlock 数组;

在这里插入图片描述

  1. GC线程从GC-ROOT_1出发标记对象;
  2. 标记对象obj_aobj_b状态为存活;obj_cobj_pobj_qobj_dobj_f对象均不可达;
  3. 遍历到引用对象SofeReference_a,发现SofeReference_areferent即对象obj_b为存活,所以放弃将SofeReference_a加入到_discovered_list队列中;
  4. 遍历到引用对象WeakReference_b,发现WeakReference_breferent即对象obj_c还未标记,先将SofeReference_b加入到_discovered_list队列中,这里采用头插法;同理,引用对象WeakReference_zWeakReference_x 也加入到_discovered_list队列;
  5. WeakReference_y不会遍历到,已经是垃圾了,不会入队到_discovered_list队列;
  6. GC线程从GC-ROOT_2出发标记对象;
  7. FinalReference_q对象因为后续要执行obj_q.finalize()方法;所以需要将obj_q对象重新标记为复活状态;同时将FinalReference_q对象加入到_discovered_list队列;

1.2.2、二次确认阶段

本阶段将进一步确认阶段一中的_discovered_list,将标记错误Reference对象的移出队列,标记正确的Reference对象将其referent置为null,即断开与obj对象的连接;

注意FinalReference对象

  • FinalReference 对象不会断开与obj的连接,方便后面执行obj.finalize()方法;
  • 当后面执行完obj.finalize()方法后,referent 才会被置为 null , 在下一轮 GC 的时候, 这个 FinalReference 对象以及它的 referent (obj_q)对象就会被 GC 掉;
  • 如下图所示,惊不惊喜,意不意外,SofeReference_a 对象竟然在我们的队列里,这明显有问题啊,问题是上面我们说了放弃将SofeReference_a 入队啊,怎么回事?

    当阶段一中,图示{3} 标记快于 {1},遍历到SofeReference_a 时发现obj_b还未标记为可达,有可能进入_discovered_list 队列;

  • WeakReference_x 对象也是标记错误的,因为阶段一中应用线程并未暂停,所有应用线程有可能将WeakReference_x自己处理了,我们收集引用对象的目的就是为了给应用线程后续处理,既然应用线程提前处理了,那GC线程没必要多此一举;

在这里插入图片描述

1.2.3、汇总阶段

在阶段一我们提到过每一个 GC 线程都有一个_discovered_list,所以需要将这些线程的_discovered_list统一收集到一起放在_pending_list中,然后再将数据转移到_reference_pending_list,腾出_pending_list空间,方便下次GC使用。
在这里插入图片描述

至此,我们已经可以回答文章开始提到的问题了。

1.3、 软引用GC时的处理

我们都知道,在内存不足时,只有软引用的对象才会被回收,那什么才是内存不足?或者说只有软引用的对象在什么情况下一定会被回收?我们需要有一个量化标准:

  • 发生Full GC时一定会回收软引用,这很明显,毋庸置疑;

  • 只有软引用的对象存活时间达到我们设定的生命周期阈值;

    JVM提供了参数-XX:SoftRefLRUPolicyMSPerMB 可以设置每 MB 的堆内存剩余空间允许只有软引用的对象存活的最大时长,默认为 1000 , 单位为毫秒(MS);

    参数-XX:SoftRefLRUPolicyMSPerMB 中有LRU,说明这个参数可以按照LRU(Least Recently Used,即最近最少使用)策略调整,即并非所有的软引用对象一起被GC掉;

    参数-XX:SoftRefLRUPolicyMSPerMB 中有PerMB,所以我们GC时需要计算剩余空间,二者乘积就是我们要的最终结果;
    举例:-XX:SoftRefLRUPolicyMSPerMB=2000,剩余空间为20MB;则存活时间为40秒(20 * 2000);

1.3.1、 软引用LRU策略

在这里插入图片描述

如上图所示,SoftReference 中有两个字段:

  • clock:clock 字段是由 JVM 来设置的,在每一次发生 GC 的时候,JVM 都会去更新这个时间戳。
  • timestamp:每次调用get()方法获取referent时,更新为上次GC的时间;

对于当前只有软引用的对象而言,如果 clock - timestamp >= 剩余空间 * SoftRefLRUPolicyMSPerMB 时,则当前只有软引用的对象就可以直接回收了,也就是可以加入到我们在1.2.1小节中提到的_discovered_list 队列中了;

相关文章:

细说 Java 引用(强、软、弱、虚)和 GC 流程(二)

一、前文回顾 在 细说Java 引用(强、软、弱、虚)和 GC 流程(一) 我们对Java 引用有了总体的认识,本文将继续深入分析 Java 引用在 GC 时的一些细节。 还是从我们在前文中提到的引用流程图里说起,这里不清…...

CSS通过webkit-scrollbar设置滚动条样式

查看::-webkit-scrollbar-*各项关系 以下图为例&#xff0c;可以分别定义滚动条背景、滚动轨道、滚动滑块的样式。 需要先给外部容器设置高度&#xff0c;再设置overflow: auto&#xff0c;最后设置三个webkit属性。 <!DOCTYPE html> <html lang"en">…...

Win10配置VSCode的C/C++编译环境

GNU&#xff08;编译器工具集合&#xff09;包含了g、gcc和gdb等编译器。MinGW&#xff08;Minimalist GNU for Windows&#xff09;是一个适用于Windows操作系统的最小化的GNU工具集&#xff0c;它包括了GCC编译器&#xff08;包括g&#xff09;以及其他一些必要的库和工具。M…...

数据结构与算法再探(七)查找-排序

查找 一、二分查找 二分查找是一种高效的查找算法&#xff0c;适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。 二分查找关键点 不变量 在二分查找中&a…...

【C语言】指针(5)

前言&#xff1a;上篇文章的末尾我们使用了转移表来解决代码冗余的问题&#xff0c;那我们还有没有什么办法解决代码冗余呢&#xff1f;有的这就是接下来要说的回调函数。 往期文章: 指针1 指针2 指针3 指针4 文章目录 一&#xff0c;回调函数二&#xff0c;qsort实现快速排序1…...

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)

Paimon的下载及安装&#xff0c;并且了解了主键表的引擎以及changelog-producer的含义参考&#xff1a; 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 利用Paimon表做lookup join&#xff0c;集成mysql cdc等参考&#xff1a; 大数据组件(四)快速入门实时数据…...

PLC通讯

PPI通讯 是西门子公司专为s7-200系列plc开发的通讯协议。内置于s7-200 CPU中。PPI协议物理上基于RS-485口&#xff0c;通过屏蔽双绞线就可以实现PPI通讯。PPI协议是一种主-从协议。主站设备发送要求到从站设备&#xff0c;从站设备响应&#xff0c;从站不能主动发出信息。主站…...

前端js进阶,ES6语法,包详细

进阶ES6 作用域的概念加深对js理解 let、const申明的变量&#xff0c;在花括号中会生成块作用域&#xff0c;而var就不会生成块作用域 作用域链本质上就是底层的变量查找机制 作用域链查找的规则是:优先查找当前作用域先把的变量&#xff0c;再依次逐级找父级作用域直到全局…...

Scrum方法论指导下的Deepseek R1医疗AI部署开发

一、引言 1.1 研究背景与意义 在当今数智化时代&#xff0c;软件开发方法论对于项目的成功实施起着举足轻重的作用。Scrum 作为一种广泛应用的敏捷开发方法论&#xff0c;以其迭代式开发、快速反馈和高效协作的特点&#xff0c;在软件开发领域占据了重要地位。自 20 世纪 90 …...

LINUX安装使用Redis

参考 Install Redis on Linux | Docs 安装命令 sudo apt-get install -y lsb-release curl gpgcurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpgsudo chmod 644 /usr/share/keyrings/redis-archive-keyrin…...

基于java新闻管理系统,推荐一款开源cms内容管理系统ruoyi-fast-cms

一、项目概述 1.1 项目背景 在信息高速流通的当下&#xff0c;新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作&#xff0c;在新闻采集、编辑、发布以及后续管理等环节中&#xff0c;不仅效率低下&#xff0c;而且容易出现人为失误。同时&#xff0…...

054 redisson

文章目录 使用Redisson演示可重入锁读写锁信号量闭锁获取三级分类redisson分布式锁 package com.xd.cubemall.product.config;import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context…...

【数据结构】(12) 反射、枚举、lambda 表达式

一、反射 1、反射机制定义及作用 反射是允许程序在运行时检查和操作类、方法、属性等的机制&#xff0c;能够动态地获取信息、调用方法等。换句话说&#xff0c;在编写程序时&#xff0c;不需要知道要操作的类的具体信息&#xff0c;而是在程序运行时获取和使用。 2、反射机制…...

java实现二维码图片生成和编解码

java实现二维码图片生成和编解码 在wutool中&#xff0c;封装了二维码工具类&#xff0c;基于google的zxing库&#xff0c;实现二维码图片生成、编码和解码。 关于wutool wutool是一个java代码片段收集库&#xff0c;针对特定场景提供轻量解决方案&#xff0c;只要按需选择代…...

Java基础常见的面试题(易错!!)

面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…...

hugging face---transformers包

一、前言 不同于计算机视觉的百花齐放&#xff0c;不同网络适用不同情况&#xff0c;NLP则由Transformer一统天下。transformer是2017年提出的一种基于自注意力机制的神经网络架构&#xff0c;transformers库是hugging face社区创造的一个py库&#xff0c;通过该库可以实现统一…...

网络安全防护指南:筑牢网络安全防线(510)

一、网络安全的基本概念 &#xff08;一&#xff09;网络的定义 网络是指由计算机或者其他信息终端及相关设备组成的按照一定的规则和程序对信息收集、存储、传输、交换、处理的系统。在当今数字化时代&#xff0c;网络已经成为人们生活和工作中不可或缺的一部分。它连接了世…...

微信小程序实现拉卡拉支付

功能需求&#xff1a;拉卡拉支付&#xff08;通过跳转拉卡拉平台进行支付&#xff09;&#xff0c;他人支付&#xff08;通过链接进行平台跳转支付&#xff09; 1.支付操作 //支付 const onCanStartPay async (obj) > {uni.showLoading({mask: true})// 支付接口获取需要传…...

git从本地其他设备上fetch分支

在 Git 中&#xff0c;如果你想从本地其他设备上获取分支&#xff0c;可以通过以下几种方式实现。不过&#xff0c;需要注意的是&#xff0c;Git 本身是分布式版本控制系统&#xff0c;通常我们是从远程仓库&#xff08;如 GitHub、GitLab 等&#xff09;拉取分支&#xff0c;而…...

【干货教程】Windows电脑本地部署运行DeepSeek R1大模型(基于Ollama和Chatbox)

文章目录 一、环境准备二、安装Ollama2.1 访问Ollama官方网站2.2 下载适用于Windows的安装包2.3 安装Ollama安装包2.4 指定Ollama安装目录2.5 指定Ollama的大模型的存储目录 三、选择DeepSeek R1模型四、下载并运行DeepSeek R1模型五、常见问题解答六、使用Chatbox进行交互6.1 …...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...