当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-10-10 18:31:51 161观看
导读1.前言本篇继续看下析构函数的一些引申知识。2.概述析构函数目前发现的总共有三个标记,这里分别一一介绍下。先上一段代码:internal class Program : IDisposable{ static void Main(string[] args){ Stre

1.前言

本篇继续看下析构函数的一些引申知识。06428资讯网——每日最新资讯28at.com

2.概述

析构函数目前发现的总共有三个标记,这里分别一一介绍下。先上一段代码:06428资讯网——每日最新资讯28at.com

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生成06428资讯网——每日最新资讯28at.com

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

这里同时需要注意 streamReader?.Dispose();这句话,streamreader实际上继承的是textreader06428资讯网——每日最新资讯28at.com

public class StreamReader : TextReader{}

所以它调用Dispose的代码是TextReader里面的Dispose:06428资讯网——每日最新资讯28at.com

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

也就是关闭了streamReader流。然后base.Dispose.这个base.Dispose实际上就是它的父类TextReader里面的06428资讯网——每日最新资讯28at.com

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

Dispose里面的下面一句代码06428资讯网——每日最新资讯28at.com

GC.SuppressFinalize(this);

它是重点。06428资讯网——每日最新资讯28at.com

GC.SuppressFinalize

1.判断当前类是否有析构函数

如果类里面有析构函数,比如例子里的Program,则会设置MethodTable的成员m_dwFlags06428资讯网——每日最新资讯28at.com

m_dwFlags |= enum_flag_HasFinalizer(0x00100000);

它的设置逻辑是如果存在析构函数,并且当前方法不是接口,不是虚方法,方法的索引小于当前类的索引数,当前的方法不是Object.Finlize()。那么说明当前这个类有析构函数,所以需要在当前类的MethodTable上进行操作,也即上面的m_dwFlags位设置。06428资讯网——每日最新资讯28at.com

逻辑代码如下:06428资讯网——每日最新资讯28at.com

//存在析构函数,并且当前方法不是接口,不是虚方法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,则设置对象头。它的判断逻辑如下06428资讯网——每日最新资讯28at.com

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

3.对象进行分配空间的时候

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

if (pMT->HasFinalizer())//判断当前类是否包含析构函数    flags |= GC_ALLOC_FINALIZE;//如果包含则设置flags最后一位为1GC_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,则不放入。06428资讯网——每日最新资讯28at.com

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的最底层逻辑。当然这里还有很多技术问题需要解决。后面再看。06428资讯网——每日最新资讯28at.com

标记的作用

GC.SuppressFinalize问题来了,它的这些标记有什么用呢?这是一个非常绕的问题,分析下。首先的enum_flag_HasFinalizer标记表示当前类包含了析构函数,GC_ALLOC_FINALIZE标记表示当前的类对象需要填充到析构队列里面去。

而BIT_SBLK_FINALIZER_RUN标记是最为重要的,它如果被标记了则表示从析构队列里面溢出,不需要运行这个当前类的析构函数。06428资讯网——每日最新资讯28at.com

在GC的标记阶段标记对象是否存活完成之后,它需要对对象的析构队列进行扫描。如果析构队列(SegQueue)里的对象被标记存活,且它的对象头有06428资讯网——每日最新资讯28at.com

BIT_SBLK_FINALIZER_RUN标志,则表示此对象的析构队列里的对象可以移出了,也就是不运行此对象的析构函数。06428资讯网——每日最新资讯28at.com

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

本文链接:http://www.28at.com/showinfo-26-12734-0.html.Net析构函数再论(源码剖析)

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Docker容器化构建可扩展的分布式缓存系统:Memcached和Redis

下一篇: 喝了100杯酱香拿铁,我开窍了

标签:
  • 热门焦点
Top