MSIL系列:關于Boxing和堆棧,棧的幾個問題的回答
累啊,近來事情也比較多,這里就簡要的同意回答一下大家提出的問題吧.
就使用jiaoer的一段代碼來說明問題:
namespace MSILTest
{
public struct Point
{
public int x;
}
class Program
{
static void Main(string[] args)
{
Point p = new Point();
p.x = 1;
object o = p;
p.x = 2;
System.Console.WriteLine(p.x+","+o);
}
}
}
調試的過程中,輸出的是2,MSILTest.Point
這里首先說明一下,輸出的后面的一部分,MSILTest.Point是非常有學問的,通過研究這段程序的MSIL代碼,以及反編譯FCL中的String類的一個Concat方法的實現,以及類在繼承中若干方法的重寫,我們就可以了解為什么是輸出MSILTest.Point;當然,如果Point是個類,里面只有一個int屬性,就會直接輸出int屬性的值.
這里,p是2很好理解.我們在object o = p;這一行下一個斷點,F5調試,然后F10單步執行兩次.這時,鼠標放在o上面,可以看到o的值是1,而不是2.
這就說明了一個問題,boxing以后,原來的計算堆棧上面的值并沒有刪除;boxing只是復制這個valuetype到heap的對象的相應區域中.這個對象相應區域的valuetype這個時候就是值類型的另外一個類型:boxed type;
同時,在新生成了一個包含這個值類型數值的對象以后,返回這個對象的應用到計算堆棧的棧頂.
有一點大家要非常注意:unboxing并不包含對數值的復制.這點相當重要.
這里涉及到MSIL里面三個指令的等價關系的問題.
就是unbox.any這個指令下發生的事情,等于unbox指令發生的事情,加上ldobj這個指令發生的事情.box指令做的事情就是大家理解的拆箱,而ldobj執行的事情,是把這個值類型的數值復制到棧頂.
拆箱這個名詞,指的是box這個指令發生的操作,并不包含數值的復制到棧頂這部分.所以MSIL又設計了一個unbox.any指令來完成這兩個動作,這個非常廣泛上的誤區大家一定要區別清楚!
好了,下面來非常簡單的說說線程堆棧,計算堆棧和堆棧,還有堆的區別.
這里設計到一個堆和棧的概念,大家通常說的堆就是托管堆,也就是heap.而通常說的棧就是計算堆棧,也就是stack.很多地方大家也習慣叫stack為堆棧.
而線程堆棧,里面涉及到的東西有多多了.當一個方法在執行的時候,一個外部存儲和一個本地存儲區域會分配給它.本地存儲區域,會分配一個本地變量列表,一個計算堆棧,一個參變量表.這三個東西就是一個方法執行的時候需要的全部的東西.
本地存儲區域作為一個field,和很多方法一起,放在了線程堆棧上面.每個現在正在執行的方法,就是線程堆棧上面最上面的方法.執行好了之后,pointer移動,這個方法就彈出去了.
所有的MSIL指令都是基于堆棧的,每個線程在分配的時候分配1m,這個在manifest文件里面有標識.
不過我算了好久就納悶0x100000哪里是表示1m了....
在有一個就是我們研究linux操作系統在給線程分配空間的時候,應該是MmCreateMemoryArea這個方法指定了分配給一個線程的空間大小,好想是這個方法,看了之后就忘記了 :)
大家可以找漫談"兼容內核之二十一.pdf"這么一個文件,里面有講的.
額,回答大家的問題有點簡略啊,呵呵,不好意思,很多東西都沒寫詳細,扯起來又是得幾千字才能說明白.
主要是最近有點累,事情也比較多.不知道給jiaoer講清楚了沒.
恩,鑒于國內在MSIL這方面的研究比較少,文檔也不全,可能會寫一個<<MSIL via CLR>>的系列吧,估計很多很多字,大家期待吧.
下一篇博文研究內容:.Net下PE文件詳解
posted on 2007-10-28 14:17 lbq1221119 閱讀(1100) 評論(13) 收藏 舉報
浙公網安備 33010602011771號