本篇繼續(xù)看下析構(gòu)函數(shù)的一些引申知識(shí)。
析構(gòu)函數(shù)目前發(fā)現(xiàn)的總共有三個(gè)標(biāo)記,這里分別一一介紹下。先上一段代碼:
internal class Program : IDisposable{ static void Main(string[] args){ StreamReader? streamReader = null; streamReader = new StreamReader("Test_.dll"); streamReader?.Dispose(); Console.ReadLine(); } ~Program(){ Console.WriteLine("調(diào)用了析構(gòu)函數(shù)"); } public void Dispose(){ this.Dispose(); GC.SuppressFinalize(this); } }
這里的析構(gòu)函數(shù)跟Dispose一起混用, ~Program()析構(gòu)函數(shù)會(huì)通過Roslyn生成
.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
這里同時(shí)需要注意 streamReader?.Dispose();這句話,streamreader實(shí)際上繼承的是textreader
public class StreamReader : TextReader{}
所以它調(diào)用Dispose的代碼是TextReader里面的Dispose:
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
也就是關(guān)閉了streamReader流。然后base.Dispose.這個(gè)base.Dispose實(shí)際上就是它的父類TextReader里面的
public void Dispose(){ this._streamReader.close();}
Dispose里面的下面一句代碼
GC.SuppressFinalize(this);
它是重點(diǎn)。
如果類里面有析構(gòu)函數(shù),比如例子里的Program,則會(huì)設(shè)置MethodTable的成員m_dwFlags
m_dwFlags |= enum_flag_HasFinalizer(0x00100000);
它的設(shè)置邏輯是如果存在析構(gòu)函數(shù),并且當(dāng)前方法不是接口,不是虛方法,方法的索引小于當(dāng)前類的索引數(shù),當(dāng)前的方法不是Object.Finlize()。那么說明當(dāng)前這個(gè)類有析構(gòu)函數(shù),所以需要在當(dāng)前類的MethodTable上進(jìn)行操作,也即上面的m_dwFlags位設(shè)置。
邏輯代碼如下:
//存在析構(gòu)函數(shù),并且當(dāng)前方法不是接口,不是虛方法if (g_pObjectFinalizerMD && !IsInterface() && !IsValueClass()){ WORD slot = g_pObjectFinalizerMD->GetSlot(); //方法的索引小于當(dāng)前類宗的索引數(shù),當(dāng)前的方法不是Object.Finlize() if (slot < bmtVT->cVirtualSlots && (*bmtVT)[slot].Impl().GetMethodDesc() != g_pObjectFinalizerMD) { GetHalfBakedMethodTable()->SetHasFinalizer(); //這個(gè)地方就是設(shè)置m_dwFlags //此處省略一萬行 }}
headerobj|BIT_SBLK_FINALIZER_RUN當(dāng)我們調(diào)用GC.SuppressFinalize的時(shí)候,它會(huì)進(jìn)行判斷m_dwFlags或上的enum_flag_HasFinalizer位是否為1,如果位0直接返回,如果為1,則設(shè)置對(duì)象頭。它的判斷邏輯如下
if (!obj->GetMethodTable ()->HasFinalizer())//HasFinalizer函數(shù)判斷m_dwFlags的enum_flag_HasFinalizer位return;GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);//這里設(shè)置當(dāng)前類的對(duì)象頭headerobj|BIT_SBLK_FINALIZER_RUNBIT_SBLK_FINALIZER_RUN定義如下:#define BIT_SBLK_FINALIZER_RUN 0x40000000
設(shè)置flags |= GC_ALLOC_FINALIZE一個(gè)對(duì)象需要進(jìn)行空間的分配,當(dāng)進(jìn)行空間分配的時(shí)候,它會(huì)判斷當(dāng)前函數(shù)是否包含了析構(gòu)函數(shù)。如果包含了,則設(shè)置flags標(biāo)志最后一位位1.然后在對(duì)象分配的時(shí)候,把它放入到析構(gòu)隊(duì)列里面去。
if (pMT->HasFinalizer())//判斷當(dāng)前類是否包含析構(gòu)函數(shù) flags |= GC_ALLOC_FINALIZE;//如果包含則設(shè)置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,};
當(dāng)進(jìn)行對(duì)象分配的時(shí)候,它會(huì)判斷falgs最后一位是否為1,如果為1,則把對(duì)象放入到析構(gòu)隊(duì)列,不為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的值,下面的邏輯如果對(duì)象為空直接返回,如果不為空則判斷flags & GC_ALLOC_FINALIZE是否等于1,如果為零直接返回,如果為1,則調(diào)用REGISTER_FOR_FINALIZATION,把對(duì)象放入析構(gòu)隊(duì)列 if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) { STRESS_LOG_OOM_STACK(_size); return NULL; }
以上是析構(gòu)函數(shù),GC.SuppressFinalize,Dispose的最底層邏輯。當(dāng)然這里還有很多技術(shù)問題需要解決。后面再看。
而BIT_SBLK_FINALIZER_RUN標(biāo)記是最為重要的,它如果被標(biāo)記了則表示從析構(gòu)隊(duì)列里面溢出,不需要運(yùn)行這個(gè)當(dāng)前類的析構(gòu)函數(shù)。
在GC的標(biāo)記階段標(biāo)記對(duì)象是否存活完成之后,它需要對(duì)對(duì)象的析構(gòu)隊(duì)列進(jìn)行掃描。如果析構(gòu)隊(duì)列(SegQueue)里的對(duì)象被標(biāo)記存活,且它的對(duì)象頭有
BIT_SBLK_FINALIZER_RUN標(biāo)志,則表示此對(duì)象的析構(gòu)隊(duì)列里的對(duì)象可以移出了,也就是不運(yùn)行此對(duì)象的析構(gòu)函數(shù)。
//這里的ScanForFinalization是在GCScanRoot之運(yùn)行的,還有一個(gè)從析構(gòu)函數(shù)里面取出//對(duì)象運(yùn)行析構(gòu)函數(shù)則是GCHeap::GetNextFinalizableObjectCFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p, gc_heap* hp){ //判斷對(duì)象頭是否標(biāo)記了BIT_SBLK_FINALIZER_RUN if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN) { //如果標(biāo)記了,則把這個(gè)對(duì)象移除到FreeList,也即是空閑的析構(gòu)列表,不然存在于析構(gòu)列表中 MoveItem (i, Seg, FreeList); //然后清除掉此對(duì)象頭BIT_SBLK_FINALIZER_RUN標(biāo)志 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN); }}
本文鏈接:http://www.www897cc.com/showinfo-26-12734-0.html.Net析構(gòu)函數(shù)再論(源碼剖析)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com
上一篇: Docker容器化構(gòu)建可擴(kuò)展的分布式緩存系統(tǒng):Memcached和Redis
下一篇: 喝了100杯醬香拿鐵,我開竅了