天天看點

velocity 區間運算符導緻生産環境OOM

最近生産環境某些機器頻繁出現FullGC和OOM,用jmap的heap功能檢視該Java程序的記憶體使用情況,如下圖:

velocity 區間運算符導緻生産環境OOM

問題分析:

再用jmap的histo圖檢視目前對象分布圖,發現有很多Integer(為啥?不知道啊。。。下面分解)

velocity 區間運算符導緻生産環境OOM

這裡可以很明顯看到,光Integer就占用了1G多記憶體空間。

手工dump一台的機器,jmap –dump:format=b,file=/home/admin/logs/aaa.bin –F<pid>

這裡将持續很久,而且可能報錯抛異常 UnmappedAddressException,一旦抛出

這種異常,就需要再次重試。另外注意磁盤空間必須保持充足(剩餘空間至少是實體記憶體1.5倍)。

使用MAT工具,分析dump結果。

觀察出問題的線程,如下圖

velocity 區間運算符導緻生産環境OOM

還有其heap占用情況:

velocity 區間運算符導緻生産環境OOM

可以觀察到,該線程持有一個3G空間大小的Integer數組,剛好印證了之前的histo圖。

那這麼大的一個int數組從哪來就很值得懷疑。檢視ASTIntegerRange類的源碼的140行,如下圖:

velocity 區間運算符導緻生産環境OOM

至此,大概可以确定,問題應該就出在這。

關鍵在于,這個類是Velocity自己的類,我們對此一無所聞,它在哪裡被調用,被誰調用,也無法知道。

檢視Apache官方文檔,找到蛛絲馬迹,該類是用來處理[n..m]這種運算符的。

搜了一下整個代碼裡面,隻有一個地方用到了該文法:

velocity 區間運算符導緻生産環境OOM

這裡的endIndex,是背景根據總頁數直接計算出來的,攻擊者沒法僞造。

但是startIndex則是根據使用者傳入的目前頁數來決定的。

而且最關鍵的一點,該文法既可以是單調增,也可以是單調減(比如[1 .. 100]或者[100 .. 1])

因而,一旦使用者傳入一個很大的目前頁數值,velocity會在内部建構一個大小為|n-m|的ArrayList(它内部實際上就是數組)

繼續閱讀