聊一聊 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 生命周期中获取参数…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...