C#中值类型和引用类型
C#中值類型和引用類型
http://www.cnblogs.com/123clb/archive/2011/03/03/1969712.html概念:
1.值類型:數據存儲在內存的堆棧中,從堆棧中可以快速地訪問這些數據,因此,值類型表示實際的數據。
2.引用類型:表示指向存儲在內存堆中的數據的指針或引用(包括類、接口、數組和字符串)。
?
C#中定義的值類型包括原類型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚舉(enum)、結構(struct)
引用類型包括:類、數組、接口、委托、字符串等。?
?
區別:
基本區別在于它們在內存中的存儲方式。值類型只將值存放在內存中,這些值類型都存儲在堆棧中。原始數據類型(如bool和int)都屬于此類型。而引用類型的內存單元中只存放內存堆中對象的地址,而對象本身放在內存堆中。如果引用的值類型的值是null,則表示未引用任何對象。
堆和堆棧區別:
堆和堆棧是兩個不同的概念,在內存中的存儲位置也不相同,
堆一般用于存儲可變長度的數據,如字符串類型;
堆棧則用于存儲固定長度的數據,如整型類型的數據int(每個int變量占用四個字節)。由數據存儲的位置可以得知,當把一個值變量賦給另一個值變量時,會在堆棧中保存兩個完全相同的值;而把一個引用變量賦給另一個引用變量,則會在堆棧中保存對同一個堆位置的兩個引用,即在堆棧中保存的是同一個堆的地址。在進行數據操作時,對于值類型,由于每個變量都有自己的值,因此對一個變量的操作不會影響到其它變量;對于引用類型的變量,對一個變量的數據進行操作就是對這個變量在堆中的數據進行操作,如果兩個引用類型的變量引用同一個對象,實際含義就是它們在堆棧中保存的堆的地址相同,因此對一個變量的操作就會影響到引用同一個對象的另一個變量。
?
為了更好地說明兩種類型之間的區別,借用如下的表格來說明。
| ? | 值類型 | 引用類型 |
| 內存分配地點 | 分配在棧中 | 分配在堆中 |
| 效率 | 效率高,不需要地址轉換 | 效率低,需要進行地址轉換 |
| 內存回收 | 使用完后,立即回收 | 使用完后,不是立即回收,等待GC回收 |
| 賦值操作 | 進行復制,創建一個同值新對象 | 只是對原有對象的引用 |
| 函數參數與返回值 | 是對象的復制 | 是原有對象的引用,并不產生新的對象 |
| 類型擴展 | 不易擴展 | 容易擴展,方便與類型擴展 |
注:GC(Garbage Collector,垃圾回收器)是一種自動回收內存的機制,釋放已經不再使用的對象的內存空間。
在.NET平臺中,我們的托管代碼一般都不再關心內存的管理,一切都有CLR(Common language Runtime)去幫我們完成了。當我們開辟內存空間用來創建對象時,使用new關鍵字,這時CLR會分配一塊內存存放對象,大部分時候,我們都不用自己去釋放內存空間,而是由CLR在某個適當的時候幫我們釋放掉?!?/span>
為什么要GC??
1.創建新對象開辟內存空間,在使用完后需要釋放內存,提高性能
2.避免開發人員直接操作內存,提高安全性
GC(回收)過程?
我們運行.NET程序后,OS Loader首先識別出IL(中間語言),然后會加載CLR的核心庫,進行一系列的必要處理后,CLR來到我們編寫的代碼入口處執行。
當我們的在代碼中使用new操作符創建class時,CLR便在叫作GC堆(GC Heap)的內存區域上分配一塊內存存放我們的對象,若對象的Size超過85K字節時,考慮到性能原因,將對象創建在LOH(Large Object Heap)上而不是GC堆上【注1】,若我們在class中定義了析構函數來釋放非托管資源【注2】,則CLR會在一個叫做終結器隊列(Finalizer Queue)的地方添加一個指向該class的項。
我們的程序在運行的過程,在某個時候需要進行垃圾回收了【注3】,首先GC會暫時掛起所有線程,然后確定對象引用的roots【注4】,并根據引用關系創建出由roots出發可以達到的對象形成的對象圖,這些對象暫時還在使用,而那些已創建的卻不在對象圖中的對象則是不可達到的,也就是垃圾了,屬于要回收的對象。隨后將仍然使用的對象移動到存活期更久的區域【注5】,更改區域指針以回收對象,壓縮內存去除內存空隙,并修復對移動的仍存活對象的引用指針,對于有析構函數的對象,則第一次回收時不會回收,而是將其在終結器隊列中移除,并添加到另一個標為準備終止的對象列表中,另一個GC線程會調用此列表指向的對象的Finalize(),回收非托管資源,然后將項從列表中移除,下一次的GC才會真正回收掉該對象。
?
注1:對象創建在Heap上的細節
1): 為了更高效的進行GC,.NET將GC堆分成了3個代,Gen0,Gen1和Gen2。
2): 這3個代只是邏輯上的劃分,在內存中,他們的地址是連續的。
3): Gen0和Gen1之和的大小大約是16M(workstation GC模式下)和64M(server GC模式下)。
4): 新創建對象Size小于85k位于Gen0上,大于85K的則創建在LOH上。
??????注2:定義析構函數釋放非托管資源
Finalize方法是用來釋放對象中使用的非托管資源,他是作為Dispose()方法的一種安全防護措施,即代碼中沒有顯示的調用Dispose()來釋放非托管資源時,GC時調用Finalize方法來釋放,Finalize方法中并不直接釋放非托管資源,而是調用Dispose(false)來釋放。自.NET2.0起,C#中不能直接override Finalize方法,是通過析構函數來實現,析構函數在IL中會被解釋為:
{
????try
????{
????????//執行自定義資源清理操作
????}
????finally
????{
????????base.Finalize();
????}
}
默認情況下,一個類是沒有析構函數的,那么在GC時是不會調用其Finalize()方法的。
注3:GC發生的時機
1)當Gen0的內存使用達到一個閾值時,將引發Gen0的GC,同理Gen1達到時,會Gen0和Gen1同時GC,若Gen2達到時,則會引發Full GC
2)Windows報告內存不足時
3)調用GC.Collect時
4)其他情況:CLR卸載AppDimain,物理內存不足等?
? 注4:確定對象引用根
對象的引用根主要來自于:FInalize Queue,CPU寄存器中的對象指針,全局對象、靜態變量、局部對象、函數調用參數等。
注5: GC時對象的轉移
1)Gen0 GC時,會將Gen0中存活的對象整體移動到Gen1中,然后壓縮Gen1,使Gen1中的內存連續,同理Gen1中移動到Gen2。
?
2)Gen2 GC時,此時發生的GC也稱為Full GC,會回收整個Heap上的對象,Gen2上的對象將不再移動,而是壓縮內存空間。
3)LOH中的對象在Full GC時被回收,但其內存不會被壓縮,而是使用一個空閑列表free list記錄LOH中的空閑空間,對釋放出來的空間進行管理。
4)若對象是pinned object,則此對象不能被移動, 會造成內存碎片。
總結
以上是生活随笔為你收集整理的C#中值类型和引用类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第八回:品味类型---值类型与引用类型(
- 下一篇: 腾讯qq在线状态,开放平台