【转】[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理...
引用自:http://www.cnblogs.com/anytao/category/155694.html
作者:Anytao
?
本文將介紹以下內(nèi)容:
- 類型的基本概念?
- 值類型深入
- 引用類型深入
- 值類型與引用類型的比較及應(yīng)用
?
1.?引言
買了新本本,忙了好幾天系統(tǒng),終于開(kāi)始了對(duì)值類型和引用類型做個(gè)全面的講述了,本系列開(kāi)篇之時(shí)就是因?yàn)橄雽戇@個(gè)主題,才有了寫個(gè)系列的想法。所以對(duì)值類型和引用類型的分析,是我最想成文的一篇,其原因是過(guò)去的學(xué)習(xí)過(guò)程中我就是從這個(gè)主題開(kāi)始,喜歡以IL語(yǔ)言來(lái)分析執(zhí)行,也喜好從底層的過(guò)程來(lái)深入了解。這對(duì)我來(lái)說(shuō),似乎是一件找到了有效提高的方法,所以想寫的沖動(dòng)就沒(méi)有停過(guò),旨在以有效的方式來(lái)分享所得。同時(shí),我也認(rèn)為,對(duì)值類型和引用類型的把握, 是理解語(yǔ)言基礎(chǔ)環(huán)節(jié)的關(guān)鍵主題,有必要花力氣來(lái)了解和深入。??
2. 一切從內(nèi)存開(kāi)始
2.1 基本概念
從上回《第七回:品味類型---從通用類型系統(tǒng)開(kāi)始》我們知道,CLR支持兩種基本類型:值類型和引用類型。因此,還是把MSDN這張經(jīng)典視圖拿出來(lái)做個(gè)鋪墊。
?
值類型(Value Type),值類型實(shí)例通常分配在線程的堆棧(stack)上,并且不包含任何指向?qū)嵗龜?shù)據(jù)的指針,因?yàn)樽兞勘旧砭桶似鋵?shí)例數(shù)據(jù)。其在MSDN的定義 為值類型直接包含它們的數(shù)據(jù),值類型的實(shí)例要么在堆棧上,要么內(nèi)聯(lián)在結(jié)構(gòu)中。我們由上圖可知,值類型主要包括簡(jiǎn)單類型、結(jié)構(gòu)體類型和枚舉類型等。通常聲明為以下類型:int、char、float、long、bool、double、struct、enum、short、byte、decimal、 sbyte、uint、ulong、ushort等時(shí),該變量即為值類型。??
引用類型(Reference Type),引用類型實(shí)例分配在托管堆(managed heap)上,變量保存了實(shí)例數(shù)據(jù)的內(nèi)存引用。其在MSDN中的定義為引用類型存儲(chǔ)對(duì)值的內(nèi)存地址的引用,位于堆上。我們由上圖可知,引用類型可以是自描 述類型、指針類型或接口類型。而自描述類型進(jìn)一步細(xì)分成數(shù)組和類類型。類類型是則可以是用戶定義的類、裝箱的值類型和委托。通常聲明為以下類型:class、interface、delegate、object、string以及其他的自定義引用類型時(shí),該變量即為引用類型。
下面簡(jiǎn)單的列出我們類型的進(jìn)一步細(xì)分,數(shù)據(jù)來(lái)自MSDN,為的是給我們的概念中有清晰的類型概念,這是最基礎(chǔ)也是最必須的內(nèi)容。
??
2.2 內(nèi)存深入
2.2.1. 內(nèi)存機(jī)制
那么.NET的內(nèi)存分配機(jī)制如何呢?
數(shù)據(jù)在內(nèi)存中的分配位置,取決于該變量的數(shù)據(jù)類型。由上可知,值類型通常分配在線程的堆棧上,而引用類型通常分配在托管堆上,由GC來(lái)控制其回收。例如,現(xiàn)在有MyStruct和MyClass分別代表一個(gè)結(jié)構(gòu)體和一個(gè)類,如下:
using?System;
public?class?Test
{
????static?void?Main()
????{
????????//定義值類型和引用類型,并完成初始化
????????MyStruct?myStruct?=?new?MyStruct();
????????MyClass?myClass?=?new?MyClass();
????????
????????//定義另一個(gè)值類型和引用類型,
????????//以便了解其內(nèi)存區(qū)別
????????MyStruct?myStruct2?=?new?MyStruct();
????????myStruct2?=?myStruct;
????????
????????MyClass?myClass2?=?new?MyClass();
????????myClass2?=?myClass;????????
????}
}
在上述的過(guò)程中,我們分別定義了值類型變量myStruct和引用類型變量myClass,并使用new操作符完成內(nèi)存分配和初始化操作,此處new的區(qū)別可以詳見(jiàn)《第五回:深入淺出關(guān)鍵字---把new說(shuō)透》? 的論述,在此不做進(jìn)一步描述。而我們?cè)诖藦?qiáng)調(diào)的是myStruct和myClass兩個(gè)變量在內(nèi)存分配方面的區(qū)別,還是以一個(gè)簡(jiǎn)明的圖來(lái)展示一下:
?
我們知道,每個(gè)變量或者程序都有其堆棧,不同的變量不能共有同一個(gè)堆棧地址,因此myStruct和myStruct2 在堆棧中一定占用了不同的堆棧地址,盡管經(jīng)過(guò)了變量的傳遞,實(shí)際的內(nèi)存還是分配在不同的地址上,如果我們?cè)賹?duì)myStruct2變量改變時(shí),顯然不會(huì)影響 到myStruct的數(shù)據(jù)。從圖中我們還可以顯而易見(jiàn)的看出,myStruct在堆棧中包含其實(shí)例數(shù)據(jù),而myClass在堆棧中只是保存了其實(shí)例數(shù)據(jù)的引用地址,實(shí)際的數(shù)據(jù)保存在托管堆中。因此,就有可能不同的變量保存了同一地址的數(shù)據(jù)引用,當(dāng)數(shù)據(jù)從一個(gè)引用類型變量傳遞到另一個(gè)相同類型的引用類型變量 時(shí),傳遞的是其引用地址而不是實(shí)際的數(shù)據(jù),因此一個(gè)變量的改變會(huì)影響另一個(gè)變量的值。從上面的分析就可以明白的知道這樣一個(gè)簡(jiǎn)單的道理:值類型和引用類型在內(nèi)存中的分配區(qū)別是決定其應(yīng)用不同的根本原因,由此我們就可以很容易的解釋為什么參數(shù)傳遞時(shí),按值傳遞不會(huì)改變形參值,而按址傳遞會(huì)改變行參的值,道理 正在于此。?
對(duì)于內(nèi)存分配的更詳細(xì)位置,可以描述如下:
- 值類型變量做為局部變量時(shí),該實(shí)例將被創(chuàng)建在堆棧上;而如果值類型變量作為類型的成員變量時(shí),它將作為類型實(shí)例數(shù)據(jù)的一部分,同該類型的其他字段都保存在托管堆上,這點(diǎn)我們將在接下來(lái)的嵌套結(jié)構(gòu)部分來(lái)詳細(xì)說(shuō)明。
- 引用類型變量數(shù)據(jù)保存在托管堆上,但是根據(jù)實(shí)例的大小有所區(qū)別,如下:如果實(shí)例的大小小于85000Byte時(shí),則該實(shí)例將創(chuàng)建在GC堆上;而當(dāng)實(shí)例大小大于等于85000byte時(shí),則該實(shí)例創(chuàng)建在LOH(Large Object Heap)堆上。
更詳細(xì)的分析,我推薦《類型實(shí)例的創(chuàng)建位置、托管對(duì)象在托管堆上的結(jié)構(gòu)》。
2.2.2. 嵌套結(jié)構(gòu)?
嵌套結(jié)構(gòu)就是在值類型中嵌套定義了引用類型,或者在引用類型變量中嵌套定義了值類型,相信園子中關(guān)于這一話題的論述和關(guān)注都不是很多。因此我們很有必要發(fā)揮一下,在此就順藤摸瓜,從上文對(duì).NET的內(nèi)存機(jī)制著手來(lái)理解會(huì)水到渠成。
- 引用類型嵌套值類型
值類型如果嵌套在引用類型時(shí),也就是值類型在內(nèi)聯(lián)的結(jié)構(gòu)中時(shí),其內(nèi)存分配是什么樣子呢?其實(shí)很簡(jiǎn)單,例如類的私有字段如果為值類型,那它作為引用類型實(shí)例的一部分,也分配在托管堆上。例如:
public?class?NestedValueinRef
{?
??//aInt做為引用類型的一部分將分配在托管堆上?
??private?int?aInt;??
??public?NestedValueinRef?
??{?
????//aChar則分配在該段代碼的線程棧上?
?????char?achar?=?'a';?
??}?
}?
其內(nèi)存分配圖可以表示為:
??
- ?值類型嵌套引用類型
引用類型嵌套在值類型時(shí),內(nèi)存的分配情況為:該引用類型將作為值類型的成員變量,堆棧上將保存該成員的引用,而成員的實(shí)際數(shù)據(jù)還是保存在托管堆中。例如:
public?struct?NestedRefinValue
{
????public?MyClass?myClass;
????public?NestedRefinValue
????{
????????myClass.X?=?1;
????????myClass.Y?=?2;
????}
}
其內(nèi)存分配圖可以表示為:
?
2.2.3. 一個(gè)簡(jiǎn)單的討論
通過(guò)上面的分析,如果我們現(xiàn)在有如下的執(zhí)行時(shí):
AType[] myType = new AType[10];
試問(wèn):如果AType是值類型,則分配了多少內(nèi)存;而如果AType是引用類型時(shí),又分配了多少內(nèi)存?
我們的分析如下:根據(jù)CRL的內(nèi)存機(jī)制,我們知道如果ATpye為Int32類型,則表示其元素是值類型,而數(shù)組本身為引用類型,myType將保存指向托管堆中的一塊大小為4×10byte的內(nèi)存地址,并且將所有的元素賦值為0;而如果AType為自定義的引用類型,則會(huì)只做一次內(nèi)存分配,在線程 的堆棧創(chuàng)建了一個(gè)指向托管堆的引用,而所有的元素被設(shè)置為null值,表示為空。?轉(zhuǎn)載于:https://www.cnblogs.com/czh-liyu/archive/2008/10/15/1311635.html
總結(jié)
以上是生活随笔為你收集整理的【转】[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 本周计划(4月12日-19日)
- 下一篇: 重命名数据库解决“无法用排他锁锁定该数据