最近吃了virtual memory的苦頭,一直接到報告說我們的產品時不時會重啟,並且報告說是“使用的memory超過了5G”。
重啟是我們產品的機制,我們會監控記憶體,超過一定限度會進行GC並重啟應用,可是超過5G這也太誇張了,而且報告出狀況的環境都沒有多大的負載。這個問題一直被認為是記憶體泄露,苦於無法重現一直無法解決,最近忽然在一個客戶的環境中重現了,趕緊研究!
最終發現我們的監控機制是有問題的:代碼是這樣判斷的, 當 virtual memory > 5 * Xmx(java option) 就會重啟, 看起來沒錯, 但是在使用free命令查看應用運行前後的記憶體size卻發現實際使用的記憶體並不多,並且剩餘記憶體一直保持在一個比較穩定的數字,並沒有持續下降的情況。這樣看來記憶體泄露實際上不存在。那是什麼原因導致應用啟動時 virtual memory 的使用有那麼大的數值呢?
無奈之後我開始關注virtual memory 這個值得意義,哇,問題出在這裡,它的意義並不是我們想當然的那樣,大相徑庭!
virtual memory 在top命令的manpage是這樣解釋的:
o: VIRT -- Virtual Image (kb) The total amount of virtual memory used by the task. It includes all code, data and shared libraries plus pages that have been swapped out. VIRT = SWAP + RES.
就是說 virtual memory 不但是包括了代碼資料還包括了共用庫(對於java來說就是jar檔案了),共用庫不但包括正在使用的還包括了已經在SWAP中的。已經在SWAP中的其實就不是在記憶體中了,什麼意思呢,就是說virtual memory 叫做虛擬記憶體,其實這個數值的一部分並不是physic的記憶體,而是磁碟空間。
更近一步解釋:virtual memory 是所有處在virtual memory map 中的資料容量之和,一般來說沒什麼意義。
舉個例子說吧:比如一個程式classpath中引用了10M的jar包,在程式中用到了十個10M的檔案那麼這個程式啟動的時候 virtual memory會是多少呢?
程式本身的code + 程式本身的data + 10m(jars) + 10*10m(檔案) + jre佔用的空間, 這已經是個很大的數字了,但是實際上在程式啟動時並不會將所有的都載入,即便載入了也很快會被swap出去, virtual memory 的數值其實就是個數字,實際中的耗費跟它沒有任何關係。
所以在監控程式中使用virtual memory沒有任何意義是錯誤的,那哪個數值表示實際使用的記憶體呢?
resident memory: the number of pages that are currently resident in RAM
它才是比較靠譜的記憶體使用量值。
介紹一片文章,講這個講的太透了:
什麼是virtual memory