Java之JVM調優案例分析與實戰(3) - 堆外內存導致的溢出錯誤


環境:基於B\S的點子考試系統,為了發現客戶端能實時地從服務端接收考試數據,系統使用了逆向AJAX技術(也稱Comet或Server Side Push),選用CometD1.1.1作為服務端推送框架,服務器是Jetty7.1.4,硬件為一台普通PC機,Core i5 CPU,

        4G內存,運行32位Windows操作系統。

說明:測試期間發現服務端不定時拋出內存溢出異常,服務器不一定每次都會出現異常,但是假如正式考試時奔潰一次,那估計整場考試都會全亂套,網站管理員嘗試過把堆開到最大,32位系統最多到1.6GB基本無法再加大了,而且開大量也基本沒效果,拋出

        內存溢出異常好像更加繁瑣了。加入-XX:+HeapDumpOnOutOfMemoryError,居然也沒有任何反應,拋出內存溢出異常時什么文件都沒產生。無奈之下只好掛着jstat使勁盯屏幕,發現GC並不頻繁,Eden區,Survivor區,老年代及擁擠代內存全部

       表示"情緒穩定,壓力不大",但是照樣不停的拋出內存溢出異常,管理員鴨梨很大。最后,在內存溢出后從系統日志中找到異常堆棧。

分析:大家都知道操作系統對每個進程能管理的內存是有限的,這台服務器使用的32位Windows平台的限制是2GB,其中給了Java堆1.6GB,而Direct Memory 並不算在1.6GB的堆之內,因此它只能在剩余的0.4GB空間分出一部分。在此應用中導致內 

        存溢出的關鍵是:垃圾收集進行時,虛擬機雖然會對Direct Memory進行回收,但是Direct Memory 卻不能像新生代和老年代那樣,發現空間不足了就通知收集器進行垃圾回收,他只能等到拋出內存溢出異常時,先catch掉,再在catch塊里面“大喊”

        “System.gc”.要是虛擬機還是不聽(如:打開了-XX:+DisableExplicitGC開關),那就只能眼睜睜地看着堆中還有許多空閑內存,自己卻不得不拋出內存異常了。而本案例中使用的Comet1.1.1框架,正好有大量的NIO操作需要用到Direct Memory。

總結:從實踐經驗來看,除了java堆和永久代之外,我們注意到下面這些區域也會占用較多的內存,這里所有的內存總和會受到操作系統進程最大內存的限制:

        1.Direct Memory:可以通過-XX:MaxDirectMemorySize調整大小,內存不足時拋出OutOfMemoryError或OutOfMemoryError:Direct buffer memory。

        2.線程堆棧:可通過-Xss調整大小內存不足時拋出StackoverflowErroe(縱向無法分配,即無法分配新的棧幀)或OutOfMemoryError:unable to create new native thread(橫向無法分配,即無法建立新的線程)。

        3.Socket緩存區:每個Socket連接都Receive和Send兩個緩存區,分別占大約37KB和25KB的內存,連接多的話這塊內存占用也比較可觀。如果無法分配,則可能會拋出IOException:Too many open files異常。

        4.JNI代碼:如果代碼中使用JNI調用本地庫,那么本地庫使用內存也不在堆中

        5.虛擬機和GC:虛擬機和GC的代碼執行也要消耗一定的內存。

       

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM