程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> C#的內存管理:堆棧、托管堆與指針

C#的內存管理:堆棧、托管堆與指針

編輯:.NET實例教程

在32位的Windows操作系統中,每個進程都可以使用4GB的內存,這得益於虛擬尋址技術,在這4GB的內存中存儲著可執行代碼、代碼加載的DLL和程序運行的所有變量,在C#中,虛擬內存中有個兩個存儲變量的區域,一個稱為堆棧,一個稱為托管堆,托管堆的出現是.Net不同於其他語言的地方,堆棧存儲值類型數據,而托管堆存儲引用類型如類、對象,並受垃圾收集器的控制和管理。在堆棧中,一旦變量超出使用范圍,其使用的內存空間會被其他變量重新使用,這時其空間中存儲的值將被其他變量覆蓋而不復存在,但有時候我們希望這些值仍然存在,這就需要托管堆來實現。我們用幾段代碼來說明其工作原理,假設已經定義了一個類class1:

class1 object1;

object1=new class1();

第一句定義了一個class1的引用,實質上只是在堆棧中分配了一個4個字節的空間,它將用來存府後來實例化對象在托管堆中的地址,在Windows中這需要4個字節來表示內存地址。第二句實例化object1對象,實際上是在托管堆中開僻了一個內存空間來存儲類class1的一個具體對象,假設這個對象需要36個字節,那麼object1指向的實際上是在托管堆一個大小為36個字節的連續內存空間開始的地址。由此也可以看出在C#編譯器中為什麼不允許使用未實例化的對象,因為這個對象在托管堆中還不存在。當對象不再使用時,這個被存儲在堆棧中的引用變量將被刪除,但是從上述機制可以看出,在托管堆中這個引用指向的對象仍然存在,其空間何時被釋放取決垃圾收集器而不是引用變量失去作用域時。

在使用電腦的過程中大家可能都有過這種經驗:電腦用久了以後程序運行會變得越來越慢,其中一個重要原因就是系統中存在大量內存碎片,就是因為程序反復在堆棧中創建和釋入變量,久而久之可用變量在內存中將不再是連續的內存空間,為了尋址這些變量也會增加系統開銷。在.Net中這種情形將得到很大改善,這是因為有了垃圾收集器的工作,垃圾收集器將會壓縮托管堆的內存空間,保證可用變量在一個連續的內存空間內,同時將堆棧中引用變量中的地址改為新的地址,這將會帶來額外的系統開銷,但是,其帶來的好處將會抵消這種影響,而另外一個好處是,程序員將不再花上大量的心思在內在洩露問題上。

當然,以C#程序中不僅僅只有引用類型的變量,仍然也存在值類型和其他托管堆不能管理的對象,如果文件名柄、網絡連接和數據庫連接,這些變量的釋放仍需要程序員通過析構函數或IDispose接口來做。

另一方面,在某些時候C#程序也需要追求速度,比如對一個含用大量成員的數組的操作,如果仍使用傳統的類來操作,將不會得到很好的性能,因為數組在C#中實際是System.Array的實例,會存儲在托管堆中,這將會對運算造成大量的額外的操作,因為除了垃圾收集器除了會壓縮托管堆、更新引用地址、還會維護托管堆的信息列表。所幸的是C#中同樣能夠通過不安全代碼使用C++程序員通常喜歡的方式來編碼,在標記為unsafe的代碼塊使用指針,這和在C++中使用指針沒有什麼不同,變量也是存府在堆棧中,在這種情況下聲明一個數組可以使用stackalloc語法,比如聲明一個存儲有50個double類型的數組:

double* pDouble=stackalloc double[50]

stackalloc會給pDouble數組在堆棧中分配50個double類型大小的內存空間,可以使用pDouble[0]、*(pDouble+1)這種方式操作數組,與在C++中一樣,使用指針時必須知道自己在做什麼,確保訪問的正確的內存空間,否則將會出現無法預料的錯誤。

掌握托管堆、堆棧、垃圾收集器和不安全代碼的工作原理和方式,將有助於你成為真正的優秀C#程序員。


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved