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