環境:基於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的代碼執行也要消耗一定的內存。