今天生產(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)不治本的。
我先是在測(cè)試環(huán)境擴(kuò)大了linux的文件句柄數(shù)限制,隨后提高測(cè)試壓力,過一段時(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 在不斷增加。
所以我果斷對(duì)代碼進(jìn)行了排查。文件的IO操作、對(duì)數(shù)據(jù)庫的操作,看了都沒有什么問題, 最后排查到由Java程序去調(diào)用Shell腳本的代碼,
代碼寫的還是很簡單的,看上去很清晰,但是有明顯的問題: Process proc = Runtime.getRuntime().exec(cmd); //略對(duì)proc.getErrorStream()、proc.getInputStream()流的操作。 proc.waitFor(); return proc.exitValue(); 這里的問題是 對(duì)流沒有在finally處做關(guān)閉處理。這個(gè)問題比較明顯。 還有一個(gè)問題就是Process的使用問題,
如果對(duì)Process的不熟悉的話,可能會(huì)以為return proc.exitValue();之后就萬事大吉了。 (exitValue()確實(shí)很像是已經(jīng)退出了并得到返回值的意思,估計(jì)是這個(gè)方法的名字迷惑了我們的開發(fā)人員。) 實(shí)際不然,看Jdk的幫助文檔可以發(fā)現(xiàn),要通過destroy()來實(shí)現(xiàn)對(duì)子進(jìn)程的銷毀并釋放占用的File Descriptor。
這個(gè)問題,短時(shí)間的測(cè)試是不會(huì)有問題的,但在投入生產(chǎn)后,隨著程序的長期運(yùn)行,開發(fā)中的疏忽就會(huì)暴露了。 所以在對(duì)使用的方法拿不準(zhǔn)的情況下,還是要多做調(diào)查,謹(jǐn)慎使用啊。
希望能讓在排查類似問題的朋友注意,如果你排查的代碼中也存在Runtime.getRuntime().exec(cmd)這樣的調(diào)用,那么請(qǐng)確保那段代碼沒有問題。
|