.Net析构函数再论(CLR源码级的剖析)

news/发布时间2024/8/25 18:19:44

前言

碰到一些问题,发觉依旧没有全面了解完全析构函数。本篇继续看下析构函数的一些引申知识。

概述

析构函数目前发现的总共有三个标记,这里分别一一介绍下。先上一段代码:

 internal class Program :  IDisposable{static void Main(string[] args){StreamReader? streamReader = null;streamReader = new StreamReader("Test_.dll");streamReader?.Dispose();Console.ReadLine();}~Program(){Console.WriteLine("调用了析构函数");}public void Dispose(){this.Dispose();GC.SuppressFinalize(this);}}

这里的析构函数跟Dispose一起混用, ~Program()析构函数会通过Roslyn生成

.method family hidebysig virtual instance void Finalize() cil managed
{.override [System.Runtime]System.Object::Finalize// 代码大小       24 (0x18).maxstack  1IL_0000:  nop.try{IL_0001:  nopIL_0002:  ldstr      bytearray (03 8C 28 75 86 4E 90 67 84 67 FD 51 70 65 )       // ..(u.N.g.g.QpeIL_0007:  call       void [System.Console]System.Console::WriteLine(string)IL_000c:  nopIL_000d:  leave.s    IL_0017}  // end .tryfinally{IL_000f:  ldarg.0IL_0010:  call       instance void [System.Runtime]System.Object::Finalize()IL_0015:  nopIL_0016:  endfinally}  // end handlerIL_0017:  ret
} // end of method Program::Finalize

这里同时需要注意 streamReader?.Dispose();这句话,streamreader实际上继承的是textreader

public class StreamReader : TextReader
{}

所以它调用Dispose的代码是TextReader里面的Dispose:

 public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}

也就是关闭了streamReader流。然后base.Dispose.这个base.Dispose实际上就是它的父类TextReader里面的

public void Dispose()
{this._streamReader.close();
}

Dispose里面的下面一句代码

GC.SuppressFinalize(this);

它是重点。

GC.SuppressFinalize

1.判断当前类是否有析构函数
如果类里面有析构函数,比如例子里的Program,则会设置MethodTable的成员m_dwFlags

m_dwFlags |= enum_flag_HasFinalizer(0x00100000);

它的设置逻辑是如果存在析构函数,并且当前方法不是接口,不是虚方法,方法的索引小于当前类宗的索引数,当前的方法不是Object.Finlize()。那么说明当前这个类有析构函数,所以需要在当前类的MethodTable上进行操作,也即上面的m_dwFlags位设置。
逻辑代码如下:

//存在析构函数,并且当前方法不是接口,不是虚方法
if (g_pObjectFinalizerMD && !IsInterface() && !IsValueClass())
{WORD slot = g_pObjectFinalizerMD->GetSlot();//方法的索引小于当前类宗的索引数,当前的方法不是Object.Finlize()if (slot < bmtVT->cVirtualSlots && (*bmtVT)[slot].Impl().GetMethodDesc() != g_pObjectFinalizerMD){GetHalfBakedMethodTable()->SetHasFinalizer(); //这个地方就是设置m_dwFlags//此处省略一万行}
}

2.调用GC.SuppressFinalize
设置当前类的对象头headerobj|BIT_SBLK_FINALIZER_RUN
当我们调用GC.SuppressFinalize的时候,它会进行判断m_dwFlags或上的enum_flag_HasFinalizer位是否为1,如果位0直接返回,如果为1,则设置对象头。它的判断逻辑如下

if (!obj->GetMethodTable ()->HasFinalizer())//HasFinalizer函数判断m_dwFlags的enum_flag_HasFinalizer位
return;
GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);//这里设置当前类的对象头headerobj|BIT_SBLK_FINALIZER_RUN
BIT_SBLK_FINALIZER_RUN定义如下:
#define BIT_SBLK_FINALIZER_RUN   0x40000000

3.对象进行分配空间的时候
设置flags |= GC_ALLOC_FINALIZE
一个对象需要进行空间的分配,当进行空间分配的时候,它会判断当前函数是否包含了析构函数。如果包含了,则设置flags标志最后一位位1.然后在对象分配的时候,把它放入到析构队列里面去。

if (pMT->HasFinalizer())//判断当前类是否包含析构函数flags |= GC_ALLOC_FINALIZE;//如果包含则设置flags最后一位为1
GC_ALLOC_FINALIZE定义如下:
enum GC_ALLOC_FLAGS
{GC_ALLOC_NO_FLAGS           = 0,GC_ALLOC_FINALIZE           = 1,GC_ALLOC_CONTAINS_REF       = 2,GC_ALLOC_ALIGN8_BIAS        = 4,GC_ALLOC_ALIGN8             = 8,GC_ALLOC_ZEROING_OPTIONAL   = 16,GC_ALLOC_LARGE_OBJECT_HEAP  = 32,GC_ALLOC_PINNED_OBJECT_HEAP = 64,GC_ALLOC_USER_OLD_HEAP      = GC_ALLOC_LARGE_OBJECT_HEAP | GC_ALLOC_PINNED_OBJECT_HEAP,
};

当进行对象分配的时候,它会判断falgs最后一位是否为1,如果为1,则把对象放入到析构队列,不为1,则不放入。

CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE); //flags & GC_ALLOC_FINALIZE判断falgs最后一位是否为1.#define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {//这里的register就是flags & GC_ALLOC_FINALIZE的值,下面的逻辑如果对象为空直接返回,如果不为空则判断flags & GC_ALLOC_FINALIZE是否等于1,如果为零直接返回,如果为1,则调用REGISTER_FOR_FINALIZATION,把对象放入析构队列if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) {STRESS_LOG_OOM_STACK(_size);return NULL;}

以上是析构函数,GC.SuppressFinalize,Dispose的最底层逻辑。当然这里还有很多技术问题需要解决。后面再看。

标记的作用

GC.SuppressFinalize问题来了,它的这些标记有什么用呢?这是一个非常绕的问题,分析下。首先的enum_flag_HasFinalizer标记表示当前类包含了析构函数,GC_ALLOC_FINALIZE标记表示当前的类对象需要填充到析构队列里面去。而BIT_SBLK_FINALIZER_RUN标记是最为重要的,它如果被标记了则表示从析构队列里面溢出,不需要运行这个当前类的析构函数。

在GC的标记阶段标记对象是否存活完成之后,它需要对对象的析构队列进行扫描。如果析构队列(SegQueue)里的对象被标记存活,且它的对象头有

BIT_SBLK_FINALIZER_RUN标志,则表示此对象的析构队列里的对象可以移出了,也就是不运行此对象的析构函数。

//这里的ScanForFinalization是在GCScanRoot之运行的,还有一个从析构函数里面取出
//对象运行析构函数则是GCHeap::GetNextFinalizableObject
CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,gc_heap* hp)
{   //判断对象头是否标记了BIT_SBLK_FINALIZER_RUNif ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN){//如果标记了,则把这个对象移除到FreeList,也即是空闲的析构列表,不然存在于析构列表中MoveItem (i, Seg, FreeList);//然后清除掉此对象头BIT_SBLK_FINALIZER_RUN标志obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);}
}

欢迎关注我的公众号:jianghupt,后台回复:dotnet7。获取一套.Net7 CLR源码教程。顶级技术分享,文章首发。
image

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

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

相关文章

数据通信与网络必背考点(黑书)

补充部分 数据通信与网络必背考点(黑书)补充部分 数据通信与网络必背考点(黑书) 填空题 Part 1 1. 数据通信效率取决于 传递性、准确性、及时性、抖动性。2. 数据通信系统的组成 报文、发送方、接收方、传输介质、协议。3. 网络准则为 性能、可靠性、安全性。4. 协议三要…

win32-Ml.exe的用法

不同版本的MASM在使用上有很大的不同, 本节所指的是可用于Win 32汇编编程的MASM 6.14及以上版本, MASM编译器的命令行用法为: Ml [/选项] 汇编源文件列表 [/link链接选项] 要注意的是汇编选项要集中写在源文件名的前面,比如下面的两条命令:Ml /c /coff /Cp Test.asmMl …

Linux Awk command All In One

Linux Awk command All In One shell script Awk languageLinux Awk command All In Oneshell scriptAwkAWK (awk /ɔːk/) is a domain-specific language designed for text processing and typically used as a data extraction and reporting tool. Like sed and grep, it …

fildder工具--过滤展示指定的host包

fildder过滤指定的host 选中Filters ->第二个下拉框选中 show onle the following hosts ->输入指定的host地址如:*.baidu.com(如果有多个地址时,每个地址后用;分割)->点击Actions->选择Run Filterset now -> Ctrl+x清空fildder抓取到的包 ->重新请求地址(…

2023-2024-1 20231303 赵泊瑄《计算机基础与程序设计》第二周学习总结

2023-2024-1 学号20231303 《计算机基础与程序设计》第二周学习总结 作业信息这个作业属于哪个课程 如2023-2024-1-计算机基础与程序设计这个作业要求在哪里 作业要求的链接如2023-2024-1计算机基础与程序设计第周作业)这个作业的目标 总结第二周学习收获作业正文 2023-2024-1 …

