.NET Core 3.0 可卸载程序集原理简析

文章转载授权级别:A       预计阅读时间:8分钟      损失发量:不好统计

因为最近在群里被问到如何理解 .NET Core 3.0 可卸载程序集,所以就写了这篇简单的分析。

因为时间实在很少,这篇文章只简单的罗列了相关的代码,请配合官方说明文档理解。

另外,书籍《.NET Core 底层原理》预计 11 月出版,出版社比较拖 :O。

Azulx: 昨晚下班群里问的农神,今早就出来了,这效率。 

链接

可卸载程序集的官方说明文档如下:

  • https://github.com/dotnet/coreclr/blob/release/3.0/Documentation/design-docs/unloadability.md

程序集中的 IL 代码经过 JIT 编译后,会储存原生代码在 LoaderAllocator 管理的 Code Heap 中,LoaderAllocator 的代码地址如下:

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.hpp

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.cpp

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.inl

负责分配原生代码的是 CodeManager ,代码地址如下:

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/codeman.h

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/codeman.cpp

GC 实现代码如下:

  • https://raw.githubusercontent.com/dotnet/coreclr/release/3.0/src/gc/gc.cpp

对象头 (MethodTable) 代码如下:

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/methodtable.h

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/methodtable.inl

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/methodtable.cpp

AssemblyLoadContext 的代码如下:

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/System.Private.CoreLib/shared/System/Runtime/Loader/AssemblyLoadContext.cs  

分析

在 .NET Core 中我们不能新建 AppDomain (尽管有默认的几个 AppDomain),程序集会通过 AssemblyLoadContext 管理。简单的来说,AssemblyLoadContext 负责管理有依赖关系的一组程序集,例如程序集 A 依赖程序集 B,那么 A 和 B 需要使用同一个 AssemblyLoadContext 加载。每个 AssemblyLoadContext 都会关联不同的 LoaderAllocator,也就是拥有不同的 Code Heap。

.NET Core 3.0 开始允许卸载用户创建的 AssemblyLoadContext ,也就是回收 AssemblyLoadContext 为程序集分配的各种资源,包括 JIT 生成的原生代码,PreCode,类型元数据等,流程大致如下:

  • 用户创建 AssemblyLoadContext (isCollectible = true)

  • 用户使用 AssemblyLoadContext 加载程序集 A

  • 用户使用 AssemblyLoadContext 加载程序集 B

  • 用户创建程序集 A 和 B 中的类型的实例,并执行其中的方法

  • 用户卸载 AssemblyLoadContext

  • .NET Core 等待所有程序集 A 和 B 中的类型的实例都被回收后,释放 AssemblyLoadContext 管理的 LoaderAllocator 分配的资源

可以参考下图理解 (这是经过简化的流程,详细流程可以看前面给出的官方说明文档链接):

640?wx_fmt=png

卸载 AssemblyLoadContext 时,取消对 LoaderAllocator 的关联的代码如下:

https://github.com/dotnet/coreclr/blob/release/3.0/src/binder/clrprivbinderassemblyloadcontext.cpp#L276  

GC 标记对象时,同时标记关联的 LoaderAllocator 的代码如下 (在 gc.cpp 里面):

#define go_through_object_cl(mt,o,size,parm,exp)                            	
{                                                                           	// 如果对象的 MethodTable 是由可回收的 AssemblyLoadContext 加载的	if (header(o)->Collectible())                                           	{                                                                       	// 获取关联的 LoaderAllocator	uint8_t* class_obj = get_class_object (o);                             	uint8_t** parm = &class_obj;                                           	// 标记 LoaderAllocator (根据 exp 的具体逻辑而定)	do {exp} while (false);                                             	}                                                                       	// 如果对象包含引用类型的成员	if (header(o)->ContainsPointers())                                      	{                                                                       	go_through_object_nostart(mt,o,size,parm,exp);                      	}                                                                       	
}
// 调用 MethodTable::GetLoaderAllocatorObjectForGC	
#define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i)

LoaderAllocator 被回收以后到释放资源的相关代码 (被回收之前的逻辑参考官方说明文档):

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.cpp#L520

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/appdomain.cpp#L857

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/appdomain.cpp#L817

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/appdomain.hpp#L3086

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/appdomain.cpp#L6240

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/appdomain.cpp#L6283

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.cpp#L88

  • https://github.com/dotnet/coreclr/blob/release/3.0/src/vm/loaderallocator.cpp#L1301  

说明就到此为止了。你可能会奇怪为什么这篇文章没有提到 Assembly 和 DomainAssembly ,这是因为它们在可卸载程序集的实现中并不重要,资源是通过 AssemblyLoadContext 关联的 LoaderAllocator 统一分配和释放的,与其说是可卸载程序集,不如说是可卸载程序集加载上下文 (AssemblyLoadContext)。 

https://github.com/dotnetcore640?wx_fmt=gif

打赏一杯酒,削减三分愁。跟着我们走,脱发包你有。

640?wx_fmt=png

640?wx_fmt=png

组织打赏账户为柠檬的账户,请标注「NCC」,并留下您的名字,以下地址可查看收支明细:https://github.com/dotnetcore/Home/blob/master/Statement-of-Income-and-Expense.md640?wx_fmt=png640?wx_fmt=png
OpenNCC,专注.NET技术的公众号
https://www.dotnetcore.xyz
640?wx_fmt=png
640?wx_fmt=png微信ID:OpenNCC
640?wx_fmt=png长按左侧二维码关注

欢迎打赏组织

给予我们更多的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313827.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NetCore技术研究-ConfigurationManager在单元测试下的坑

最近在将原有代码迁移.NET Core, 代码的迁移基本很快,当然也遇到了不少坑,重构了不少,后续逐步总结分享给大家。今天总结分享一下ConfigurationManager遇到的一个问题。先说一下场景:迁移.NET Core后,已有的配置文件&a…

分析一次double强转float的翻车原因

人逢喜事精神爽,总算熬到下班撩~~正准备和同事打个招呼回家,被同事拖住问了.?‍♂️: 你们组做的那块代码,把double类型数据成float有问题啊?.?‍♀️: 嗯?不对是正常啊,float精度是没有double高,但float能保存到小数点后好多位,对我们来说完全够用了!?‍♂️: 不是啊,这不…

.NET Core 3.0之深入源码理解Host(二)

写在前面停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了。本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要内容是关于创建Long Run Program的创建与守护。关于Host,我们最容易想到的就…

dotNET Core实现分布式环境下的流水号唯一

业务背景在管理系统中,很多功能模块都会涉及到各种类型的编号,例如:流程编号、订单号、合同编号等等。编号各有各自的规则,但通常有一个流水号来确定编号的唯一性,保证流水号的唯一,在不同的环境中实现方式…

认证方案之初步认识JWT

前言:现在越来越多的项目或多或少会用到JWT,为什么会出现使用JWT这样的场景的呢?假设现在有一个APP,后台是分布式系统。APP的首页模块部署在上海机房的服务器上,子页面模块部署在深圳机房的服务器上。此时你从首页登录…

Java实现生产消费模型的5种方式

** 前言 ** 生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储…

Leetcode 86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。示例:输入: head 1->4->3->2->5->2, x 3输出: 1->2->2->4->3->5题目分析…

深入理解 JVM Class文件格式(一)

** 一、JVM体系结构 ** ** 二、class格式文件概述 ** class文件是一种8位字节的二进制流文件, 各个数据项按顺序紧密的从前向后排列, 相邻的项之间没有间隙, 这样可以使得class文件非常紧凑, 体积轻巧, 可以被J…

深入理解 JVM Class文件格式(二)

** class文件中的特殊字符串 ** 特殊字符串是常量池中符号引用的一部分,包括三种: 类的全限定名, 字段和方法的描述符, 特殊方法的方法名。 下面我们就分别介绍这三种特殊字符串。 (1) 类的全限定名 在…

.NET框架之“小马过河”

.NET框架之“小马过河”有许多流行的 .NET框架,大家都觉得挺“重”,认为很麻烦,重量级,不如其它“轻量级”框架,从而不愿意使用。面对形形色色的框架发愁,笔者也曾发愁。但我发现只要敢于尝试,这…

深入理解 JVM Class文件格式(三)

** JVM常量池中各数据项类型详解 ** 关于常量池的大概内容, 已经在 深入理解 JVM Class文件格式(一) 中讲解过了, 这篇文章中还介绍了常量池中的11种数据类型。 本文的任务是详细讲解这11种数据类型, 深度剖析源文件…

深入理解 JVM Class文件格式(四)

(3)CONSTANT_Integer_info 一个常量池中的CONSTANT_Integer_info数据项, 可以看做是CONSTANT_Integer类型的一个实例。 它存储的是源文件中出现的int型数据的值。 同样, 作为常量池中的一种数据类型, 它的第一个字节也是一个tag值…

.Net Core中使用Quartz.Net Vue开即用的UI管理

Quartz.NETQuartz.Net 定制UI维护了常用作业添加、删除、修改、停止、启动功能,直接使用cron表达式设置作业执行间隔,有完整的日志记录。Quartz.NET是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大型企业系统。Quartz.NET是一个…

深入理解 JVM Class文件格式(五)

(8) CONSTANT_Class_info 常量池中的一个CONSTANT_Class_info, 可以看做是CONSTANT_Class数据类型的一个实例。 他是对类或者接口的符号引用。 它描述的可以是当前类型的信息, 也可以描述对当前类的引用, 还可以描述对…

混沌工程详细介绍——Netflix持续交付实践探寻

内容来源:DevOps案例深度研究 – Netflix的文化与工程实践战队(本文只展示部分案例PPT及研究成果,更多细节请关注案例分享活动,及本公众号)。本案例内容贡献者:高金梅,李晓莉,潘雄鹰…

深入理解 JVM Class文件格式(六)

经过前几篇文章, 终于将常量池介绍完了, 之所以花这么大的功夫介绍常量池, 是因为对于理解class文件格式,常量池是必须要了解的, 因为class文件中其他地方,大量引用了常量池中的数据项。 对于还不了解常量池…

远程开发初探 - VS Code Remote Development

如果你是学生,你还在你的 windows 电脑上为各种环境配置头疼的时候,你应该了解一下 Remote Development。如果你喜欢 linux 的开发环境和舒适的 shell,但却不舍得抛弃 windows/macos 图形界面给你带来的用户体验和一些软件的兼容(QQ, 微信), …

深入理解 JVM Class文件格式(七)

本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍。 本文将会继续介绍class文件中未讲解的信息。 先回顾一下上面一篇文章。 在上一篇博客中, 我们介绍了: this_class 对当前类的描述 super_class 对当前类的超类的描述 in…

微信小程序集成腾讯云 IM SDK

1、背景因业务功能需求需要接入IM(即时聊天)功能,一开始想到的是使用 WebSocket 来实现这个功能,然天意捉弄(哈哈)服务器版本太低不支持 wx 协议(也就不支持 WebSocket了)不得不寻找…

深入理解 JVM Class文件格式(八)

在本专栏的第一篇文章 深入理解Java虚拟机到底是什么 中, 我们主要讲解了什么是虚拟机, 这篇博客是对JVM的一个概述。 在随后的几篇文章中,一直在讲解class文件格式。 在今天这篇博客中, 将会继续讲解class文件中的其他信息。 在本…