最近生產環境某些機器頻繁出現FullGC和OOM,用jmap的heap功能查看該Java進程的內存使用情況,如下圖:
問題分析:
再用jmap的histo圖查看當前對象分佈圖,發現有很多Integer(爲啥?不知道啊。。。下面分解)
這裏可以很明顯看到,光Integer就佔用了1G多內存空間。
手工dump一臺的機器,jmap –dump:format=b,file=/home/admin/logs/aaa.bin –F<pid>
這裏將持續很久,而且可能報錯拋異常 UnmappedAddressException,一旦拋出
這種異常,就需要再次重試。另外注意磁盤空間必須保持充足(剩餘空間至少是物理內存1.5倍)。
使用MAT工具,分析dump結果。
觀察出問題的線程,如下圖
還有其heap佔用情況:
可以觀察到,該線程持有一個3G空間大小的Integer數組,剛好印證了之前的histo圖。
那這麼大的一個int數組從哪來就很值得懷疑。查看ASTIntegerRange類的源碼的140行,如下圖:至此,大概可以確定,問題應該就出在這。
關鍵在於,這個類是Velocity自己的類,我們對此一無所聞,它在哪裏被調用,被誰調用,也無法知道。
查看Apache官方文檔,找到蛛絲馬跡,該類是用來處理[n..m]這種運算符的。
搜了一下整個代碼裏面,只有一個地方用到了該語法:
這裏的endIndex,是後臺根據總頁數直接計算出來的,***者沒法僞造。
但是startIndex則是根據用戶傳入的當前頁數來決定的。
而且最關鍵的一點,該語法既可以是單調增,也可以是單調減(比如[1 .. 100]或者[100 .. 1])
因而,一旦用戶傳入一個很大的當前頁數值,velocity會在內部構建一個大小爲|n-m|的ArrayList(它內部實際上就是數組)
從而導致OOM。