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

细说 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 时的一些细节。 还是从我们在前文中提到的引用流程图里说起,这里不清…...

【深度学习】Unet的基础介绍

U-Net是一种用于图像分割的深度学习模型,特别适合医学影像和其他需要分割细节的任务。如图: Unet论文原文 为什么叫U-Net? U-Net的结构像字母“U”,所以得名。它的结构由两个主要部分组成: 下采样(编码…...

Python--函数进阶(下)

3. 返回值与print​的辨析 3.1 返回值的作用 ​return​:将结果传递给调用者,可后续处理。​print​:仅输出到控制台,不保留数据。 def add(a, b):return a bresult add(3, 4) # 结果存储在result中 print(result) # …...

ROS2机器人开发--服务通信与参数通信

服务通信与参数通信 在 ROS 2 中,服务(Services)通信和参数(Parameters)通信是两种重要的通信机制。服务是基于请求和响应的双向通信机制。参数用于管理节点的设置,并且参数通信是基于服务通信实现的。 1 …...

DeepSeek写贪吃蛇手机小游戏

DeepSeek写贪吃蛇手机小游戏 提问 根据提的要求,让DeepSeek整理的需求,进行提问,内容如下: 请生成一个包含以下功能的可运行移动端贪吃蛇H5文件: 要求 蛇和食物红点要清晰,不超过屏幕外 下方有暂停和重新…...

【开源项目】分布式文本多语言翻译存储平台

分布式文本多语言翻译存储平台 地址: Gitee:https://gitee.com/dreamPointer/zza-translation/blob/master/README.md 一、提供服务 分布式文本翻译服务,长文本翻译支持流式回调(todo)分布式文本多语言翻译结果存储服…...

代码随想录刷题day29|(栈与队列篇:队列)225.用队列实现栈

目录 一、队列基本知识 二、队列在Java中的实现 1.Queue 2.Deque ①实现普通队列 ②实现栈 ③实现双端队列 3.基于底层数据结构 4.组合模式 三、相关算法题目 思路 代码 四、栈和队列总结 一、队列基本知识 队列只能在队尾添加元素,在队头删除元素&a…...

Python安全之反序列化——pickle/cPickle

一. 概述 Python中有两个模块可以实现对象的序列化,pickle和cPickle,区别在于cPickle是用C语言实现的,pickle是用纯python语言实现的,用法类似,cPickle的读写效率高一些。使用时一般先尝试导入cPickle&…...

Deepin(Linux)安装MySQL指南

1.下载 地址:https://downloads.mysql.com/archives/community/ 2.将文件解压到 /usr/local 目录下 先cd到安装文件所在目录再解压,本机是cd /home/lu01/Downloads sudo tar -xvJf mysql-9.2.0-linux-glibc2.28-x86_64.tar.xz -C /usr/local3.创建软链…...

vue-fastapi-admin 部署心得

vue-fastapi-admin 部署心得 这两天需要搭建一个后台管理系统,找来找去 vue-fastapi-admin 这个开源后台管理框架刚好和我的技术栈所契合。于是就浅浅的研究了一下。 主要是记录如何基于原项目提供的Dockerfile进行调整,那项目文件放在容器外部&#xf…...

计算机视觉算法实战——三维重建(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ 1. 三维重建领域简介 三维重建(3D Reconstruction)是计算机视觉的核心任务之一,旨在通过多视角图像、视频…...

先进制造aps专题三十 用免费生产排程软件isuperaps进行长期生产计划制定

isuperaps是生产排产软件,同时也可以用来制定长期生产计划 通过isuperaps制定长期生产计划,一个指导原则就是大bom, 单工序,大bom的意思是bom中只包含主要的半成品和原料,单工序的意思是半成品/产品生产以工厂或车间为基本生产单…...

DeepSeek使用从入门到精通

1. DeepSeek概述 - DeepSeek是国产大模型,提供网页版和App版。因其强大功能,遭受网络攻击,但国内用户可直接使用。 2. 入门技巧 - 忘掉复杂提示词:用简洁明了的需求指令,AI能自我思考并生成优质内容 - 明确需求&#…...

迎接DeepSeek开源周[Kimi先开为敬]发布开源最新Muon优化器可替代 AdamW计算效率直接翻倍

Muon优化器在小规模语言模型训练中表现出色,但在大规模模型训练中的可扩展性尚未得到证实。月之暗面通过系统分析和改进,成功将 Muon 应用于 3B/16B 参数的 MoE 模型训练,累计训练 5.7 万亿 token。结果表明,Muon 可以替代 AdamW …...

【工作流】Spring Boot 项目与 Camunda 的整合

【工作流】Spring Boot 项目与 Camunda 的整合 【一】Camunda 和主流流程引擎的对比【二】概念介绍【1】Camunda 概念:【2】BPMN 概念 【三】环境准备【1】安装流程设计器CamundaModeler【画图工具】(1)下载安装 【2】CamundaModeler如何设计…...

Grouped-Query Attention(GQA)详解: Pytorch实现

Grouped-Query Attention(GQA)详解 Grouped-Query Attention(GQA) 是 Multi-Query Attention(MQA) 的改进版,它通过在 多个查询头(Query Heads)之间共享 Key 和 Value&am…...

docker基操

docker基操 首先就是安装docker使用docker:创建容器-制作一个镜像-加载镜像首先就是安装docker 随便找一个教程安装就可以,安装过程中主要是不能访问谷歌,下面这篇文章写了镜像的一些问题: 安装docker的网络问题 使用docker:创建容器-制作一个镜像-加载镜像 主要是参考:这篇…...

SF-HCI-SAP问题收集1

最近在做HCI的集成,是S4的环境,发现很多东西都跑不通,今天开始收集一下错误点 如果下图冲从0001变成0010,sfiom_rprq_osi表就会存数据,系统检查到此表就会报错,这个选项的作用就是自定义信息类型也能更新&a…...

当 OpenAI 不再 open,DeepSeek 如何掀起 AI 开源革命?

开源与闭源的路线之争成为了行业瞩目的焦点,DeepSeek掀起的 AI 开源风暴! 一、硅谷“斯普特尼克时刻” 1957年,苏联将人类首颗人造卫星“斯普特尼克”送上太空,美国举国震动。 这颗“篮球”般的卫星,刺痛了自诩科技霸…...

理解 logits_to_keep = logits_to_keep + 1 在 _get_per_token_logps 中的作用

理解 logits_to_keep logits_to_keep 1 在 _get_per_token_logps 中的作用 source: anaconda3/envs/xxx/lib/python3.10/site-packages/trl/trainer/grpo_trainer.py def _get_per_token_logps(self, model, input_ids, attention_mask, logits_to_keep):# We add 1 to logi…...

论文笔记-WSDM2025-ColdLLM

论文笔记-WSDM2025-Large Language Model Simulator for Cold-Start Recommendation ColdLLM:用于冷启动推荐的大语言模型模拟器摘要1.引言2.前言3.方法3.1整体框架3.1.1行为模拟3.1.2嵌入优化 3.2耦合漏斗ColdLLM3.2.1过滤模拟3.2.2精炼模拟 3.3模拟器训练3.3.1LLM…...

DeepSeek与AI幻觉

AI幻觉(AI Hallucination) 是指人工智能系统(尤其是生成式模型,如大型语言模型或图像生成模型)在输出内容时,生成与事实不符、逻辑混乱或完全虚构的信息的现象。这种现象类似于人类的“幻觉”,即…...

Linux 命令大全完整版(09)

4. 压缩与解压缩命令 ar 功能说明&#xff1a;建立或修改备存文件&#xff0c;或是从备存文件中抽取文件。语法&#xff1a;ar[-dmpqrtx][cfosSuvV][a<成员文件>][b<成员文件>][i<成员文件>][备存文件][成员文件]补充说明&#xff1a;可让您集合许多文件&a…...

deepseek_清华大学指导手册_pdf_1-5

deepseek_清华大学指导手册_pdf_1-5 无套路&#xff0c;无需关注&#xff0c;无需登录&#xff0c;无需app&#xff0c;直接下载&#xff1a; 下载地址 文件列表&#xff1a; 001_清华大学_DeepSeek从入门到精通.pdf 002_清华大学_DeepSeek如何赋能职场应用.pdf 003_清华大学…...

深度学习-127-LangGraph之基础知识(四)自定义状态添加额外字段的聊天机器人

文章目录 1 自定义状态2 自定义工具2.1 完善工具human_assistance2.2 浏览器工具baidu_search3 聊天机器人3.1 绑定工具的聊天模型3.2 聊天机器人(带记忆)4 调用图4.1 调用工具时中断4.2 人工提供信息恢复4.3 查询存储的状态4.4 手动更新状态5 参考附录使用LangGraph,在状态中…...

自定义实现简版状态机

状态机&#xff08;State Machine&#xff09;是一种用于描述系统行为的数学模型&#xff0c;广泛应用于计算机科学、工程和自动化等领域。它通过定义系统的状态、事件和转移来模拟系统的动态行为。 基本概念 状态&#xff08;State&#xff09;&#xff1a;系统在某一时刻的特…...

基于 Python Django 的校园互助平台(附源码,文档)

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…...

Python pip 缓存清理:全面方法与操作指南

在使用 Python 的 pip 进行包安装时&#xff0c;pip 会将下载的包缓存起来&#xff0c;以加快后续相同包的安装速度。不过&#xff0c;随着时间推移&#xff0c;缓存会占用大量磁盘空间&#xff0c;这时你可以对其进行清理。下面为你介绍不同操作系统下清理 pip 缓存的方法。 …...

Windows系统第一次运行C语言程序,环境配置,软件安装等遇到的坑及解决方法

明确需要编辑器和编译器&#xff0c;并选择自己要用什么&#xff08;我选的编辑器是VSCode&#xff1a;Visual Studio Code&#xff1b;编译器是gcc&#xff09;下载VSCode并配置环境变量&#xff08;这里没啥问题&#xff09;&#xff0c;安装C/C的拓展安装Cygwin&#xff0c;…...

Python开发Django面试题及参考答案

目录 Django 的请求生命周期是怎样的? Django 的 MTV 架构中的各个组件分别是什么? Django 的 URL 路由是如何工作的? Django 的视图函数和视图类有什么区别? Django 的模板系统是如何渲染 HTML 的? Django 的 ORM 是如何工作的? Django 的中间件是什么?它的作用是…...