在 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 的底层原理的更多相关文章

  1. Neo4j图数据库简介和底层原理

    现实中很多数据都是用图来表达的,比如社交网络中人与人的关系.地图数据.或是基因信息等等.RDBMS并不适合表达这类数据,而且由于海量数据的存在,让其显得捉襟见肘.NoSQL数据库的兴起,很好地解决了海 ...

  2. 【T-SQL进阶】02.理解SQL查询的底层原理

    本系列[T-SQL]主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式 ...

  3. spring框架的IOC的底层原理

    1.IOC概念:spring容器创建对象并管理 2.IOC的底层原理的具体实现: 1)所使用的技术: (1). dom4j解析xml配置文件 (2).工厂设计模式(解耦合) (3).反射 第一步:配置 ...

  4. 深入研究Sphinx的底层原理和高级使用

    深入研究Sphinx的底层原理和高级使用

  5. 深入研究Node.js的底层原理和高级使用

    深入研究Node.js的底层原理和高级使用

  6. HashMap的底层原理

    简单说: 底层原理就是采用数组加链表: 两张图片很清晰地表明存储结构: 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现: // 存储时: int hash = ke ...

  7. 操作系统底层原理与Python中socket解读

    目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...

  8. Servlet底层原理、Servlet实现方式、Servlet生命周期

    Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...

  9. Spring Aop底层原理详解

    Spring Aop底层原理详解(来源于csdn:https://blog.csdn.net/baomw)

  10. JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能

    摘要: 理解浏览器渲染. 原文:JavaScript是如何工作的: CSS 和 JS 动画底层原理及如何优化它们的性能 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这是专门探索 J ...

随机推荐

  1. axios封装(处理token跟get中Content-Type的请求问题)

    axios封装 import axios from 'axios' //引入axios import store from '@/store/index' //引入store //此处引入router ...

  2. 使用 Kotlin DSL 编写网络爬虫

    本博文将会通过一个网络爬虫的例子,向你介绍 Kotlin 的基本用法和其简洁有力的 DSL. 关于DSL 按照维基百科的说法,DSL(domain-specific language) 是一种专注于某 ...

  3. Linux安装Oracle12C及一些参考

    目录 安装 系统配置 安装前装备 安装依赖包 创建用户和组 修改内核参数 修改系统资源限制 创建安装目录及设置权限 设置oracle环境变量 安装Oracle 一些参考 compat-libstdc+ ...

  4. verilog之random

    verilog之random 1.基本作用 random,用于产生随机数.在测试时,有时需要测试的情况太多,无法一一列举,就需要使用抽样测试的方法验证功能是否可行.random是一个有返回值的系统函数 ...

  5. 源自opencore的fifo的IP核解析

    fifo的IP核学习与解析 1.fifo的基本原理 fifo,就是缓存器,可以理解为升级版的D触发器.D触发器是在时钟信号的控制下,数据等时间间隔刷新.对于fifo而言,数据也是需要刷新的,不过有一个 ...

  6. BlockQNN:NASNet同期,商汤提出block-wise版的MetaQNN | CVPR 2018

    作为NASNet的同期论文,BlockQNN同样地将目光从整体网络转换到了block-wise,整体思路大概是MetaQNN的block版,增加了一些细节地小修改.配合Early Stop Strat ...

  7. KingbaseES 基于SQL的函数过程

    什么是SQL函数? SQL函数包体是一些可执行的SQL语言.同时包含1条以上的查询,但是函数只返回最后一个查询(必须是SELECT)的结果. 除非SQL函数声明为返回void,否则最后一条语句必须是S ...

  8. #数学期望,高斯消元#洛谷 3232 [HNOI2013]游走

    题目 分析 如果计算出边的期望经过次数那就可以算出来答案 首先转换成点的期望经过次数,设\(dp[x]\)表示点\(x\)的期望经过次数 那么\(dp[x]=\sum_{y\in son}\frac{ ...

  9. #单位根反演,二项式定理#LOJ 6485 LJJ 学二项式定理

    题目 \[\large\sum_{i=0}^nC(n,i)S^ia_{i\bmod 4} \] \(n\leq 10^{18},S,a\leq 10^8\) 分析 前面这一坨看起来就像是二项式定理,考 ...

  10. Seaborn结构化图形绘制(FacetGrid)

    结构化图形绘制(FacetGrid) 可实现多行多列个性化绘制图形. sns.FacetGrid( data, row=None, col=None, hue=None, col_wrap=None, ...