今天生產(chǎn)環(huán)境的一個(gè)Java應(yīng)用程序的日志里,出現(xiàn)了很不和諧的記錄:
java.io.IOException: Too many open files
在網(wǎng)上查了一些關(guān)于此異常的解決方案,基本上都是說要擴(kuò)大linux系統(tǒng)的文件句柄數(shù)限制。
但如果程序?qū)τ赟ocket、Stream等使用后沒能及時(shí)關(guān)閉的話,擴(kuò)大這個(gè)文件句柄數(shù)限制是治標(biāo)不治本的。
我先是在測試環(huán)境擴(kuò)大了linux的文件句柄數(shù)限制,隨后提高測試壓力,過一段時(shí)間后發(fā)現(xiàn)還是會(huì)報(bào)這個(gè)異常。
(中間也用lsof命令查看占用的文件句柄數(shù),不斷的增加啊,心寒啊。)
現(xiàn)象是 用 lsof -p *** 來查看,形如
java 22055 webapp 21w FIFO 0,6 29300342 pipe
java 22055 webapp 22r FIFO 0,6 29256305 pipe
在不斷增加。
所以我果斷對代碼進(jìn)行了排查。文件的IO操作、對數(shù)據(jù)庫的操作,看了都沒有什么問題,
最后排查到由Java程序去調(diào)用Shell腳本的代碼,
代碼寫的還是很簡單的,看上去很清晰,但是有明顯的問題:
Process proc = Runtime.getRuntime().exec(cmd);
//略
對proc.getErrorStream()、proc.getInputStream()流的操作。
proc.waitFor();
return proc.exitValue();
這里的問題是 對流沒有在finally處做關(guān)閉處理。這個(gè)問題比較明顯。
還有一個(gè)問題就是Process的使用問題,
如果對Process的不熟悉的話,可能會(huì)以為return proc.exitValue();之后就萬事大吉了。
(exitValue()確實(shí)很像是已經(jīng)退出了并得到返回值的意思,估計(jì)是這個(gè)方法的名字迷惑了我們的開發(fā)人員。)
實(shí)際不然,看Jdk的幫助文檔可以發(fā)現(xiàn),要通過destroy()來實(shí)現(xiàn)對子進(jìn)程的銷毀并釋放占用的File Descriptor。
這個(gè)問題,短時(shí)間的測試是不會(huì)有問題的,但在投入生產(chǎn)后,隨著程序的長期運(yùn)行,開發(fā)中的疏忽就會(huì)暴露了。
所以在對使用的方法拿不準(zhǔn)的情況下,還是要多做調(diào)查,謹(jǐn)慎使用啊。
希望能讓在排查類似問題的朋友注意,如果你排查的代碼中也存在Runtime.getRuntime().exec(cmd)這樣的調(diào)用,那么請確保那段代碼沒有問題。