久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

堆棧和委托堆的區(qū)別(c#)一

 阿修羅之獅猿授 2016-08-28

 文章轉(zhuǎn)自:http://www.cnblogs.com/shenfengok/archive/2011/09/06/2169306.html

   首先堆棧和堆(托管堆)都在進(jìn)程的虛擬內(nèi)存中,。(在32位處理器上每個(gè)進(jìn)程的虛擬內(nèi)存為4GB)

堆棧stack

1,、堆棧中存儲(chǔ)值類型

2、堆棧實(shí)際上是向下填充,,即由高內(nèi)存地址指向低內(nèi)存地址填充

3,、堆棧的工作方式是先分配內(nèi)存的變量后釋放(先進(jìn)后出原則)

4,、堆棧中的變量是從下向上釋放,這樣就保證了堆棧中先進(jìn)后出的規(guī)則不與變量的生命周期起沖突

5,、堆棧的性能非常高,,但是對(duì)于所有的變量來(lái)說(shuō)還不靈活,而且變量的生命周期必須嵌套,。

6,、通常我們希望使用一種方法分配內(nèi)存來(lái)存儲(chǔ)數(shù)據(jù),并且方法退出后很長(zhǎng)一段時(shí)間內(nèi)數(shù)據(jù)仍然可以使用,。此時(shí)我們就用到了堆(托管堆)

 

堆(托管堆)heap

堆(托管堆)存儲(chǔ)引用類型

此堆非彼堆,,.net中的堆有垃圾收集器自動(dòng)管理

與堆棧不同,堆是從下往上分配,,所以自由的空間都已用空間的上面

比如創(chuàng)建一個(gè)對(duì)象:

customer cus;

cus=new customer();

申明一個(gè)customer的引用cus,在堆棧上給這個(gè)引用分配存儲(chǔ)空間,。這僅僅只是一個(gè)引用,不是實(shí)際的customer對(duì)象,!

cus占4個(gè)字節(jié)的空間,,包含了存儲(chǔ)customer的引用地址。

接著分配堆上的內(nèi)存以存儲(chǔ)customer對(duì)象的實(shí)例,,假定customer對(duì)象的實(shí)例是32字節(jié),,為了在堆上找到一個(gè)存儲(chǔ)customer對(duì)象的存儲(chǔ)位置。

.net運(yùn)行庫(kù)在堆中搜索第一個(gè)從未使用的,,32字節(jié)的連續(xù)塊存儲(chǔ)customer對(duì)象的實(shí)例,!

然后把分配給customer對(duì)象實(shí)例的地址給cus變量!

從這個(gè)例子中可以看出,,建立對(duì)象引用的過程比建立值變量的過程復(fù)雜,,且不能避免性能的降低!

實(shí)際上就是.net運(yùn)行庫(kù)保存對(duì)狀態(tài)信息,,在堆中添加新數(shù)據(jù)時(shí),,堆棧中引用變量也要更新。性能上損失很多,!

有種機(jī)制在分配變量?jī)?nèi)存的時(shí)候,,不會(huì)受到堆棧的限制:把一個(gè)引用變量的值賦給一個(gè)相同類型的變量,那么這兩個(gè)變量就引用一個(gè)堆中的對(duì)象,。

當(dāng)一個(gè)應(yīng)用變量出作用于域時(shí),,它會(huì)從堆棧中刪除,但引用對(duì)象的數(shù)據(jù)仍熱保留在堆中,,一直到程序結(jié)束或者該數(shù)據(jù)不被任何變量應(yīng)用時(shí),,垃圾收集器會(huì)刪除它。

——————————————————————————————————————————

裝箱轉(zhuǎn)換

using System;

class Boxing

{

public  static  void Main()

{

  int i=0;

  object obj=i;

   i=220;

    Console.WriteLine("i={0},obj={1}",i,obj);

    obj=330;

    Console.WriteLine("i={0},obj={1}",i,obj);

}

}

定義整形類型變量i的時(shí)候,這個(gè)變量占用的內(nèi)存是內(nèi)存棧中分配的,,第二句是裝箱操作將變量110存放到了內(nèi)存堆中,,而定義object對(duì)象類型的變量obj則在內(nèi)存棧中,并指向int類型的數(shù)值為110,,而該數(shù)值是付給變量i的數(shù)值副本,。

所以運(yùn)行結(jié)果是:

i=220,obj=110

i=220,obj=330

 內(nèi)存格局通常分為四個(gè)區(qū)

    1、全局?jǐn)?shù)據(jù)區(qū):存放全局變量,,靜態(tài)數(shù)據(jù),,常量

    2、代碼區(qū):存放所有的程序代碼

    3,、棧區(qū):存放為運(yùn)行而分配的局部變量,參數(shù),、返回?cái)?shù)據(jù),,返回地址等,,

    4,、堆區(qū):即自由存儲(chǔ)區(qū)

值類型區(qū)分兩種不同的內(nèi)存區(qū)域:線程堆棧(Thread Stack)和托管堆(Manged Heap)。

每個(gè)正在運(yùn)行的程序都對(duì)應(yīng)著一個(gè)進(jìn)程(process),,在一個(gè)進(jìn)程內(nèi)部,,可以有一個(gè)或者多個(gè)線程(thread),

每個(gè)線程都擁有一塊“自留地”,稱為“線程堆?!?,大小為1M,用于保存自身的一些數(shù)據(jù),比如函數(shù)中自定義的局部變量,、函數(shù)調(diào)用時(shí)傳送的參數(shù)值等,,這部分內(nèi)存域與回收不需要程序員干涉。

所有值類型的變量都是在線程堆棧中分配的,。

另一塊內(nèi)存區(qū)域稱為“堆(heap)”,在.Net這種托管環(huán)境下,,堆由CLR進(jìn)行管理,,所以又稱為“托管堆(manged heap)”,。

用new 關(guān)鍵字創(chuàng)建的類的對(duì)象時(shí),分配給對(duì)象的內(nèi)存單元就位于托管堆中,。

在程序中我們可以隨意的使用new關(guān)鍵字創(chuàng)建多個(gè)對(duì)象,,因此,托管堆中的內(nèi)存資源是可以動(dòng)態(tài)申請(qǐng)并使用的,,當(dāng)然用完了必須歸還,。

打個(gè)比方更容易理解:托管堆相當(dāng)于一個(gè)旅館,其中房間相當(dāng)于托管堆中所擁有的內(nèi)存單元。當(dāng)程序員用new方法去創(chuàng)建對(duì)象時(shí),,相當(dāng)于游客向旅館預(yù)訂房間,,旅館管理員會(huì)先看一下有沒有合適的房間,有的話,,就可以將此房間提供給游客住宿,要辦理退房手續(xù),,房間又可以為其他游客提供服務(wù)了,。

引用類型共有四種:類類型,、接口類型、數(shù)組類型和委托類型,。

所有引用類型變量所引用的對(duì)象,,其內(nèi)存都是在托管堆中分配的,。

嚴(yán)格地說(shuō),我們常說(shuō)的“對(duì)象變量”其實(shí)是類類型的引用變量,。但在實(shí)際人們經(jīng)常將引用類型的變量簡(jiǎn)稱為“對(duì)象變量”,,用它來(lái)指代所有四種類型的引用變量。在不致于引起混淆的情況下,,我們也這么認(rèn)為

在理解了對(duì)象內(nèi)存模型之后,,對(duì)象變量之間的相互賦值的含義也就清楚了。請(qǐng)看一下代碼:

class A

{

   public int i;

}

class Program

{

   A a;

   a=new A();

   a.i=100;

  A b=null;

  b=a; //對(duì)象變量的相互賦值

Console.WriteLine("b.i="+b.i); //b.i=?

}

注意第12和13句,。

程序的運(yùn)行結(jié)果是:

b.i=100;

事實(shí)上,兩個(gè)對(duì)象變量的相互賦值意味著賦值后兩個(gè)對(duì)象變量所占有的內(nèi)存單元其內(nèi)容是相同的,。

詳細(xì)一些:

當(dāng)創(chuàng)建對(duì)象以后,,其首地址(假設(shè)為“1234 5678”)被放入到變量a自身的4個(gè)字節(jié)的內(nèi)存單元中。

有定義了一個(gè)對(duì)象變量b,其值最初為null(即對(duì)應(yīng)的4個(gè)字節(jié)內(nèi)存單元中為“0000 0000”)

a變量的值被復(fù)制到b的內(nèi)存單元中,,現(xiàn)在,,b內(nèi)存單元的值也為“1234 5678”

根據(jù)上面介紹的對(duì)象內(nèi)存模型,我們知道現(xiàn)在變量a和b都指向同一個(gè)實(shí)例對(duì)象,。

如果通過b.i修改字段i的值,a.i也會(huì)同步變化,,因?yàn)閍.i與b.i其實(shí)代表同一對(duì)象的統(tǒng)一字段,。

如上圖

由此得到一個(gè)重要結(jié)論:

對(duì)象變量的互相賦值不會(huì)導(dǎo)致對(duì)象自身被復(fù)制,,其結(jié)果是兩個(gè)對(duì)象變量指向同一個(gè)對(duì)象。

另外,,由于對(duì)象變量本身是一個(gè)局部變量,,因此,對(duì)象本身是位于線程堆棧中的,。

嚴(yán)格區(qū)分對(duì)象變量于對(duì)象變量所引用的對(duì)象,,是面向?qū)ο缶幊痰年P(guān)鍵技術(shù)之一。

由于對(duì)象變量類似于一個(gè)對(duì)象指針,,這就產(chǎn)生了“判斷兩個(gè)對(duì)象變量是否引用用一個(gè)對(duì)象”的問題

c#使用“==”運(yùn)算符對(duì)比兩個(gè)對(duì)象變量是否引用同一個(gè)對(duì)象,,“!=”比對(duì)兩個(gè)對(duì)象變量是否引用不同的對(duì)象,。

A a1=new A();

A a2=new A();

Console.WriteLine(a1==a2);//輸出:false

a2=a1;//a1和a2引用相同的對(duì)象

Console.WriteLine(a1==a2);//輸出 true

需要注意的是,,如果“==”被用到值類型的變量之間,則比對(duì)的變量的內(nèi)容:

int i=0;

int j=100;

if(i==j)

{

   Console.WriteLine("i與j的值相等");

}

理解值類型與引用類型的區(qū)別在面向?qū)ο缶幊讨蟹浅jP(guān)鍵,。

1,、類型,對(duì)象,,堆棧和托管堆

c#的類型和對(duì)象在應(yīng)用計(jì)算機(jī)內(nèi)存時(shí),大體用到了兩種內(nèi)存,,一個(gè)叫堆棧,另一個(gè)叫托管堆,,下面我們用直接長(zhǎng)方形表示堆棧,,用圓角長(zhǎng)方形來(lái)代表托管堆。

 先舉個(gè)例子,,有如下兩個(gè)方法,Method_1和Add,分別如下:

public  void Method_1()

{

   int value1=10; //1

   int value2=20;//2

   int value3=Add(value,value);//3

}

public int Add(int n1,int n2) //4

{

  int sum=n1+n2;//5

  return sum;//6

}

這段代碼執(zhí)行,,用圖表示為為:

上圖基本對(duì)應(yīng)程序中的每個(gè)步驟。在執(zhí)行Method_1的時(shí)候,,先把value1壓入堆棧頂部,然后是value2,接著下面的調(diào)用方法Add,,因?yàn)榉椒ㄓ袃蓚€(gè)參數(shù)是n1和n2,所以把n1和n2分別壓入堆棧,,因?yàn)榇颂幷{(diào)用了一個(gè)方法,,并且方法有返回值,所以這里需要保存Add的返回地址,然后進(jìn)入Add方法內(nèi)部,,在Add內(nèi)部,,首先給sum賦值,所以把sum壓入棧頂,,然后用return返回,此時(shí),,返回的返回地址就起到了作用,,return會(huì)根據(jù)地址返回回去,,在返回的過程中,把sum推出棧頂,,找到了返回地址,但在Method_1方法中,,我們希望把Add的返回值賦給Value3,此時(shí)的返回地址也被推出堆棧,,把value2壓入堆棧,。

雖然這個(gè)例子的結(jié)果沒大用途,,但這個(gè)例子很好的說(shuō)明方法被執(zhí)行時(shí),變量在進(jìn)出堆棧的情況,。這里也能看出為什么方法內(nèi)部局部變量用過之后,,不能在其他方法中訪問的原因。

下面我們討論一下類和對(duì)象在托管堆和堆棧中的情況,。

先看一下代碼:

class Car

{

  public void Run()

   {

     Console.WriteLine("一切正常");

   }

   public  virtual  double GetPrice()

  {

     return 0;

  }

  public static void Purpose()

  {

    console.WtriteLine("載入");

  }

}

class BMW:Car

{

  public override double GetPrice()

  {

    return  80000;

  }

}

上面是兩個(gè)類,,一個(gè)Father一個(gè)son,son繼承了Father,因?yàn)槟泐愔杏幸粋€(gè)virtual的BuyHouse方法,所以son類可以重寫這個(gè)方法,。

下面接著看調(diào)用代碼,。

public void Method_A()

{

  double  CarPrice;//1

  Car  car=new BMW();//2

  CarPrice=car.GetPrice();//調(diào)用虛方法(其實(shí)調(diào)用的是重寫后的方法)

  car.Run();//調(diào)用實(shí)例化方法

  car.Purpose();//調(diào)用靜態(tài)方法

}

這個(gè)方法也比較簡(jiǎn)單,就是定義一個(gè)變量用來(lái)獲得價(jià)格,,同時(shí)定義了一個(gè)父類的變量,,用子類來(lái)實(shí)例化它

接下來(lái),我們來(lái)分步驟說(shuō)明:

看一下運(yùn)行時(shí)堆棧和托管堆的情況:

這里需要說(shuō)明的是,,類是位于托管堆中的,,這個(gè)類有分為四個(gè)類部,用來(lái)關(guān)聯(lián)對(duì)象,;同步索引,,引用完成同步(比如線程同步)需要建立的,,靜態(tài)成員是屬于類的,所以在類中出現(xiàn),,還有一個(gè)方法列表(這里的方法列表項(xiàng)與具體的方法對(duì)應(yīng)),。

當(dāng)Mehod_A方法的第一執(zhí)行時(shí):

這時(shí)的CarPrice是沒有值得

當(dāng)Method_A方法執(zhí)行到第二步,其實(shí)第二步又可以分成

Car  car;

car=new BWM();

先看Car

car在這里是一個(gè)方法內(nèi)部的變量,,所以被壓力到堆棧中,。

在看car =new BWM();

這是一個(gè)實(shí)例化過程,car變成了一個(gè)對(duì)象

這里是用子類來(lái)實(shí)例化父類類型,。對(duì)象其實(shí)是子類的類型的,但變量的類型的父類的,。

接下來(lái),,在Method_A中的調(diào)用的中調(diào)用car.GetPrice(),對(duì)于car來(lái)說(shuō),,這個(gè)方法是虛方法(并且子類重寫了它),虛方法在調(diào)用是不會(huì)執(zhí)行類型上的方法,,即不會(huì)執(zhí)行car類中的虛方法,而是執(zhí)行對(duì)象對(duì)應(yīng)類的上的方法,,即BWM中的GtPrice.如果Method_A中執(zhí)行方法Run()因?yàn)镽un是普通實(shí)例方法,,所以執(zhí)行car類中的Run方法,。

如果調(diào)用了Method_A的Prupose方法,即不用變量car調(diào)用,,也不用對(duì)象調(diào)用,而使類名Car調(diào)用,,因?yàn)殪o態(tài)方法會(huì)在類中分配內(nèi)存中。如果用Car生成多個(gè)實(shí)例,,靜態(tài)成員只有一份,,就是在類中,,而不是在對(duì)象中。

-------------------------------------------------------------------------------------------------------

在32位的window操作系統(tǒng)中,,每個(gè)進(jìn)程都可以用4GB的內(nèi)存,,這得益于虛擬尋址技術(shù),,在這4GB的內(nèi)存中存儲(chǔ)這可執(zhí)行代碼,、代碼加載的DLL和程序運(yùn)行的所有變量,,在c#中,,虛擬內(nèi)存中有個(gè)兩個(gè)存儲(chǔ)變量區(qū)域,,一個(gè)稱為堆棧,一個(gè)稱為托管堆,,托管堆的出現(xiàn)是.net不同于其他語(yǔ)言的地方,堆棧存儲(chǔ)值類型數(shù)據(jù),,而托管堆棧存儲(chǔ)引用類型如類,、對(duì)象,,并受垃圾回收集器的控制和管理。在堆棧中,,一旦變量超出使用范圍,,其使用的內(nèi)存空間會(huì)被其他變量重新使用,這時(shí)其空間存儲(chǔ)的值被其他變量覆蓋而不復(fù)存在,,但有時(shí)候我們希望有些值仍然存在,,這需要托管堆來(lái)實(shí)現(xiàn),我們用幾段代碼來(lái)說(shuō)明其工作原理,,假設(shè)已經(jīng)定義了一個(gè)類class:

class1 object1;

object1=new class1();

第一句定義了一個(gè)class1的引用,,實(shí)質(zhì)上只是在堆棧中分配一個(gè)4個(gè)字節(jié)的空間,,它將用來(lái)存儲(chǔ)后來(lái)實(shí)例化對(duì)象在托管堆中的地址,在windows中這需要4個(gè)字節(jié)來(lái)表示內(nèi)存地址,。第二句實(shí)例化object1對(duì)象,實(shí)際上是在托管堆中開辟了一個(gè)內(nèi)存空間來(lái)存儲(chǔ)類class1的一個(gè)具體對(duì)象,,假設(shè)這個(gè)對(duì)象需要36個(gè)字節(jié),那么object1指向的實(shí)際上是在托管推一個(gè)大小為36字節(jié)的連續(xù)內(nèi)存空間開始地址,,當(dāng)對(duì)象不再使用時(shí),,這個(gè)被存儲(chǔ)在堆棧中引用變量將被刪除,但是從上述機(jī)制中可以看出,,在托管堆中這個(gè)引用指向的對(duì)象仍然存在,,其空間何時(shí)被釋放取決垃圾收集器而不是引用變量失去作用域時(shí)。

在使用電腦的過程中大家可以都有過這種經(jīng)驗(yàn),,電腦用久了以后程序會(huì)變得越來(lái)越慢,,其中一個(gè)重要的原因就就系統(tǒng)存在中大量?jī)?nèi)存碎片,就是因?yàn)槌绦蚍磸?fù)在堆棧中創(chuàng)建和釋放變量,,久而久之可用變量在內(nèi)存中將不再是連續(xù)的內(nèi)存空間,,為了尋址這些變量也會(huì)增加系統(tǒng)開銷。在.net中這種類型將得到很大改善,,這是因?yàn)橛辛死厥占鞯墓ぷ?,垃圾收集器將?huì)壓縮托管堆的內(nèi)存空間,保證可用變量在一個(gè)連續(xù)的內(nèi)存空間中,,同時(shí)將堆棧中引用變量中的地址改為新的地址,,這將會(huì)帶來(lái)額外的系統(tǒng)開銷,,但是,其帶來(lái)的好處會(huì)抵消這種影響,,而另外的一個(gè)好處是,程序員將不再花上大量的心思在內(nèi)存泄露問題上,。

當(dāng)然,以c#程序中不僅僅只有引用類型的變量,,仍然存在值類型和其他托管堆不能管理的對(duì)象,如果文件名柄,、網(wǎng)絡(luò)連接和數(shù)據(jù)庫(kù)連接,,這些變量的釋放仍然需要程序員通過析構(gòu)函數(shù)或者IDispose接口來(lái)做。

另一方面,,在某些時(shí)候c#程序需要追求速度,,比如對(duì)一個(gè)含有大量成員的數(shù)組的操作,如仍使用傳統(tǒng)的類來(lái)操作,,將不會(huì)得到很好的性能,,因?yàn)閿?shù)組在c#中實(shí)際是System.Array的實(shí)例,會(huì)存儲(chǔ)在托管堆中,這將會(huì)對(duì)運(yùn)算造成大量的額外的操作,,因?yàn)槌死占鞒藭?huì)壓縮托管堆,、更新引用地址,還會(huì)維護(hù)托管堆的信息列表,。所幸的是c#中同樣能夠通過不安全代碼使用c++程序員通常喜歡的方式來(lái)編碼,,在標(biāo)記為unsafe的代碼塊使用指針,這和在c++中使用指針沒有什么不同,,變量也是存在堆棧中,,在這種情況下聲明一個(gè)數(shù)組可以使用stacklloc語(yǔ)法,,比如聲明一個(gè)存儲(chǔ)有50個(gè)double類型的數(shù)組:

double*  pDouble=stackalloc double[50]

stackalloc會(huì)給pDouble數(shù)組在堆棧中分配50個(gè)double類型大小的內(nèi)存空間,,可以使用pDouble[0]、*(pDouble+1)這種方式操作數(shù)組,,與在c++中一樣,使用指針必須知道自己在做什么,,確保訪問的正確的內(nèi)存空間,否則會(huì)出現(xiàn)無(wú)法預(yù)料的錯(cuò)誤,。

進(jìn)程中每個(gè)線程都有自己的堆棧,這是一段線程創(chuàng)建時(shí)保留下的地址區(qū)域,。我們的“棧內(nèi)存”即在此,。至于“堆”內(nèi)存,我個(gè)人認(rèn)為在未使用new定義時(shí),,堆應(yīng)該就是未“保留”為“提交”的自由空間,,new的功能是在這些自由空間中保留出一個(gè)地址范圍

棧(stack)是操作系統(tǒng)在建立某個(gè)進(jìn)程時(shí)或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個(gè)線程的存儲(chǔ)區(qū)域,該區(qū)域具有FIFO的特性,,在編譯的時(shí)候可以指定需要的Stack的大小,。在編程中,例如c/c++中,,所有的局部變量都是從棧中分配內(nèi)存空間,,實(shí)際上也不是什么都分配,只是從棧頂向上用就行,,在推出函數(shù)的時(shí)候,,只修改棧指針就可以把棧中的內(nèi)容銷毀,,所以速度最快,。

堆(Heap)是應(yīng)用程序在運(yùn)行的時(shí)候請(qǐng)求操作系統(tǒng)給自己內(nèi)存,,一般是申請(qǐng)/給予的過程,,c/c++分別用malloc/New請(qǐng)求分配Heap,用free/delete銷毀內(nèi)存,。由于從操作系統(tǒng)管理的內(nèi)存分配所以在分配和銷毀時(shí)都要占用時(shí)間,,所以堆的效率要低的多!,,但是堆的好處是可以做的很大,,c/c++堆分配的Heap是不初始化的。

在Java中除了簡(jiǎn)單類型(int,char等)都是在堆中分配內(nèi)存,,這也是程序慢的一個(gè)主要原因,。但是跟C/C++不同,Java中分配Heap內(nèi)存是自動(dòng)初始化的,。在Java中所有的對(duì)象(包括int的wrapper   Integer)都是在堆中分配的,,但是這個(gè)對(duì)象的引用卻是在Stack中分配,。也就是說(shuō)在建立一個(gè)對(duì)象時(shí)從兩個(gè)地方都分配內(nèi)存,在Heap中分配的內(nèi)存實(shí)際建立這個(gè)對(duì)象,,而在Stack中分配的內(nèi)存只是一個(gè)指向這個(gè)堆對(duì)象的指針(引用)而已,。

在.NET的所有技術(shù)中,最具爭(zhēng)議的恐怕是垃圾收集(Garbage Collection,,GC)了,。作為.NET框架中一個(gè)重要的部分,托管堆和垃圾收集機(jī)制對(duì)我們中的大部分人來(lái)說(shuō)是陌生的概念,。在這篇文章中將要討論托管堆,,和你將從中得到怎樣的好處。

 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多