簡體   English   中英

Java IOException“打開的文件太多”

[英]Java IOException "Too many open files"

我正在對多個文件進行一些文件 I/O(寫入 19 個文件,它確實如此)。 在給他們寫了幾百次之后,我得到了 Java IOException : Too many open files 但實際上我一次只打開了幾個文件。 這里有什么問題? 我可以驗證寫入是否成功。

在 Linux 和其他 UNIX / 類 UNIX 平台上,操作系統對進程在任何給定時間可能擁有的打開文件描述符的數量進行了限制。 在過去,這個限制曾經是硬連線1 ,而且相對較小。 現在它要大得多(成百上千),並且受到每個進程的“軟”可配置資源限制。 (查找內置的ulimit shell ...)

您的 Java 應用程序必須超出每個進程的文件描述符限制。

你說你打開了 19 個文件,幾百次后你得到一個 IOException 說“打開的文件太多”。 現在這個特殊的異常只會在請求一個新的文件描述符時發生; 即當您打開文件(或管道或套接字)時。 您可以通過打印 IOException 的堆棧跟蹤來驗證這一點。

除非您的應用程序以較小的資源限制運行(這似乎不太可能),否則它必須重復打開文件/套接字/管道,但無法關閉它們。 找出發生這種情況的原因,您應該能夠弄清楚該怎么做。

僅供參考,以下模式是一種寫入文件的安全方式,可保證不會泄漏文件描述符。

Writer w = new FileWriter(...);
try {
    // write stuff to the file
} finally {
    try {
        w.close();
    } catch (IOException ex) {
        // Log error writing file and bail out.
    }
}

1 - 硬連線,如編譯到內核中。 更改可用 fd 插槽的數量需要重新編譯......並且可能導致更少的內存可用於其他事情。 在 Unix 通常在 16 位機器上運行的日子里,這些事情真的很重要。

更新

Java 7 的方式更簡潔:

try (Writer w = new FileWriter(...)) {
    // write stuff to the file
} // the `w` resource is automatically closed 

更新 2

顯然,您在嘗試運行外部程序時也會遇到“打開的文件太多”。 基本原因如上所述。 但是,您在exec(...)遇到此問題的原因是 JVM 正在嘗試創建將連接到外部應用程序的標准輸入/輸出/錯誤的“管道”文件描述符。

對於 UNIX:

正如 Stephen C 所建議的,將最大文件描述符值更改為更高的值可以避免此問題。

嘗試查看您當前的文件描述符容量:

   $ ulimit -n

然后根據您的要求更改限制。

   $ ulimit -n <value>

請注意,這只會更改當前 shell 和任何子/后代進程中的限制。 要使更改“堅持”,您需要將其放入相關的 shell 腳本或初始化文件中。

在打開新文件描述符之前,您顯然沒有關閉文件描述符。 你是windows還是linux?

盡管在大多數一般情況下,錯誤很明顯是文件句柄尚未關閉,但我剛剛在 Linux 上遇到了一個帶有 JDK7 的實例……這足以解釋這里。

該程序打開了一個 FileOutputStream (fos)、一個 BufferedOutputStream (bos) 和一個 DataOutputStream (dos)。 寫入數據輸出流后,dos 關閉,我認為一切正常。

然而,在內部,dos 試圖刷新 bos,它返回磁盤已滿錯誤。 該異常被 DataOutputStream 吃掉了,因此底層 bos 沒有關閉,因此 fos 仍然打開。

在稍后階段,該文件從(帶有 .tmp 的東西)重命名為其真實名稱。 因此,java 文件描述符跟蹤器丟失了原始 .tmp 的跟蹤,但它仍然是打開的!

為了解決這個問題,我必須首先自己刷新 DataOutputStream,檢索 IOException 並自己關閉 FileOutputStream。

我希望這可以幫助別人。

如果您在自動化測試中看到這一點:最好在測試運行之間正確關閉所有文件。

如果你不確定你打開了哪些文件,一個好的開始是拋出異常的“打開”調用! 😄

如果您有一個文件句柄,只要其父對象還活着,就應該打開它,您可以在父對象上添加一個finalize方法,該方法在文件句柄上調用 close。

最近有一個程序批處理文件,我當然已經關閉了循環中的每個文件,但錯誤仍然存​​在。

后來,我通過每隔數百個文件進行垃圾收集來解決這個問題:

int index;
while () {
    try {
        // do with outputStream...
    } finally {
        out.close();
    }
    if (index++ % 100 = 0)
        System.gc();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM