聊一聊 C# 弱引用 底层是怎么玩的
一:背景
1. 讲故事
最近在分析dump时,发现有程序的卡死和WeakReference有关,在以前只知道怎么用,但不清楚底层逻辑走向是什么样的,借着这个dump的契机来简单研究下。
二:弱引用的玩法
1. 一些基础概念
用过WeakReference的朋友都知道这里面又可以分为弱短和弱长两个概念,对应着构造函数中的trackResurrection参数,同时它也是对底层GCHandle.Alloc 方法的封装,参考源码如下:
public WeakReference(object? target, bool trackResurrection)
{Create(target, trackResurrection);
}private void Create(object target, bool trackResurrection)
{nint num = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);_taggedHandle = (trackResurrection ? (num | 1) : num);ComAwareWeakReference.ComInfo comInfo = ComAwareWeakReference.ComInfo.FromObject(target);if (comInfo != null){ComAwareWeakReference.SetComInfoInConstructor(ref _taggedHandle, comInfo);}
}public enum GCHandleType
{//// Summary:// This handle type is used to track an object, but allow it to be collected. When// an object is collected, the contents of the System.Runtime.InteropServices.GCHandle// are zeroed. Weak references are zeroed before the finalizer runs, so even if// the finalizer resurrects the object, the Weak reference is still zeroed.Weak = 0,//// Summary:// This handle type is similar to System.Runtime.InteropServices.GCHandleType.Weak,// but the handle is not zeroed if the object is resurrected during finalization.WeakTrackResurrection = 1
}
从上面的 GCHandleType 的注释来看。
- Weak 会在终结器执行之前判断持有的对象是否为垃圾对象,如果是的话直接切断引用。
- WeakTrackResurrection 会在终结器执行之后判断对象是否为垃圾对象,如果是的话直接切断引用。
可能这么说有点抽象,画张图如下:

2. 一个简单的测试例子
为了方便讲述两者的区别,使用 对象复活 来做测试。
- Weak 的情况
因为在 ScanForFinalization 方法之前做的判断,所以与垃圾对象的联系会被马上切断,参考代码如下:
class Program{static void Main(){WeakReferenceCase();GC.Collect();GC.WaitForPendingFinalizers();Console.WriteLine(weakHandle.Target ?? "Person 引用被切断");Console.ReadLine();}public static GCHandle weakHandle;static void WeakReferenceCase(){var person = new Person() { ressurect = false };weakHandle = GCHandle.Alloc(person, GCHandleType.Weak);}}public class Person{public bool ressurect = false;~Person(){if (ressurect){Console.WriteLine("Person 被永生了,不可能被消灭的。。。");GC.ReRegisterForFinalize(this);}else{Console.WriteLine("Person 析构已执行...");}}}

- WeakTrackResurrection 的情况
因为是在 ScanForFinalization 之后做的判断,这时候可能会存在 对象复活 的情况,所以垃圾又变成不垃圾了,如果是这种情况就不能切断,参考代码如下:
static void WeakReferenceCase()
{var person = new Person() { ressurect = true };weakHandle = GCHandle.Alloc(person, GCHandleType.WeakTrackResurrection);
}

