dotnet ConditionalWeakTable 的底层原理
在 dotnet 中有一个特殊的类,这个类能够做到附加属性一样的功能。也就是给某个对象附加一个属性,当这个对象被回收的时候,自然解除附加的属性的对象的引用。本文就来聊聊这个类的底层原理
小伙伴都知道弱缓存是什么,弱缓存的核心是弱引用。也就是我虽然拿到一个对象,但是我没有给这个对象添加依赖引用,也就是这个对象不会记录被弱引用的引用。而 ConditionalWeakTable 也是一个弱缓存只是有些特殊的是关联的是其他对象。使用方法请看 .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary) - walterlv
这个类一般用来做弱缓存字典,只要 Key 没有被回收,而 value 就不会被回收。如果 key 被回收,那么 value 将会减去一个依赖引用。而字典对于 key 是弱引用
通过阅读 runtime 的源代码,可以看到实际上这个类的核心需要 DependentHandle 结构体的支持,因为依靠 key 定住 value 需要 CLR 的 GC 支持。什么是依靠 key 定住 value 的功能?这里的定住是 Pin 的翻译,意思是如果 key 存在内存,那么将会给 value 添加一个引用,此时的 value 将不会被回收。而如果 key 被回收了,此时的 value 将失去 key 对他的强引用
换句话说,只要 key 的值存在,那么 value 一定不会回收
这个功能纯使用 WeakReference 是做不到的,需要 GC 的支持,而在 dotnet core 里面提供 GC 支持的对接的是 DependentHandle 结构体
那么 DependentHandle 的功能又是什么?这个结构体提供传入 object primary, object? secondary
构造函数,作用就是当 primary 没有被回收的时候,给 secondary
添加一个引用计数。在 primary 回收的时候,解除对 secondary 的引用。而这个结构体本身对于 primary 是弱引用的,对于 secondary 仅在 primary 没有被回收时是强引用,当 primary 被回收之后将是弱引用
刚好利用 GC 的只要对象至少有一个引用就不会被回收的功能,就能做到 ConditionalWeakTable 提供附加属性的功能
下面代码是 DependentHandle 结构体的代码,可以看到大量的方法都是需要 GC 层的支持,属于 CLR 部分的注入方法
internal struct DependentHandle
{
private IntPtr _handle;
public DependentHandle(object primary, object? secondary) =>
// no need to check for null result: nInitialize expected to throw OOM.
_handle = nInitialize(primary, secondary);
public bool IsAllocated => _handle != IntPtr.Zero;
// Getting the secondary object is more expensive than getting the first so
// we provide a separate primary-only accessor for those times we only want the
// primary.
public object? GetPrimary() => nGetPrimary(_handle);
public object? GetPrimaryAndSecondary(out object? secondary) =>
nGetPrimaryAndSecondary(_handle, out secondary);
public void SetPrimary(object? primary) =>
nSetPrimary(_handle, primary);
public void SetSecondary(object? secondary) =>
nSetSecondary(_handle, secondary);
// Forces dependentHandle back to non-allocated state (if not already there)
// and frees the handle if needed.
public void Free()
{
if (_handle != IntPtr.Zero)
{
IntPtr handle = _handle;
_handle = IntPtr.Zero;
nFree(handle);
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr nInitialize(object primary, object? secondary);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? nGetPrimary(IntPtr dependentHandle);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object? nGetPrimaryAndSecondary(IntPtr dependentHandle, out object? secondary);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void nSetPrimary(IntPtr dependentHandle, object? primary);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void nSetSecondary(IntPtr dependentHandle, object? secondary);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void nFree(IntPtr dependentHandle);
}
而核心实现的入口是在 gchandletable.cpp 的 OBJECTHANDLE GCHandleStore::CreateDependentHandle(Object* primary, Object* secondary)
代码,这部分属于更底的一层了,在功能上就是实现上面的需求,而实现上为了性能优化,代码可读性还是渣了一些
要实现这个功能需要在 GC 层里面写上一大堆的代码,但使用上现在仅有 ConditionalWeakTable 一个在使用
dotnet ConditionalWeakTable 的底层原理的更多相关文章
- Neo4j图数据库简介和底层原理
现实中很多数据都是用图来表达的,比如社交网络中人与人的关系.地图数据.或是基因信息等等.RDBMS并不适合表达这类数据,而且由于海量数据的存在,让其显得捉襟见肘.NoSQL数据库的兴起,很好地解决了海 ...
- 【T-SQL进阶】02.理解SQL查询的底层原理
本系列[T-SQL]主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式 ...
- spring框架的IOC的底层原理
1.IOC概念:spring容器创建对象并管理 2.IOC的底层原理的具体实现: 1)所使用的技术: (1). dom4j解析xml配置文件 (2).工厂设计模式(解耦合) (3).反射 第一步:配置 ...
- 深入研究Sphinx的底层原理和高级使用
深入研究Sphinx的底层原理和高级使用
- 深入研究Node.js的底层原理和高级使用
深入研究Node.js的底层原理和高级使用
- HashMap的底层原理
简单说: 底层原理就是采用数组加链表: 两张图片很清晰地表明存储结构: 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现: // 存储时: int hash = ke ...
- 操作系统底层原理与Python中socket解读
目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...
- Servlet底层原理、Servlet实现方式、Servlet生命周期
Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...
- Spring Aop底层原理详解
Spring Aop底层原理详解(来源于csdn:https://blog.csdn.net/baomw)
- JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能
摘要: 理解浏览器渲染. 原文:JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这是专门探索 J ...
随机推荐
- 可视化学习:WebGL实现缩放平移
前言 在上篇文章中,我们使用WebGL实现了网格背景,当时有提到说使用WebGL来实现的好处之一,是网格背景可以与画布上的其他元素更好地融合,比如一起缩放平移,那么在WebGL中怎么实现缩放和平移呢? ...
- C# 平台调用过程
(1)调用LoadLibrary加载非托管DLL到内存中,并调用GetProcAddress 获得内存中非托管函数的指针. (2) 为包含非托管函数地址的托管签名生成一个DllImport存根(st ...
- CentOS 7.6 防火墙打开、关闭,端口开启、关闭
查看CentOS版本 cat /etc/redhat-release 显示系统名.节点名称.操作系统的发行版号.操作系统版本.运行系统的机器 ID 号. uname -a 防火墙命令 #查询防火墙状态 ...
- zynq之TF卡写入函数f_printf
zynq之TF卡写入函数f_printf 1.基本原理 前面使用f_write写入了数据到TF(SD)卡中,可以实现较短字符串的写入.当字符增加时,容易出现乱码.而f_printf则是专门用于字符串写 ...
- KingbaseES V8R6 常用的系统函数
查看当前日志文件lsn位置: select sys_current_wal_lsn(); 查看某个lsn对应的日志名: select sys_walfile_name('0/1162FBA0'); 查 ...
- Python爬虫爬取全球疫情数据并存储到mysql数据库
思路:使用Python爬虫对腾讯疫情网站世界疫情数据进行爬取,封装成一个函数返回一个 字典数据格式的对象,写另一个方法调用该函数接收返回值,和数据库取得连接后把 数据存储到mysql数据库 ...
- [Android开发学iOS系列] 和一个真正iOS开发的区别?
和一个真正iOS开发的区别? 学习iOS的这段时间, 我一直在思考和感受着自己和一个真正做了几年iOS的dev之间的区别. 同时也在反向思考, 我自己和一个新学Android的人, 又有什么区别. 也 ...
- #二分,哈希 or dp#洛谷 4398 [JSOI2008]Blue Mary的战役地图
题目 求两个正方形矩阵的最大公共正方形矩阵边长 分析 第一种就是\(dp\): 设\(dp[x1][y1][x2][y2]\)表示第一个正方形矩阵以\((x1,y1)\)为右下角, 第二个正方形矩阵以 ...
- 提升面试成功率:深入理解 C++ 11 新特性
C++11是C++语言的一个重大更新,引入了许多新特性,包括自动类型推导.lambda表达式.右值引用.智能指针等等.这些新特性使得C++更加现代化.高效.易用.也是面试容很容易被问到一个问题,下面我 ...
- 动态库 DLL 封装四:对dll二次封装,开放回调函数,并减少回调函数中参数个数
背景: 我需要对一个dll进行二次封装,其中有一个接口,里面的参数需要传回调函数. 需求: 这个回调函数,我需要开放出去,并且减少回调函数参数的个数 示例: // 回调原型 VOID __stdcal ...