1. 程式人生 > >通過簡單的程式碼分析.NET物件記憶體分配

通過簡單的程式碼分析.NET物件記憶體分配

System.Windows.Forms.Button btn = new Button();

以中間賦值符號為界限,先看左邊。System.Windows.Forms 是名稱空間,放在一邊。後面是 Button btn,也就是型別名稱,變數名的形式。

其實這跟 int i 沒區別,因為 int i 就是型別名稱,變數名的形式。 i 變數在棧上分配地址空間,因此 btn 變數也是在棧上分配地址空間。

再看賦值符號右邊,new Button(); new 運算子在絕大多數的語言中都是用來在堆上分配記憶體的。堆記憶體有地址空間大,申請和釋放需要呼叫者維護的特點。 new 運算子在堆上分配了一塊用於存放 Button 型別物件的連續地址空間。

同時,new 運算子還會返回指向 Button 物件的首地址。後邊的 () 表示在物件建立之後,呼叫該型別的預設建構函式。 btn = new Button() 的意思就是把在堆上分配的Button 型別物件的首地址儲存在堆的 btn 變數中。

當呼叫 btn.Text = 類似這樣的屬性時,先在堆疊上找到btn 變數所在地址,然後從該地址中取出堆上Button 物件的首地址,然後再去託管堆上找 Button 物件,並修改 Text 屬性。其實 Text 屬性是個String 型別也就是引用型別物件,其實是在堆的另外的地方分配了一塊空間的。

當執行 btn = null 時,表示對上的 Button 物件此時是 unreachable 的情況,因為沒有誰再儲存著 Button 型別的物件的首地址,因此也就無法再使用這個Button 物件了。

此時如果GC 啟動,會將這個物件標記,但並未進行回收。

一旦決定回收這個 Button 物件了,首先會把這個 Button 物件的首地址插入到 FinalizeQueue 佇列中,然後等待 GC 的 Finalize 執行緒呼叫 Button 物件的解構函式。

當 GC 再次啟動時,如果 Button 物件的解構函式已經呼叫完成了,那麼 GC 就將 Button佔用的地址空間回收,並且對記憶體物件進行遷移和整理。

這主要是為了保持記憶體中可用地址空間是連續的。

做這個操作的時候,由於會挪動託管堆上的很多物件,因此會導致其他物件比如 Label.Text 類似這樣的定址失效。

因此 GC 要把當前應用程式執行程式碼的執行緒掛起,停止執行。當記憶體物件遷移完成之後,再恢復執行。

這就是為什麼每次GC啟動特消耗資源的原因,一方面要利用演算法遷移記憶體中的物件,使大片可用地址空間保持連續,另一方面要掛起應用程式執行執行緒,導致應用程式短時間停止響應讓應用程式執行變慢(毫秒或者幾十毫秒級別)。

IDispose 介面設計

IDispose 介面這種設計模式是為了讓一些資源的釋放變成程式設計師可控。上面說到的 GC 自動回收物件,需要GC兩次啟動才能完成一個物件的回收操作。

IDispose 介面中含有一個 Dispose 方法,在該方法中寫入釋放資源的函式,並呼叫 System.GC.SuppressFinalize(this); 方法,告訴 GC 這個物件不必再放入FinalizeQueue 佇列中等待呼叫析構函數了。

這樣可以讓GC 一次完成物件的回收。

以上是針對一般物件,對於塊頭特別巨大的物件比如大於85KB 的物件,有專門的大物件堆LOH進行存放。

但是LOH 上的物件一旦釋放,就不會再做記憶體整理了,因為在記憶體中拷貝這麼大的物件效率太低。

GC 工作模式

GC 有兩種工作模式,伺服器和工作站。

  • 工作站模式下,所有的CPU和記憶體都會看做成是一體的;
  • 伺服器模式下,一個 CPU 一組堆。

目前 .NET 伺服器模式下最多支援32個GC 一起工作。因此,對於特別特別特別牛逼的機器,比如64核的伺服器,其實 .NET 是用不上的。只能是32個核,其他都是擺設。

伺服器模式是為了提高並行度,減少 GC 給應用程式帶來的影響。以至於後來又有了concurrent GC 模式,其實都是為了儘量減少應用程式掛起的時間提高程式執行效率。

但是不管是哪種模式,GC 只要工作就會掛起工作執行緒。因此,程式碼中恰當的物件分配才是提高程式效率的根本。

內容整理自廣州 .NET 微軟技術俱樂部微信群內發言人:李爭

微軟(中國)有限公司全渠道事業部資深技術顧問。

知識共享許可協議

本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。

歡迎轉載、使用、重新發布,但務必保留文章署名 鄭子銘 (包含連結: http://www.cnblogs.com/MingsonZheng/ ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。

如有任何疑問,請與我聯絡 ([email protected]) 。