2014-03-05 61 views
12

我試圖找出如何使用fixed關鍵字定義的固定指針如何工作。我的想法是,內部使用GCHandle.Alloc(object, GCHandleType.Pinned)。但是,當我看着成下面的C#代碼產生的白細胞介素:`fixed`與GCHandle.Alloc(obj,GCHandleType.Pinned)

unsafe static void f1() 
{ 
    var arr = new MyObject[10]; 
    fixed(MyObject * aptr = &arr[0]) 
    { 
    Console.WriteLine(*aptr); 
    } 
} 

我再也找不到GCHandle任何痕跡。

我看到固定指針在該方法中使用的唯一線索是下面的IL聲明:

.locals init ([0] valuetype TestPointerPinning.MyObject[] arr, 
       [1] valuetype TestPointerPinning.MyObject& pinned aptr) 

因此被宣佈爲指針固定,而且不需要任何額外的方法調用,以壓住他。

我的問題是

  • 有沒有在聲明中使用固定指針和使用GCHandle類釘住指針之間有什麼區別?
  • 有什麼辦法可以在不使用fixed關鍵字的情況下在C#中聲明固定指針?我需要這個在一個循環內連接一堆指針,而且我無法使用固定關鍵字來完成這個任務。

回答

14

那麼,當然有一個區別,你看到它。 CLR支持多種方式來固定對象。只有GCHandleType.Pinned方法直接暴露給用戶代碼。但還有其他一些功能,如"async pinned handles",這是一種在驅動程序執行重疊I/O操作時保持I/O緩衝區固定的功能。而且,修復了關鍵字使用的那個,它根本不使用顯式句柄或方法調用。添加了這些額外的方法,以儘可能快速和可靠地再次固定對象,這對於GC健康非常重要。

固定緩衝器引腳由抖動實現。當它將MSIL轉換爲機器碼時,執行兩個重要的作業,高度可見的是機器碼本身,您可以很容易地用調試器看到它​​。但它也會生成垃圾收集器使用的數據結構,在調試器中完全不可見。 GC需要可靠地查找存儲在堆棧幀或CPU寄存器中的對象引用。更多關於this answer中的數據結構。

抖動使用元數據中變量聲明上的[pinned]屬性來設置該數據結構中的一個位,表示該變量所引用的對象是暫時固定的。 GC看到這一點,並知道不會移動該對象。非常高效,因爲它不需要顯式的方法調用來分配句柄並且不需要任何存儲。

不過沒有關係,這些技巧不可,否則到C#代碼,你真的需要使用固定關鍵字在你的代碼。或者GCHandle.Alloc()。如果你發現自己迷失在引腳中,那麼你應該考慮使用pinvoke或C++/CLI,這樣你就可以輕鬆地調用本地代碼。 pinvoke編組用於在本機代碼運行時保持對象穩定的臨時引腳是不需要顯式代碼的自動pinning的另一個示例。