本博客主要講述運行時類型
對象
線程棧和托管堆之間的相互關系
靜態方法
實例方法和虛方法的區別
以及內存的分配和回收
線程棧
在一個進程中可能包含多個線程
一個線程在創建的時候
會分配到一個大小
MB大小的棧
棧用於存儲方法的實參
形參以及方法內部的局部變量
棧是從高位內存地址向地位地址構建的
由於棧有先進後出的特點
所以先定義的變量後被回收
下面來看一個簡單的例子
讓你更了解線程棧
由於線程棧是從高位開始分配內存先分配的我就畫在上面了在調用F()方法時分配內存的順序是name>n>F的返回地址>Age>name回收內存的順序當然是反過來的在一個方法中應該包含一些序幕代碼進行一些初始化工作還有一些尾聲代碼等方法執行完成之後做一些回收工作由於方法的返回地址先分配在方法執行完成的時候回到返回地址遞歸太深就容易出現棧溢出請看我的《遞歸再一次讓哥震驚了》因為參數局部變量都必須等到方法返回的時候才能回收
在介紹托管堆之前先看看兩個簡單的類
publicclassPerson
{
privateintheight;
publicvoidSetHeight(intheight)
{
thisheight = height;
}
publicvirtualvoidSay(stringword) { }
publicstaticstringHead()
{
returnmy head;
}
publicstaticintAge = ;
}
publicclassStudent : Person
{
publicoverridevoidSay(stringword)
{
ConsoleWriteLine(word);
}
}
staticvoidMain(string[] args)
{
Person student = newStudent();
studentSay(Hello cth);
studentSetHeight();
PersonHead();
ConsoleReadLine();
}
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會定位與靜態方法對象的類型對象然後在對應實例對象對象的方法表中查找相關的記錄項如果沒有找到同樣會回溯
From:http://tw.wingwit.com/Article/program/ASP/201311/21810.html