狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

一張圖帶你了解.NET終結(jié)(Finalize)流程

freeflydom
2024年10月12日 9:34 本文熱度 383

簡介

"終結(jié)"一般被分為確定性終結(jié)(顯示清除)與非確定性終結(jié)(隱式清除)

  1. 確定性終結(jié)主要
    提供給開發(fā)人員一個顯式清理的方法,比如try-finally,using。

  2. 非確定性終結(jié)主要
    提供一個注冊的入口,只知道會執(zhí)行,但不清楚什么時候執(zhí)行。比如IDisposable,析構(gòu)函數(shù)。

為什么需要終結(jié)機制?

首先糾正一個觀念,終結(jié)機制不等于垃圾回收。它只是代表當(dāng)某個對象不再需要時,我們順帶要執(zhí)行一些操作。更加像是附加了一種event事件。
所以網(wǎng)絡(luò)上有一種說法,IDisposable是為了釋放內(nèi)存。這個觀念并不準(zhǔn)確。應(yīng)該形容為一種兜底更為貼切。
如果是一個完全使用托管代碼的場景,整個對象圖由GC管理,那確實不需要。在托管環(huán)境中,終結(jié)機制主要用于處理對象所持有的,不被GC和runtime管理的資源。
比如HttpClient,如果沒有終結(jié)機制,那么當(dāng)對象被釋放時,GC并不知道該對象持有了非托管資源(句柄),導(dǎo)致底層了socket連接永遠(yuǎn)不會被釋放。

如前所述,終結(jié)器不一定非得跟非托管資源相關(guān)。它的本質(zhì)是”對象不可到達(dá)后的do something“.
比如你想收集對象的創(chuàng)建與刪除,可以將記錄代碼寫在構(gòu)造函數(shù)與終結(jié)器中

終結(jié)機制的源碼

源碼
namespace Example_12_1_3
{
    internal class Program
    {
        static void Main(string[] args)
        {
            TestFinalize();
            Console.WriteLine("GC is start. ");
            GC.Collect();
            Console.WriteLine("GC is end. ");
            Debugger.Break();
            Console.ReadLine();
            Console.WriteLine("GC2 is start. ");
            GC.Collect();
            Console.WriteLine("GC2 is end. ");
            Debugger.Break();
            Console.ReadLine();
        }
        static void TestFinalize()
        {
            var list = new List<Person>(1000);
            for (int i = 0; i < 1000; i++)
            {
                list.Add(new Person());
            }
            var personNoFinalize = new Person2();
            Console.WriteLine("person/personNoFinalize分配完成");
            Debugger.Break();
        }
    }
    public class Person
    {
        ~Person()
        {
            Console.WriteLine("this is finalize");
            Thread.Sleep(1000);
        }
    }
    public class Person2
    {
    }
}
IL
	// Methods
	.method family hidebysig virtual 
		instance void Finalize () cil managed 
	{
		.override method instance void [mscorlib]System.Object::Finalize()
		// Method begins at RVA 0x2090
		// Header size: 12
		// Code size: 30 (0x1e)
		.maxstack 1
		IL_0000: nop
		.try
		{
			// {
			IL_0001: nop
			// Console.WriteLine("this is finalize");
			IL_0002: ldstr "this is finalize"
			IL_0007: call void [mscorlib]System.Console::WriteLine(string)
			// Console.ReadLine();
			IL_000c: nop
			IL_000d: call string [mscorlib]System.Console::ReadLine()
			IL_0012: pop
			// }
			IL_0013: leave.s IL_001d
		} // end .try
		finally
		{
			// (no C# code)
			IL_0015: ldarg.0
			IL_0016: call instance void [mscorlib]System.Object::Finalize()
			IL_001b: nop
			IL_001c: endfinally
		} // end handler
		IL_001d: ret
	} // end of method Person::Finalize
匯編
0199097B  nop  
0199097C  mov         ecx,dword ptr ds:[4402430h]  
01990982  call        System.Console.WriteLine(System.String) (72CB2FA8h)  
01990987  nop  
01990988  call        System.Console.ReadLine() (733BD9C0h)  
0199098D  mov         dword ptr [ebp-40h],eax  
01990990  nop  
01990991  nop  
01990992  mov         dword ptr [ebp-20h],offset Example_12_1_3.Person.Finalize()+045h (00h)  
01990999  mov         dword ptr [ebp-1Ch],0FCh  
019909A0  push        offset Example_12_1_3.Person.Finalize()+06Ch (019909BCh)  
019909A5  jmp         Example_12_1_3.Person.Finalize()+057h (019909A7h)  
可以看到,C#的析構(gòu)函數(shù)只是一種語法糖。IL重寫了System.Object.Finalize方法。在底層的匯編中,直接調(diào)用的就是Finalize()



終結(jié)的流程

補充一個細(xì)節(jié),實際上f-reachable queue 內(nèi)部還分為Critical/Normal兩個區(qū)間,其區(qū)別在于是否繼承自CriticalFinalizerObject。
目的是為了保證,即使在AppDomain或線程被強行中斷的情況下,也一定會執(zhí)行。
一般也很少直接繼承CriticalFinalizerObject,更常見是選擇繼承SafeHandle.
不過在.net core中區(qū)別不大,因為.net core不支持終止線程,也不支持卸載AppDomain。

眼見為實

使用windbg看一下底層。
1. 創(chuàng)建Person對象,是否自動進(jìn)入finalize queue?

可以看到,當(dāng)new obj 時,finalize queue中已經(jīng)有了Person對象的析構(gòu)函數(shù)

2. GC開始后,是否移動到F-Reachable queue?

可以看到代碼中創(chuàng)建的1000個Person的析構(gòu)函數(shù)已經(jīng)進(jìn)入了F-Reachable queue

sosex !finq/!frq 指令同樣可以輸出

3. 析構(gòu)對象是否被"復(fù)活"?
GC發(fā)生前,在TestFinalize方法中創(chuàng)建了兩個變量,person=0x02a724c0,personNoFinalize=0x02a724cc。


可以看到所屬代都為0,且托管堆中都能找到它們。

GC發(fā)生后



可以看到,Person2對象因為被回收而在托管堆中找不到了,Person對象因為還未執(zhí)行析構(gòu)函數(shù),所以還存在gcroot 。因此并未被回收,且內(nèi)存代從0代提升到1代

4. 終結(jié)線程是否執(zhí)行,是否被移出F-Reachable queue


在GC將托管線程從掛起到恢復(fù)正常后,且F-Reachable queue 有值時,終結(jié)線程將亂序執(zhí)行。
并將它們移出隊列

5. 析構(gòu)函數(shù)的對象是否在第二次GC中釋放?
等到第二次GC發(fā)生后,由于對象析構(gòu)函數(shù)已經(jīng)被執(zhí)行,不再擁有g(shù)croot,所以托管堆最終釋放了該對象,

6. 析構(gòu)函數(shù)如果沒有及時執(zhí)行完成,又觸發(fā)了一次GC。會不會再次升代?

答案是肯定的

Finaze Queue/F-Reachable Queue 底層結(jié)構(gòu)

眼見為實


每個不同的代,維護(hù)在不同的內(nèi)存地址中,但彼此之間的內(nèi)存地址又緊密聯(lián)系在一起。


與GC代優(yōu)點細(xì)微區(qū)別的是,沒有LOH概念,大對象分配在0代中。Person3對象是一個 new byte[8500000]。 其他行為與GC代保持一致

終結(jié)的開銷

  1. 如果一個類型具有終結(jié)器,將使用慢速分支執(zhí)行分配操作
    且在分配時還需要額外進(jìn)入finalize queue而引入的額外開銷

  2. 終結(jié)器對象至少要經(jīng)歷2次GC才能夠被真正釋放
    至少兩次,可能更多。終結(jié)線程不一定能在兩次GC之間處理完所有析構(gòu)函數(shù)。此時對象從1代升級到2代,2代對象觸發(fā)GC的頻率更低。導(dǎo)致對象不能及時被釋放(析構(gòu)函數(shù)已經(jīng)執(zhí)行完畢,但是對象本身等了很久才被釋放)。

  3. 對象升代/降代時,finalize queue也要重復(fù)調(diào)整
    與GC分代一樣,也分為3個代和LOH。當(dāng)一個對象在GC代中移動時,對象地址也需要也需要在finalization queue移動到對應(yīng)的代中.
    由于finalize queue與f-reachable queue 底層由同一個數(shù)組管理,且元素之間并沒有留空。所以升代/降代時,與GC代不同,GC代可以見縫插針的安置對象,而finalize則是在對應(yīng)的代末尾插入,并將后面所有對象右移一個位置

眼見為實

點擊查看代碼
    public class BenchmarkTester
    {
        [Benchmark]
        public void ConsumeNonFinalizeClass()
        {
            for (int i = 0; i < 1000; i++)
            {
                var obj = new NonFinalizeClass();
                obj.Age = i;
            }
        }
        [Benchmark]
        public void ConsumeFinalizeClass()
        {
            for (int i = 0; i < 1000; i++)
            {
                var obj = new FinalizeClass();
                obj.Age = i;
            }
        }
    }

非常明顯的差距,無需解釋。

總結(jié)

使用終結(jié)器是比較棘手且不完全可靠。因此最好避免使用它。只有當(dāng)開發(fā)人員沒有其他辦法(IDisposable)來釋放資源時,才應(yīng)該把終結(jié)器作為最后的兜底

轉(zhuǎn)自https://www.cnblogs.com/lmy5215006/p/18456380


該文章在 2024/10/12 9:43:19 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved