原文地址:.NET Memory usage - A restaurant analogy
發布時間:Wednesday, September 06, 2006 1:06 PM
作 者: Tess
我喜愛的作者Simon Singh真是個善於分析的天才。從他的書中可以得知,他善於出神繪色地分析概念,不僅通俗易懂,而且讓人難以忘記。因為經過他的分析,會在你腦海中呈現一個概念的景象。
前幾天工作中,我聽到我的一個同事正給一位客戶解釋有關記憶體的使用和為什麼會出現記憶體異常的問題。雖然我在早期的一篇文章中談到了記憶體溢出和記憶體管理,但我覺得他的分析實在太有趣了,因此我想將此也分享給大家。
聲明: 為了避免冗長,我簡化許多東西。譬如,我是這麼說的,記憶體回收行程分配64MB區段空間。儘管在不同的framework版本下分配的區段空間大小是不同的。並且跟最初給各個對象分配的空間有關,譬如,讀取大對象堆。另外的一些有關記憶體配置細節依賴於配置,我在這也不考慮這些細節了。
第一部分 -通常的記憶體使用量
若你讀過我先前的貼子,就會知道:32位系統進程典型定址空間為2GB的虛擬位址空間。這是供給工作的記憶體空間,它獨立於同時佔有多少RAM空間。佔有更多的RAM有益於提升效能,但它並不會協助擴充到2GB之外的地址空間。
此時,你將這2GB的地址空間想象成餐館的所有佔地空間。
你為對象(不管受否是.net對象)分配空間時,通常兩步走:先預定空間,然後將對象置於預定的空間內。
記憶體預定工作如同在餐館中預定餐桌。正如餐館一樣,記憶體預定依賴其記憶體管理器(memory manager),他會幫你預定到一塊連續的記憶體(memory in chunks)。譬如,你有一個3人的聚會,餐館裡很可能沒有3人餐桌,但你可能會預定一個4人餐桌,你就會使用其中的3個座位,浪費一個座位。
相對於記憶體空間,你預定餐桌好比預定記憶體(這塊記憶體現佔用虛擬位元組),實際佔用的座位(3個座位)好比實際佔用記憶體(實際佔用的記憶體佔用私人位元組)。沒有預定的地板空間就是剩餘可用記憶體。
一個美好的晚上,餐館的情景可能這樣:
藍色地區表示已經預訂的空間,紅色地區表示實際使用的空間,白色地區表示剩餘的可用空間。如所示:
現在假如某人某人電話預訂一個3人餐桌,他將被告之餐館已無可用餐桌。因為3個人坐在一起需要一個4人餐桌。儘管可以分在兩個2人餐桌上坐下,但這似乎並不是一個好主意,因為你們想坐在一起。
相似地,記憶體預定也不可能將記憶體分成小塊,分散在各個不同的地方。要不就是什麼也不分配,要不就是在一塊連續的地區內分配。因此記憶體預定也如上面的預定3人餐桌情景,那麼將會“記憶體溢出”,儘管還有足夠的小塊剩餘空間可用。
細心的觀眾也許會說,不是可以將各個餐桌靠隴,使他們一個連著一個,這不是騰出一個4人餐桌出來了嗎?但是已經用餐的客戶願意此時移動餐桌嗎?預定記憶體正如這樣。
我們談論了記憶體片段,也談了剩餘但停用記憶體塊(因為他們太小了,不足以容納一個新的餐桌),並且我們也談談有多少已經預訂了但尚未使用的記憶體(這不同於佔用虛擬位元組和私人位元組的記憶體)。
第二部分 -.NET的記憶體回收
絕大多數情況下,你依靠某種記憶體管理器(memery manager)(譬如,NT Heap, C++ Heap,GC等等)在應用程式中建立對象。在餐館案例情景下,你將記憶體管理器比作女服務員,她會為你預定座位並會帶你到相應的座位上。例如,調用了malloc方法,就不需要提供欲分配到的地址,而只需告之需要的記憶體區塊大小,malloc會返回記憶體預定的地址。ok,在“C++ heap"區的table 1就是安置給你的地址。
.NET的記憶體回收機制(GC) 則要更進一步。進程中,若要想使用.net對象,它會預先保留一張大“桌子”(64座的桌子)。當某人建立了.net對象,GC會接待他們到桌子的下一個可用座位。有時接待員也會在餐桌旁來回走動,檢查是否有人已經用餐完畢並邀請他們離開,此時其他的客人會就坐該餐桌。一些人可能需要等待其他人用餐完畢才能離開(比如引用對象),因此他們必須獃著等待。也有一些"大爺"人物(釘扣對象[pinned objects]),佔據著靠窗的座位,很生氣地說,“我不走了”。這就意味著,他不能騰出位置來,其他人也過不來。
客人和客人間留著的空位好比.NET記憶體片段。
一旦64座的桌子坐滿了,如需接待新來客人,GC需要預定一個新的64座的桌子。如果預定失敗,就會拋出一個記憶體溢出異常。
但是,它的真正面目怎樣?
好了, 分析得足夠透了,在這兒你將看到真正的asp.net應用程式記憶體情況。
再次說明一下,紅色部分表示已使用記憶體,籃色部分表示預定但尚未使用的記憶體,白色部分表示剩餘可用記憶體。
在記憶體空間的末端,你看到的一些小點很可能是一些動態連結程式庫。如同餐館情形下,儘管還有很多的白色空間,很可能在兩個紅點間的空隙不足以容納一個64 M的區塊,在下一次GC申請一塊新的記憶體時,就會產生一個記憶體溢出異常。
這些小紅點(動態連結程式庫)像這樣布滿整個空間的原因是由於這些特殊的動態連結程式庫會裝載在更適合的基地址上。你不能對那類動態連結程式庫預先分配地址,因為很難預測他們將分配的更適合的地址在哪,對此你僅能做的是,找出實際使用的記憶體到底去哪了。