3. coreclr源码分析
在 coreclr 里有一个 struct 枚举强对应 GCHandleType 结构体,而且名字看的更加清楚,代码如下:
typedef enum
{HNDTYPE_WEAK_SHORT = 0,HNDTYPE_WEAK_LONG = 1,
}
HandleType;
接下来看下刚才截图源码上的验证。
void gc_heap::mark_phase(int condemned_gen_number, BOOL mark_only_p)
{// null out the target of short weakref that were not promoted.GCScan::GcShortWeakPtrScan(condemned_gen_number, max_generation, &sc);dprintf(3, ("Finalize marking"));finalize_queue->ScanForFinalization(GCHeap::Promote, condemned_gen_number, mark_only_p, __this);// null out the target of long weakref that were not promoted.GCScan::GcWeakPtrScan(condemned_gen_number, max_generation, &sc);
}BOOL CFinalize::ScanForFinalization(promote_func* pfn, int gen, BOOL mark_only_p, gc_heap* hp)
{for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++){Object** endIndex = SegQueue(Seg);for (Object** i = SegQueueLimit(Seg) - 1; i >= endIndex; i--){CObjectHeader* obj = (CObjectHeader*)*i;if (!g_theGCHeap->IsPromoted(obj)){if (method_table(obj)->HasCriticalFinalizer()){MoveItem(i, Seg, CriticalFinalizerListSeg);}else{MoveItem(i, Seg, FinalizerListSeg);}}}}if(finalizedFound) GCToEEInterface::EnableFinalization(true);return finalizedFound;
}
源码中有几个注意点:
- 如何判断一个对象为垃圾
gc 在标记时,将有根的对象mt的第一位设为 1 来表示当前已经标记过,即有用对象,未被标记的即为垃圾对象。
- 终结器线程真的被启动了吗
从简化的源码看,一旦有垃圾对象被送入到 终结器队列的 预备区 时,就会通过 GCToEEInterface::EnableFinalization(true) 启动终结器线程,所以在测试代码中加了 GC.WaitForPendingFinalizers(); 就是为了等待终结器线程执行完毕然后才判断 Target,这样结果就会更加准确。
4. 切断逻辑在哪里
有些朋友会好奇那个 weakHandle.Target=null 的逻辑到底在 coreclr 的何处,这个比较简单,可以用 windbg 下 ba 断点即可,我们还是拿弱引用来举例,截图如下:

三:总结
WeakReference 的内部玩法有很多,更深入的理解还需要对 g_HandleTableMap 进行深度挖掘,后面有机会再聊吧,有时候dump分析还是挺苦逼的,需要对相关领域底层知识有一个足够了解,否则谈何修复呢?
相关文章:
聊一聊 C# 弱引用 底层是怎么玩的
一:背景 1. 讲故事 最近在分析dump时,发现有程序的卡死和WeakReference有关,在以前只知道怎么用,但不清楚底层逻辑走向是什么样的,借着这个dump的契机来简单研究下。 二:弱引用的玩法 1. 一些基础概念 …...
蜘蛛池规矩采集优化与运用技巧 什么是蜘蛛池/SEO蜘蛛池怎么养?(蜘蛛池新手入门虚良SEO)
作为一名网络内容修改,我常常需求从各种网站上收集文章并转载到咱们的网站上。而在这个过程中,我深深感受到了蜘蛛池对我的帮助。今日,我就来共享一下我对蜘蛛池收集规矩的亲自感受。 归纳 本文将分9个方面具体介绍蜘蛛池收集规矩的长处和运…...
SerDes介绍以及原语使用介绍(1)OSERDESE2
文章目录 前言:为什么需要serdes一、OSERDESE2框图二、OSERDESE2端口信号二、OSERDESE2原语参数三、OSERDESE2时序3.1、SDR模式3.2、DDR模式3.3、DDR模式下三态传输 前言:为什么需要serdes 需要 SerDes(串行器/解串器)主要是为了…...
基于单片机和组态王的温度监控系统的设计
摘 要 : 介绍了以 MSP430 单片机为核心 , 建立基于 DS18B20 和组态王的温度采集和监控系统。主要研究了单片机和组态王的通用通讯协议。按照 KingView 提供的通信协议 , 设计组态王与单片机的通信程序 , 实现了组态王与M SP430 单片机的直接串行通讯。在中药提取装置的…...
unity 导入的模型设置讲解
咱们先讲Model这一栏 Model Scene:场景级属性,例如是否导入灯光和照相机,以及使用什么比例因子。 Scale Factor:缩放因子(也就是模型导入后大小如果小了或者大了在这里直接改是相当于该模型的大小的,而且在…...
汽车 vSOC安全运营管理平台开发解决方案
汽车 vSOC 安全解决方案 一、引言 随着汽车行业的快速发展,汽车的智能化和互联化程度越来越高,汽车网络安全问题也日益凸显。汽车 vSOC(Vehicle Security Operations Center)作为汽车网络安全的重要组成部分,其作用越来越受到重视。本方案旨在提供一套可实施落地的汽车 vS…...
python 第三方库
一、什么是第三方库 python的三方库指的是,需要通过pip install 安装后才能使用的 python 工具 三方库有很多: 做web自动化测试的库:selenium单元测试框架:pytest、unittest做app自动化测试:Python-Appium-Client做接…...
VMware Workstation环境下,DHCP服务的安装配置,用ubuntu来测试
需求说明: 某企业信息中心计划使用IP地址17216.11.0用于虚拟网络测试,注册域名为xyz.net.cn.并将172.16.11.2作为主域名的服务器(DNS服务器)的IP地址,将172.16.11.3分配给虚拟网络测试的DHCP服务器,将172.16.11.4分配给虚拟网络测试的web服务器,将172.16.11.5分配给FTP服务器…...
CSS实现文字颜色渐变
直接上代码和效果图: <p class"linecolor">文字颜色渐变</p><style type"text/css">.linecolor{font-size: 30px;background-image:-webkit-linear-gradient(bottom,red,#fd8403,yellow);-webkit-background-clip:text;-web…...
《每天5分钟用Flask搭建一个管理系统》第4章:模板渲染
第4章:模板渲染 4.1 模板的概念和使用 模板是一种用于生成输出的方法,它允许您将Python代码和HTML标记混合在一起,从而创建动态网页。 示例代码:基本模板 <!-- templates/home.html --> <!DOCTYPE html> <html…...
逆向学习汇编篇:指令的操作
本节课在线学习视频(网盘地址,保存后即可免费观看): https://pan.quark.cn/s/660c759dea95 在逆向工程中,深入理解汇编语言的指令操作是至关重要的。汇编指令是计算机硬件与软件之间的桥梁,它们直…...
VB.net实战(VSTO):VSTOwpf体验框架打包教程
如果是考虑到Wps用户较多,就不建议采用侧边栏的形式 只是个体验框架,界面未作美化,office的用户可以用任意一种窗体,喜欢那个界面就写那个界面,wps的侧边栏只能弹出一部分,每次需要的手动拖动。 打包了案例…...
Jquery 获得Form下的所有text、checkbox等表单的值
Jquery使用表单我主要是想获得某一个表单下的所有text获得checkbox的值: 可以这样写: var parameter{}; $("input[typetext]",document.forms[0]).each(function(){ alert(this.name); }); 获得所有名为hobby的选中的checkbox的值和form2下的所有text的值 function s…...
stl之string
构造函数 void test1() {string s1;//不传参cout << s1 << endl;string s2("123456");cout << s2 << endl;string s3(s2);cout << s3 << endl;string s4(s2, 1, 5);cout << s4 << endl;string s5("123456&quo…...
Vue3学习笔记<->nginx部署vue项目
安装nginx vue项目通常部署到nginx上,所以先安装一个nginx。为了方便安装的是windows版nginx,解压就能用。 项目参考上一篇文章《Vue3学习笔记<->创建第一个vue项目》《Vue3学习笔记<->创建第一个vue项目》…...
使用 WebGL 创建 3D 对象
WebGL Demohttps://mdn.github.io/dom-examples/webgl-examples/tutorial/sample5/index.html 现在让我们给之前的正方形添加五个面从而可以创建一个三维的立方体。最简单的方式就是通过调用方法 gl.drawElements() 使用顶点数组列表来替换之前的通过方法gl.drawArrays() 直接…...
百度地图3d区域掩膜,最常见通用的大屏地图展现形式
需求及效果 原本项目使用的是百度地图3.0,也就是2d版本的那个地图,客户不满意觉得不够好看,让把地图改成3d的,但是我们因为另外的系统用的都是百度地图,为了保持统一只能用百度地图做 经过3天的努力,最后我终于把这个…...
小区物业管理收费系统源码小程序
便捷、透明、智能化的新体验 一款基于FastAdminUniApp开发的一款物业收费管理小程序。包含房产管理、收费标准、家属管理、抄表管理、在线缴费、业主公告、统计报表、业主投票、可视化大屏等功能。为物业量身打造的小区收费管理系统,贴合物业工作场景,轻…...
C++实现一个简单的Qt信号槽机制
昨天写这个文章《深入探讨C的高级反射机制(2):写个能用的反射库》的时候就在想,是不是也能在这套反射逻辑的基础上,实现一个类似Qt的信号槽机制? Qt信号槽机制简介 所谓的Qt的信号槽(Signals …...
微信小程序常用的传值
1.通过 URL 传参 在页面跳转时,可以在 URL 中携带参数进行传递,然后在目标页面的 onLoad 生命周期中获取参数。 // 在页面 A 中跳转到页面 B 并传递参数 wx.navigateTo({url: /pages/detail/index?id123 });// 在页面 B 的 onLoad 生命周期中获取参数…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...
spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...
