相信很多人在這樣或那樣的項目中,或者無意間看到了fixed語句塊,看到之后你肯定會疑問:
1、這個fixed關鍵字是做什么用的?
2、什么情況下需要該關鍵字?
3、這個關鍵字該怎么用?
我相信解決了上面四個問題之后,你對這個fixed語句就理解和掌握到位了,我也在網上大致瀏覽了下,網上關於該關鍵字的詳細說明太少太少了,基本都是摘抄MSDN官方文檔,毫無自身理解與發散出來的東西,當然完全依據MSDN的只言片文也能理解不過相當費勁,在這里我結合自己的理解給大家說明下該關鍵字的用法,希望各位看過之后能給出自己的想法。
在MSDN如下介紹:
1、fixed 語句禁止垃圾回收器重定位可移動的變量。fixed 語句只能出現在不安全的上下文中。Fixed 還可用於創建固定大小的緩沖區。
2、fixed 語句設置指向托管變量的指針並在 statement 執行期間“釘住”該變量。如果沒有 fixed 語句,則指向可移動托管變量的指針的作用很小,因為垃圾回收可能不可預知地重定位變量。C# 編譯器只允許在 fixed 語句中分配指向托管變量的指針。
3、執行完語句中的代碼后,任何固定變量都被解除固定並受垃圾回收的制約。因此,不要指向 fixed 語句之外的那些變量。
看到這幾乎話你可能雲里霧里,霧里雲里,
第一句:fixed禁止垃圾回收器定位可移動變量這到底是怎么一回事?
如果你不理解這句話說明你得需要去了解下GC,我們知道GC是CLR管理下的垃圾回收器。當進程初始化時,CLR保留一塊連續的地址空間,這個地址空間最初並沒有對應的物理存儲空間,這個地址空間就是托管堆。在托管堆中,連續分配的對象可以確保他們在內存中時連續的。托管堆維護着一個叫做NextObjPtr的指針,它指向下一個對象在堆中的分配位置。調用new操作符創建對象時,如果沒有足夠的地址空間來分配對象,也即對象的字節數+NextObjPtr指針的地址超過了地址空間末尾則需要進行一次垃圾回收。
回收機制是采用根標記堆上的對象,當根不可達時則回收堆所占的內存(這里不去擴展,只給個大概的脈絡,其實還涉及GC的代Generation),當回收完畢時的下一階段就是壓縮內存,這個階段垃圾回收器線性的便利堆,以尋找未標記(垃圾)對象的連續內存塊,如果發現的內存塊比較小,垃圾回收期會忽略它們,但是,如果發現大的,可用的連續內存塊,垃圾回收器會把非垃圾的對象移動到這里以壓縮堆。
很自然地,移動內存中的對象之后,包含“指向這些對象的指針”的變量和CPU寄存器現在都會變得無效。所以垃圾回收器必須重新訪問應用程序的所有根,並修改它們來指向對象的新內存位置。另外,如果對象中的字段指向的是另一個已移動了位置的對象,垃圾回收器要負責更正這些字段。堆內存壓縮之后,托管堆的NextObjPtr指針指向緊接在最后一個非垃圾對象之后的對象之后的位置。
到這里......哇 看了這么久你肯定累了,可以休息下哈.................................................................................................
上面所說的都是在托管環境的CLR指導下完成的,那如果是非安全代碼如果是指針呢?指針指向了一個托管對象,而GC時內存會壓縮,誰還管你的非安全指針,我們CLR只負責托管代碼啊!所以微軟給出的解決辦法是采用fixed關鍵字。
所以當你看完到這里而且明白的話第1,2個問題都解決啦!!
對於第三個問題,我采用一個實例結合MSDN說明:
結合上面的理論,如果還能輸出結果說明指針操作成功,沒有因為GC的回收造成指針操縱了不正確的地址,因為fixed語句塊釘住了ints變量,禁止垃圾回收器操作內存地址,重定位可移動變量。為什么要這樣做呢?我們不是可以用托管代碼直接操作數組嗎?
沒錯,但你別忘了指針操作數組是很快的,因為它會關閉數組索引的檢查即關閉索引的上下限檢查,這樣的好處是:你的程序是性能優先,以性能為核心的時候這反而是最佳的解決方案!