Laravel RCE后渗透利用

常规laravel组件 RCE后的简单后渗透利用引言 水一篇文章,本文介绍了常规laravel组件 RCE后的简单后渗透利用,常见的RCE Nday例如:CVE-2021-3129,篇幅内很多利用方式与 Aspera Faspex RCE后渗透利用 文章中类似,因此就不赘述了。 维持权限 RCE通过反弹shell命令,获取nc sh…

day05-字符串

我们在上篇day04-数据类型中简单介绍了一下字符串,以及字符串的下标,今天我们来详细认识下字符串。字符串(str)可以使用单引号或双引号来创建字符串,并且字符串是不可变的数据类型,字符串也是Python中最常用的数据类型,所以我们一定学会它,学习字符串一定先熟悉概念,知…

08:信息收集-架构,搭建,WAF等

前言:在安全测试中,信息收集是非常重要的一个环节,此环节的信息将影响到后续的成功几率,掌握信息的多少将决定发现漏洞机会大小,换言之决定着是否能完成目标的测试任务。也可以很直接的跟大家说:渗透测试的思路就是从信息收集这里开始,你与大牛的差距也是从这里开始的!…

输入一个5位数,逐个打印出这5位数的个位、十位、百位、千位、万位。

五位数I 描述输入一个5位数,逐个打印出这5位数的个位、十位、百位、千位、万位。输入输入为一个五位整数输出输出其万位、千位、百位、十位、个位 各式多少,每一位之间用一个空格隔开输入样例 1 12345输出样例 1 1 2 3 4 5 # 输入一个五位整数 num = int(input())# 提取并打…

Go with Protobuf

原文在这里。本教程为 Go 程序员提供了使用Protocol buffer的基本介绍。本教程使用proto3向 Go 程序员介绍如何使用 protobuf。通过创建一个简单的示例应用程序,它向你展示了如何:在.proto中定义消息格式 使用protocol buffer编译器 使用Go protocol buffer API读写消息这并不…

软考1

二进制转八进制——从小数点开始三位一组,不够补零 十进制转R进制——短除法 eg:十进制转二进制——不断除二取余直到零,逆向取余数 二进制B 十进制D 十六进制H通常只有无符号取证 原码符号位上,零正一负 机器字长第一位为符号位,其余为数值位 反码——正数的反码等于原…

第四次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/uzz/cs3这个作业要求在哪里 https://edu.cnblogs.com/campus/uzz/cs3/homework/13061这个作业的目标 第4次作业-SQL语句的基本使用4-分组查询、连接查询select 学号,姓名,性别,出生日期,家庭住址 from student_info order…

在python中pip安装boto3

安装: https://aka.ms/vs/16/release/vc_redist.x64.exe安装之后重启。pip install scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple如何在python中pip安装boto3 在python目录cmd下输入:pip install botocore -i pip install boto3 -i https://pypi.tuna.tsinghu…

GitHub CEO:AI 无法取代程序员

导读 GitHub 首席执行官 Thomas Dohmke 最近在公开场合分享了他对于人工智能和软件开发之间关系的看法。Thomas Dohmke 认为,在 Copilot 及其相关 Copilot Chat 等辅助工具的推动下,人工智能和软件开发现在已密不可分。 与此同时,他也坚持自己的观点 —— 滚雪球式的人工智能…

c语言代码练习(小游戏)24

太马虎,‘ ’和" "注意区分需求:井字棋 第一个文件:game.h#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <time.h> #pragma once #define ROW 3 #define LOC 3void IntBoard(char arr[ROW][LOC], int rot,…

ubuntu个人物理机分区demo

如图 800G演示分配注意本文来自博客园,作者:__username,转载请注明原文链接:https://www.cnblogs.com/code3/p/17747935.html

【Python】如何使用PyInstaller打包自己写好的代码

使用PyInstaller打包自己写好的代码 零、需求 最近接到一个小单,需要批量修改文档内容,用Python做好后要打包成exe程序给客户的Win7电脑使用,此时需要用到PyInstaller打包自己的代码,想到还要有给用户试用的需求,所以还要加密打包。这里介绍一下如何打包并“加密”自己的P…

专业综合课程设计 - 优阅书城项目(第三版)

此项目是《专业综合课程设计》带练项目实现的功能有: 登录、注销、添加图书、删除图书、编辑图书、分页功能(支持首页,上一页,下一页,末页,到第N页)、首页价格查询分页功能包含资源: 优阅书城(bookstore)源码 数据库数据 课程笔记下载链接:https://wwpv.lanzoue.com…
推荐文章