進程內存是怎麼爆倉的
但是當爆倉的時候倉庫真的就滿了嗎?再也放不下任何東西了嗎?
其實不一定,快遞的包裹總是比裡面的貨物要大的,有的包裹很大但是裡面東西很少,所以倉庫滿了,不是被實際貨物佔滿而是被裝有貨物的快遞包裹佔滿了,而是倉庫里的空間沒有了。
對於程序進程的內存一樣存在類似的問題。
進程實際佔用的內存空間總是比它實際用到的內存要大的,有時候我們沒有真的用到很多內存,但是進程的地址空間滿了,也就無法再分配到內存了。
進程的所有地址空間相當於倉庫,實際讀寫分配的內存塊相當於貨物,而佔用的地址空間相當於包裝盒。
對於Windows 32位進程來說,能用到的最大地址空間是 2GB,每次分配虛擬地址空間的最小單位是 64KB,也就是說分配64KB以下的內存也至少要佔用64KB的地址空間,也就是最多能分配3萬多塊。
GetProcessMemoryInfo 函數可以用來查看佔用了多少虛擬地址。
但是這和很多人的直覺不符合,很多人覺得自己可以分配很多內存,內存也沒有爆掉。這是因為我們通常分配內存是從堆上分配,調用malloc其實也就是HeapAlloc,但是Heap是哪裡來的呢?Heap的內存是用VirtualAlloc得來的。
大部分內存分配都是幾個位元組到幾百位元組的,所以系統會先用VirtualAlloc分配一塊大內存,然後切割成很多小塊通過HeapAlloc給程序用。
進程的地址空間不僅僅給Heap使用還有很多其它用途,比如可執行模塊,內存映像文件,系統保留段等。實際上程序真正能用到的部分不足2GB,大約1.8-1.9GB。
內存地址空間有個利用率問題,有時候明明沒有用那麼多內存,但是還是分配不到新內存了,因為有些人分配了一塊大內存,但是只讀寫其中很小的一部分。
打個比方,有家影院100個座位,票都賣掉了,其中有個家庭買了5張票,但是電影開演的時候他們有事情,只有一個人去看電影了,剩下的票也沒有退,所以電影院里看起來還空著4個座位,但是也沒辦法再賣票了。
此外,還有個碎片化問題,因為每次出於不同的目的總是分配不同大小的內存,使用過程中一旦不需要了會釋放掉,頻繁的分配釋放會導致碎片話。這時候看起來空閑內存還有一些,但是都是不連續的碎片,如果有人要分配一塊大內存就找不到連續的塊了。
打個比方,有家電影院有100個座位,賣出了90張票,這時候有個5口之家要看電影,他們要買5個連續的座位,因為他們不想分開坐,雖然看起來還有10個空座但是都是零散,沒有連續的5個座位,所以這家人還是買不到票。
剛剛進入Win32時代的時候,我這種從DOS時代640KB內存過來的人,覺得2GB實在是太大了,怎麼用都用不完,沒想到如今2GB已經遠遠不夠了。伺服器開發的同學早就大部分切換到64位了,否則32位下並發連接數都上不去,隨便搞點緩存地址就爆倉了。但是對於客戶端開發,特別是Windows客戶端開發還不得不生活在32位下,向64位的遷移過程十分緩慢。
iOS和Mac在喬幫主的暴力推動下,已經消滅了32位系統了,只有極個別冥頑不靈的App開發者還在堅持寫32位App。
微軟是因為歷史包袱太重,Android是因為缺乏統一的領導,所以64位推進緩慢。
推薦個工具VMMap,sysinternal系列工具之一,用來查看虛擬內存使用狀態。
VMMap
今天寫這個文章的原因是因為我們內部開發的一個視頻直播工具內存爆倉,需要優化了,視頻是個很吃內存的應用,一幀1080p視頻的RGBA原始圖像就要8MB,如果同時處理多路再緩存幾秒的內容就很容易爆倉了。到了4K時代,無論如何也要寫成64位App了。
推薦閱讀:
※如何對視頻中人員翻牆的行為進行檢測?
※雙目測距和激光測距的優缺點比較?
※暴風影音的 「左眼鍵」 到底是炒作還是真技術?