C#運行時的相互關係

來源:互聯網
上載者:User

C#運行時的相互關係

本部落客要講述運行時類型、對象、線程棧和託管堆之間的相互關係,靜態方法、執行個體方法和虛方法的區別,以及記憶體的分配和回收。

線程棧:在一個進程中可能包含多個線程,一個線程在建立的時候,會分配到一個大小1MB大小的棧,棧用於儲存方法的實參、形參以及方法內部的局部變數,棧是從高位記憶體位址向地位地址構建的,由於棧有先進後出的特點,所以先定義的變數後被回收。

下面來看一個簡單的例子,讓你更瞭解線程棧

由於線程棧是從高位開始分配記憶體,先分配的我就畫在上面了,在調用F1();方法時,分配記憶體的順序是:name->n->F2的返回地址->Age->name;回收記憶體的順序當然是反過來的。在一個方法中,應該包含一些序幕代碼,進行一些初始化工作,還有一些尾聲代碼,等方法執行完成之後做一些回收工作。由於方法的返回地址先分配,在方法執行完成的時候回到返回地址,遞迴太深就容易出現棧溢出,請看我的《遞迴再一次讓哥震驚了》,因為參數、局部變數都必須等到方法返回的時候才能回收。

在介紹託管堆之前先看看兩個簡單的類:

    public class Person
{
private int height;
public void SetHeight(int height)
{
this.height = height;
}
public virtual void Say(string word) { }
public static string Head()
{
return "my head";
}
public static int Age = 100;
}
public class Student : Person
{
public override void Say(string word)
{
Console.WriteLine(word);
}
}

static void Main(string[] args)
{
Person student = new Student();
student.Say("Hello cth");
student.SetHeight(172);
Person.Head();
Console.ReadLine();
}

 

CLR會在第一次訪問一個對象時載入該對象,在這裡,定義變數student時會為Person對象線上程棧中分配記憶體,第一次載入嗎,在構造一個Student對象之前先要載入Student對象,並為Student類型對象分配記憶體,並構建一個Student對象。對象的地址存入線程棧中的局部變數student 中,我們知道類型對象的內容包含:類型對象指標、同步索引塊、靜態欄位和方法(靜態和非靜態),不管是類型對象、還是執行個體類型都必須有類型對象指標、同步索引塊;我們知道靜態欄位屬於類,被這個類的所有執行個體共用,當然靜態欄位的記憶體是在類型本身中分配的,方法也是類的所有執行個體共用的,他的記憶體也是在類型本身中分配的,在每一個類型對象中都有一個方法表,類中定義的方法都有一個對應的項。

在構造一個對象的執行個體時,只需要為類型對象指標、同步索引塊、該對象的執行個體欄位分配記憶體,對於對象執行個體來說,類型對象指標可以讓執行個體訪問類型對象中德靜態欄位、方法等。

Student是線程棧中的定義的一個局部變數,儲存Student的一個執行個體的在託管堆中的地址,所以他可以訪問Student對象中的欄位,方法,其實存取方法是通過類型對象指標訪問類型對象Student中的方法表中對象的項。

Say方法的執行過程:變數student指向的是一個Student對象,調用的當然是Student類型對象中的Say方法,儘管在定義student的時候是Person類型,因為他是參考型別,他指向的是託管堆中Student對象的記憶體,然後遍曆該對象的方法表,找到該方法調用。

特別說明虛方法,JIT在虛方法中加了一些額外的代碼,方法每次調用的時候都會執行這些代碼,這些代碼會檢查發出調用的變數,然後根據這個變數找到其應用的對象,然後調用這個對象的方法,若沒有這些代碼,你覺得CLR是調用父類的方法還是調用之類的方法呢,虛方法帶來方便的同時,也多了這些必須的檢查的代碼。

SetHeight方法的執行過程:和Say方法前面是一樣,只是在遍曆Student對象的方法表時沒有找到該方法,我們知道父類中定義的非private方法都可以被子類繼承,是因為每個類型都定義了一個欄位引用了他的基類,如果一個類調用的方法那個方法不是自己定義的,那麼編譯器會回溯類階層,一直到基類Object,找到相關的方法並調用,如果沒有找到相關的方法就報了異常唄。所以SetHeight方法其實調用的是Person中的SetHeight方法。

Head方法的執行:由於Head方法是靜態方法和上面兩個方法有所不同,調用靜態方法的時候,CLR會定位與靜態方法對象的類型對象,然後在對應執行個體對象對象的方法表中尋找相關的記錄項,如果沒有找到,同樣會回溯。

當執行完student.SetHeight(172);時,student在也沒有被引用,成為垃圾,在其所在的方法返回之前將會被回收,也就是說student執行個體對象被回收,釋放其所在的記憶體,而類型對象不會被回收,類型對象的組建循環是:對象被載入到CLR中,直到其所在的AppDomain卸載。靜態欄位是他所參考型別的跟,所以被靜態類型引用的對象永遠不會被回收,如果其引用的是一個集合對象,並向其中不斷的加入元素的話,就會造成記憶體泄露,更多關於記憶體管理記憶體回收,請看我的另一篇部落格《記憶體回收--代》

 

作者:陳太漢

部落格:http://www.cnblogs.com/hlxs/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.