欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
C#的內存管理:堆棧、托管堆與指針
在32位的Windows操作系統中,每個(gè)進(jìn)程都可以使用4GB的內存,這得益于虛擬尋址技術(shù),在這4GB的內存中存儲著(zhù)可執行代碼、代碼加載的DLL和程序運行的所有變量,在C#中,虛擬內存中有個(gè)兩個(gè)存儲變量的區域,一個(gè)稱(chēng)為堆棧,一個(gè)稱(chēng)為托管堆,托管堆的出現是.NET不同于其他語(yǔ)言的地方,堆棧存儲值類(lèi)型數據,而托管堆存儲引用類(lèi)型如類(lèi)、對象,并受垃圾收集器的控制和管理。在堆棧中,一旦變量超出使用范圍,其使用的內存空間會(huì )被其他變量重新使用,這時(shí)其空間中存儲的值將被其他變量覆蓋而不復存在,但有時(shí)候我們希望這些值仍然存在,這就需要托管堆來(lái)實(shí)現。我們用幾段代碼來(lái)說(shuō)明其工作原理,假設已經(jīng)定義了一個(gè)類(lèi)class1:
class1 object1;
object1=new class1();
  第一句定義了一個(gè)class1的引用,實(shí)質(zhì)上只是在堆棧中分配了一個(gè)4個(gè)字節的空間,它將用來(lái)存府后來(lái)實(shí)例化對象在托管堆中的地址,在windows中這需要4個(gè)字節來(lái)表示內存地址。第二句實(shí)例化object1對象,實(shí)際上是在托管堆中開(kāi)僻了一個(gè)內存空間來(lái)存儲類(lèi)class1的一個(gè)具體對象,假設這個(gè)對象需要36個(gè)字節,那么object1指向的實(shí)際上是在托管堆一個(gè)大小為36個(gè)字節的連續內存空間開(kāi)始的地址。由此也可以看出在C#編譯器中為什么不允許使用未實(shí)例化的對象,因為這個(gè)對象在托管堆中還不存在。當對象不再使用時(shí),這個(gè)被存儲在堆棧中的引用變量將被刪除,但是從上述機制可以看出,在托管堆中這個(gè)引用指向的對象仍然存在,其空間何時(shí)被釋放取決垃圾收集器而不是引用變量失去作用域時(shí)。
  在使用電腦的過(guò)程中大家可能都有過(guò)這種經(jīng)驗:電腦用久了以后程序運行會(huì )變得越來(lái)越慢,其中一個(gè)重要原因就是系統中存在大量?jì)却嫠槠?,就是因為程序反復在堆棧中?chuàng )建和釋入變量,久而久之可用變量在內存中將不再是連續的內存空間,為了尋址這些變量也會(huì )增加系統開(kāi)銷(xiāo)。在.net中這種情形將得到很大改善,這是因為有了垃圾收集器的工作,垃圾收集器將會(huì )壓縮托管堆的內存空間,保證可用變量在一個(gè)連續的內存空間內,同時(shí)將堆棧中引用變量中的地址改為新的地址,這將會(huì )帶來(lái)額外的系統開(kāi)銷(xiāo),但是,其帶來(lái)的好處將會(huì )抵消這種影響,而另外一個(gè)好處是,程序員將不再花上大量的心思在內在泄露問(wèn)題上。
  當然,以C#程序中不僅僅只有引用類(lèi)型的變量,仍然也存在值類(lèi)型和其他托管堆不能管理的對象,如果文件名柄、網(wǎng)絡(luò )連接和數據庫連接,這些變量的釋放仍需要程序員通過(guò)析構函數或IDispose接口來(lái)做。
  另一方面,在某些時(shí)候C#程序也需要追求速度,比如對一個(gè)含用大量成員的數組的操作,如果仍使用傳統的類(lèi)來(lái)操作,將不會(huì )得到很好的性能,因為數組在C#中實(shí)際是System.Array的實(shí)例,會(huì )存儲在托管堆中,這將會(huì )對運算造成大量的額外的操作,因為除了垃圾收集器除了會(huì )壓縮托管堆、更新引用地址、還會(huì )維護托管堆的信息列表。所幸的是C#中同樣能夠通過(guò)不安全代碼使用C++程序員通常喜歡的方式來(lái)編碼,在標記為unsafe的代碼塊使用指針,這和在C++中使用指針沒(méi)有什么不同,變量也是存府在堆棧中,在這種情況下聲明一個(gè)數組可以使用stackalloc語(yǔ)法,比如聲明一個(gè)存儲有50個(gè)double類(lèi)型的數組:
double* pDouble=stackalloc double[50]
stackalloc會(huì )給pDouble數組在堆棧中分配50個(gè)double類(lèi)型大小的內存空間,可以使用pDouble[0]、*(pDouble+1)這種方式操作數組,與在C++中一樣,使用指針時(shí)必須知道自己在做什么,確保訪(fǎng)問(wèn)的正確的內存空間,否則將會(huì )出現無(wú)法預料的錯誤。
  掌握托管堆、堆棧、垃圾收集器和不安全代碼的工作原理和方式,將有助于你成為真正的優(yōu)秀C#程序員。
  進(jìn)程中每個(gè)線(xiàn)程都有自己的堆棧,這是一段線(xiàn)程創(chuàng )建時(shí)保留下的地址區域。我們的“棧內存”即在此。至于“堆”內存,我個(gè)人認為在未用new定義時(shí),堆應該就是未“保留”未“提交”的自由空間,new的功能是在這些自由空間中保留(并提交?)出一個(gè)地址范圍
  棧(Stack)是操作系統在建立某個(gè)進(jìn)程時(shí)或者線(xiàn)程(在支持多線(xiàn)程的操作系統中是線(xiàn)程)為這個(gè)線(xiàn)程建立的存儲區域,該區域具有FIFO
FILO的特性,在編譯的時(shí)候可以指定需要的Stack的大小。在編程中,例如C/C++中,所有的局部變量都是從棧中分配內存空間,實(shí)際上也不是什么分配,只是從棧頂向上用就行,在退出函數的時(shí)候,只是修改棧指針就可以把棧中的內容銷(xiāo)毀,所以速度最快。 
    堆(Heap)是應用程序在運行的時(shí)候請求操作系統分配給自己內存,一般是申請/給予的過(guò)程,C/C++分別用malloc/New請求分配Heap,用free/delete銷(xiāo)毀內存。由于從操作系統管理的內存分配所以在分配和銷(xiāo)毀時(shí)都要占用時(shí)間,所以用堆的效率低的多!但是堆的好處是可以做的很大,C/C++對分配的Heap是不初始化的。 
    在Java中除了簡(jiǎn)單類(lèi)型(int,char等)都是在堆中分配內存,這也是程序慢的一個(gè)主要原因。但是跟C/C++不同,Java中分配Heap內存是自動(dòng)初始化的。在Java中所有的對象(包括int的wrapper   Integer)都是在堆中分配的,但是這個(gè)對象的引用卻是在Stack中分配。也就是說(shuō)在建立一個(gè)對象時(shí)從兩個(gè)地方都分配內存,在Heap中分配的內存實(shí)際建立這個(gè)對象,而在Stack中分配的內存只是一個(gè)指向這個(gè)堆對象的指針(引用)而已。
  在.NET的所有技術(shù)中,最具爭議的恐怕是垃圾收集(Garbage Collection,GC)了。作為.NET框架中一個(gè)重要的部分,托管堆和垃圾收集機制對我們中的大部分人來(lái)說(shuō)是陌生的概念。在這篇文章中將要討論托管堆,和你將從中得到怎樣的好處。
  為什么要托管堆?
  .NET框架包含一個(gè)托管堆,所有的.NET語(yǔ)言在分配引用類(lèi)型對象時(shí)都要使用它。像值類(lèi)型這樣的輕量級對象始終分配在棧中,但是所有的類(lèi)實(shí)例和數組都被生成在一個(gè)內存池中,這個(gè)內存池就是托管堆。
  垃圾收集器的基本算法很簡(jiǎn)單:
  ● 將所有的托管內存標記為垃圾
  ● 尋找正被使用的內存塊,并將他們標記為有效
  ● 釋放所有沒(méi)有被使用的內存塊
  ● 整理堆以減少碎片
  托管堆優(yōu)化
  看上去似乎很簡(jiǎn)單,但是垃圾收集器實(shí)際采用的步驟和堆管理系統的其他部分并非微不足道,其中常常涉及為提高性能而作的優(yōu)化設計。舉例來(lái)說(shuō),垃圾收集遍歷整個(gè)內存池具有很高的開(kāi)銷(xiāo)。然而,研究表明大部分在托管堆上分配的對象只有很短的生存期,因此堆被分成三個(gè)段,稱(chēng)作generations。新分配的對象被放在generation 0中。這個(gè)generation是最先被回收的——在這個(gè)generation中最有可能找到不再使用的內存,由于它的尺寸很?。ㄐ〉阶阋苑胚M(jìn)處理器的L2 cache中),因此在它里面的回收將是最快和最高效的。
  托管堆的另外一種優(yōu)化操作與locality of reference規則有關(guān)。該規則表明,一起分配的對象經(jīng)常被一起使用。如果對象們在堆中位置很緊湊的話(huà),高速緩存的性能將會(huì )得到提高。由于托管堆的天性,對象們總是被分配在連續的地址上,托管堆總是保持緊湊,結果使得對象們始終彼此靠近,永遠不會(huì )分得很遠。這一點(diǎn)與標準堆提供的非托管代碼形成了鮮明的對比,在標準堆中,堆很容易變成碎片,而且一起分配的對象經(jīng)常分得很遠。
  還有一種優(yōu)化是與大對象有關(guān)的。通常,大對象具有很長(cháng)的生存期。當一個(gè)大對象在.NET托管堆中產(chǎn)生時(shí),它被分配在堆的一個(gè)特殊部分中,這部分堆永遠不會(huì )被整理。因為移動(dòng)大對象所帶來(lái)的開(kāi)銷(xiāo)超過(guò)了整理這部分堆所能提高的性能。
  關(guān)于外部資源(External Resources)的問(wèn)題
  垃圾收集器能夠有效地管理從托管堆中釋放的資源,但是資源回收操作只有在內存緊張而觸發(fā)一個(gè)回收動(dòng)作時(shí)才執行。那么,類(lèi)是怎樣來(lái)管理像數據庫連接或者窗口句柄這樣有限的資源的呢?等待,直到垃圾回收被觸發(fā)之后再清理數據庫連接或者文件句柄并不是一個(gè)好方法,這會(huì )嚴重降低系統的性能。
  所有擁有外部資源的類(lèi),在這些資源已經(jīng)不再用到的時(shí)候,都應當執行Close或者Dispose方法。從Beta2(譯注:本文中所有的Beta2均是指.NET Framework Beta2,不再特別注明)開(kāi)始,Dispose模式通過(guò)IDisposable接口來(lái)實(shí)現。這將在本文的后續部分討論。
  需要清理外部資源的類(lèi)還應當實(shí)現一個(gè)終止操作(finalizer)。在C#中,創(chuàng )建終止操作的首選方式是在析構函數中實(shí)現,而在Framework層,終止操作的實(shí)現則是通過(guò)重載System.Object.Finalize 方法。以下兩種實(shí)現終止操作的方法是等效的:
  ~OverdueBookLocator()
  {
   Dispose(false);
  }
  和:
  public void Finalize()
  {
   base.Finalize();
   Dispose(false);
  }
  在C#中,同時(shí)在Finalize方法和析構函數實(shí)現終止操作將會(huì )導致錯誤的產(chǎn)生。
  除非你有足夠的理由,否則你不應該創(chuàng )建析構函數或者Finalize方法。終止操作會(huì )降低系統的性能,并且增加執行期的內存開(kāi)銷(xiāo)。同時(shí),由于終止操作被執行的方式,你并不能保證何時(shí)一個(gè)終止操作會(huì )被執行。
  內存分配和垃圾回收的細節
  對GC有了一個(gè)總體印象之后,讓我們來(lái)討論關(guān)于托管堆中的分配與回收工作的細節。托管堆看起來(lái)與我們已經(jīng)熟悉的C++編程中的傳統的堆一點(diǎn)都不像。在傳統的堆中,數據結構習慣于使用大塊的空閑內存。在其中查找特定大小的內存塊是一件很耗時(shí)的工作,尤其是當內存中充滿(mǎn)碎片的時(shí)候。與此不同,在托管堆中,內存被組制成連續的數組,指針總是巡著(zhù)已經(jīng)被使用的內存和未被使用的內存之間的邊界移動(dòng)。當內存被分配的時(shí)候,指針只是簡(jiǎn)單地遞增——由此而來(lái)的一個(gè)好處是,分配操作的效率得到了很大的提升。
  當對象被分配的時(shí)候,它們一開(kāi)始被放在generation 0中。當generation 0的大小快要達到它的上限的時(shí)候,一個(gè)只在generation 0中執行的回收操作被觸發(fā)。由于generation 0的大小很小,因此這將是一個(gè)非??斓腉C過(guò)程。這個(gè)GC過(guò)程的結果是將generation 0徹底的刷新了一遍。不再使用的對象被釋放,確實(shí)正被使用的對象被整理并移入generation 1中。
  當generation 1的大小隨著(zhù)從generation 0中移入的對象數量的增加而接近它的上限的時(shí)候,一個(gè)回收動(dòng)作被觸發(fā)來(lái)在generation 0和generation 1中執行GC過(guò)程。如同在generation 0中一樣,不再使用的對象被釋放,正在被使用的對象被整理并移入下一個(gè)generation中。大部分GC過(guò)程的主要目標是generation 0,因為在generation 0中最有可能存在大量的已不再使用的臨時(shí)對象。對generation 2的回收過(guò)程具有很高的開(kāi)銷(xiāo),并且此過(guò)程只有在generation 0和generation 1的GC過(guò)程不能釋放足夠的內存時(shí)才會(huì )被觸發(fā)。如果對generation 2的GC過(guò)程仍然不能釋放足夠的內存,那么系統就會(huì )拋出OutOfMemoryException異常
  帶有終止操作的對象的垃圾收集過(guò)程要稍微復雜一些。當一個(gè)帶有終止操作的對象被標記為垃圾時(shí),它并不會(huì )被立即釋放。相反,它會(huì )被放置在一個(gè)終止隊列(finalization queue)中,此隊列為這個(gè)對象建立一個(gè)引用,來(lái)避免這個(gè)對象被回收。后臺線(xiàn)程為隊列中的每個(gè)對象執行它們各自的終止操作,并且將已經(jīng)執行過(guò)終止操作的對象從終止隊列中刪除。只有那些已經(jīng)執行過(guò)終止操作的對象才會(huì )在下一次垃圾回收過(guò)程中被從內存中刪除。這樣做的一個(gè)后果是,等待被終止的對象有可能在它被清除之前,被移入更高一級的generation中,從而增加它被清除的延遲時(shí)間。
  需要執行終止操作的對象應當實(shí)現IDisposable接口,以便客戶(hù)程序通過(guò)此接口快速執行終止動(dòng)作。IDisposable接口包含一個(gè)方法——Dispose。這個(gè)被Beta2引入的接口,采用一種在Beta2之前就已經(jīng)被廣泛使用的模式實(shí)現。從本質(zhì)上講,一個(gè)需要終止操作的對象暴露出Dispose方法。這個(gè)方法被用來(lái)釋放外部資源并抑制終止操作,就象下面這個(gè)程序片斷所演示的那樣:
  view plaincopy to clipboardprint?
public class OverdueBookLocator: IDisposable  
  {  
   ~OverdueBookLocator()  
   {  
   InternalDispose(false);  
   }  
   public void Dispose()  
   {  
   InternalDispose(true);  
   }  
   protected void InternalDispose(bool disposing)  
   {  
   if(disposing)  
   {  
   GC.SuppressFinalize(this);  
   // Dispose of managed objects if disposing.  
   }  
   // free external resources here  
   }  
  } 

本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/asdfnike/archive/2009/04/07/4054568.aspx
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
堆棧和托管堆 c#
Java中的垃圾回收原理
.Net內存分配筆記
Unity零基礎到進(jìn)階 ??| Unity中的 GC及優(yōu)化 超級全面解析 ☆(ゝω?)v 建議收藏!
Unity優(yōu)化之GC
深入Java核心 探秘Java垃圾回收機制
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久