編譯50字節(jié)代碼耗費4G內(nèi)存
想用宏和內(nèi)聯(lián)匯編做些邪惡的事情(僅僅試著做一些怪異的測試,目的無關(guān)緊要),我決定寫個程序讓Vistual Studio的C++編譯器分配4GB的內(nèi)存,然后處于卡死狀態(tài)。
寫個50字節(jié)的代碼就可以了。
一開始我可能沒注意到我的機子并沒有4GB的空閑內(nèi)存,瘋狂的數(shù)據(jù)分頁,需要找到4GB的內(nèi)存,使得我的筆記本在幾分鐘內(nèi)都毫無反應(yīng)。如果你的機子有超過4GB的空閑內(nèi)存,使用ETW做內(nèi)存分析倒是個很好的測試,看看你可以復(fù)現(xiàn)我的結(jié)果嗎?
我簡化了代碼,只留下的最基本的精華部分,只是覺得這樣很好玩:
- void test()
- {
- __asm { add eax
- __asm { add eax
- }
這是編譯器的輸出:
- error C2414: illegal number of operands
- error C2414: illegal number of operands
- error C2400: inline assembler syntax error in ‘opcode’; found ‘end of file’
- fatal error C1060: compiler is out of heap space
我是在Windows 64位上運行的,編譯器是32位的大型地址進(jìn)程,所以堆空間耗盡意味著分配大約4GB的內(nèi)存空間。我連續(xù)進(jìn)行多次編譯,可以看出每次4GB的內(nèi)存使用量都在飆升。
我很好奇,到底是哪部分編譯程序在分配這些內(nèi)存。我使用cl.exe編譯器,用etwheap.bat記錄了所有的堆空間和 VirtualAlloc 分配 ,并再次編譯了源文件。事實證明我本應(yīng)該使用wprui’s VAlloc Usage選項去獲取追蹤。僅僅只有幾MB是從堆上分配的,大部分都是使用VirtualAlloc來分配的,如圖所示:
接下來,為了完成調(diào)查,我查看了所有的調(diào)用棧。我們可以看到內(nèi)聯(lián)匯編程序的語法分析器正在使用它自己的VirtualHeap分配大量的Asm Tokens。VirtualHeap::Create 預(yù)留內(nèi)存空間,VirtualHeap::HeapExtend提交內(nèi)存。再深入研究下(沒有顯示)發(fā)現(xiàn)內(nèi)存空間預(yù)留在512KB的內(nèi)存塊,被提交在 32KB的內(nèi)存塊。
還有一些細(xì)節(jié),不是很清楚,像為什么VirtualHeap::HeapExtend調(diào)用 VirtualHeap::Create,但是卻沒有源代碼,難以得知。
所以我們不再探究了,我像往常一樣將把這個問題提交給VC++團隊。如果他們解決了這個問題,我并不驚訝,這也算不上是一個嚴(yán)重的問題。第一次遇到這個問題時,因為我的機子沒有4GB的空閑內(nèi)存,所以才注意到它。
編譯器是32位進(jìn)程也是件好事兒,要不然它還會繼續(xù)消耗內(nèi)存,將遠(yuǎn)遠(yuǎn)超過4GB。條件限制萬歲!
這些測試都是在VC++ 2010 的調(diào)試版本上進(jìn)行的,我沒試過其他版本。
Linux 變體
還有一個很類似的問題(鏈接器在一個很簡單的程序上消耗了大量內(nèi)存,詳情見 棧溢出)。
Windows 糟透了?
我預(yù)料到有人會說Windows太爛了,這就是為什么當(dāng)遇到這個問題時,我的筆記本幾分鐘內(nèi)都毫無反應(yīng)。但是如果在Linux和OSX系統(tǒng)上分配 (或?qū)懭耄?GB的內(nèi)存,并不會引發(fā)嚴(yán)重的系統(tǒng)延遲問題,但其實這說明不了什么。我的筆記本只有8GB的內(nèi)存,大部分都在被使用,想獲取到空閑的4GB內(nèi) 存的唯一可能的辦法就是把大量的數(shù)據(jù)寫到磁盤上。筆記本的硬盤相當(dāng)慢,如果我是在工作機子上(32GB的內(nèi)存,20GB可用)或者當(dāng)筆記本上只有很少的程 序在運行時(5GB空閑內(nèi)存),做同樣的測試,4GB內(nèi)存的分配和釋放不到5秒中就可以完成。
Reddit的討論鏈接在這兒。
額外補充
很奇怪,怎么會有一些博客文章比其他人的更受歡迎…
有人在復(fù)現(xiàn)這個問題時遇到了困難,這個bug只能確定在VS2010 SP1 出現(xiàn),并且test函數(shù)放在源文件的最后。
這顯然不是一個嚴(yán)重的bug—代碼也有缺陷,編譯器有給出了警告并且指出問題所在,也沒出現(xiàn)什么大問題。但是它的確是一個失敗的詞法分析程序。尤其 是,內(nèi)存不足會阻止VC++去報告一些括號不匹配的問題—假如你在test函數(shù)之后再添加一個函數(shù),詞法分析完成后,額外的警告就會顯示出來了。
編譯錯誤代碼時給出出錯的信息提示是很重要的,這也是Clang的顯式設(shè)計目標(biāo)之一。
在 Visual Studio SP1 中復(fù)現(xiàn)這個 Bug 了。見下圖或查看原圖。
原文鏈接:http://randomascii.wordpress.com/2013/08/14/50-bytes-of-code-that-took-4-gb-to-compile/