2013-08-29 230 views
3

我有一個工作,直到最近,一些舊的代碼,但現在似乎BARF它運行使用的OpenJDK 6而不是Java SE的6JAI創建似乎離開文件描述符開放

這個問題似乎在新服務器上圍繞JAI.create。我有我縮放並轉換爲PNG文件的jpeg文件。這段代碼過去沒有任何泄漏,但現在移動到了運行OpenJDK的盒子,文件描述符似乎永遠不會關閉,並且我看到越來越多的tmp文件在服務器上的tmp目錄中累積。這些不是我創建的文件,所以我認爲它是JAI。

另一個原因可能是新服務器上的堆大小較大。如果JAI清理完成,但是GC發生的頻率較低,那麼也許這些文件因此而堆積如山。減少堆大小不是一種選擇,我們似乎與增加限制有關的不相關問題。

這裏是當我運行這個泄漏的文件的一個例子:

/tmp/imageio7201901174018490724.tmp 

一些代碼:

// Processor is an internal class that aggregates operations 
// performed on the image, like resizing 
private byte[] processImage(Processor processor, InputStream stream) { 
    byte[] bytes = null; 
    SeekableStream s = null; 
    try { 
     // Read the file from the stream 
     s = SeekableStream.wrapInputStream(stream, true); 
     RenderedImage image = JAI.create("stream", s); 
     BufferedImage img = PlanarImage.wrapRenderedImage(image).getAsBufferedImage(); 
     // Process image 
     if (processor != null) { 
      image = processor.process(img); 
     } 
     // Convert to bytes 
     bytes = convertToPngBytes(image); 
    } catch (Exception e){ 
     // error handling 
    } finally { 
     // Clean up streams 
     IOUtils.closeQuietly(stream); 
     IOUtils.closeQuietly(s); 
    } 
    return bytes; 
} 

private static byte[] convertToPngBytes(RenderedImage image) throws IOException { 
    ByteArrayOutputStream out = null; 
    byte[] bytes = null; 
    try { 
     out = new ByteArrayOutputStream(); 
     ImageIO.write(image, "png", out); 
     bytes = out.toByteArray(); 
    } finally { 
     IOUtils.closeQuietly(out); 
    } 
    return bytes; 
} 

我的問題是:

  1. 有沒有人遇到這一點,解決了嗎?由於創建的tmp文件不是我的,我不知道他們的名字是什麼,因此不能對它們做任何事情。
  2. 調整和重新格式化圖像的選擇庫有哪些?我聽說過斯卡爾 - 我應該看看什麼?

我寧可不要在這個時候rewite的舊代碼,但如果沒有別的選擇......

謝謝!

回答

0

找到了!

所以流被另一個流中的代碼不同的區域包裹:

iis = ImageIO.createImageInputStream(stream); 

進一步回落,流被關閉。

與Sun Java一起運行時,這似乎不會泄漏任何資源,但在使用Open JDK運行時似乎會導致泄漏。

我不確定這是爲什麼(我沒有看過源代碼來驗證,雖然我有我的猜測),但這似乎正在發生。一旦我明確地關閉了包裝流,一切都很好。

+0

所以在這個例子中,你明確地調用'iis.close()'並解決了這個問題? – rogerdpack

+0

@rogerdpack唉這是很久以前,我不記得我做了什麼。我同意這似乎是答案的含義,我無法證實這是否確實是我所做的。抱歉。 – MrSilverSnorkel

4

只是對臨時文件/終結器問題的評論,現在你似乎已經解決了問題的根源(對於評論太長了,所以我將它作爲答案發布......:-P) :

臨時文件由ImageIO的FileCacheImageInputStream創建。只要您致電ImageIO.createImageInputStream(stream)並且useCache標誌爲true(默認值),就會創建這些實例。您可以將其設置爲false以禁用磁盤緩存,但會以內存緩存爲代價。這可能是有意義的,因爲你有一大堆,但如果你正在處理非常大的圖像可能不會。

我也認爲你是(幾乎)正確的終結者問題。你會發現下面的'finalize'方法上FileCacheImageInputStream(太陽JDK 6/1.6.0_26):

protected void finalize() throws Throwable { 
    // Empty finalizer: for performance reasons we instead use the 
    // Disposer mechanism for ensuring that the underlying 
    // RandomAccessFile is closed/deleted prior to garbage collection 
} 

有一個在類的構造函數頗有些「有趣」的代碼,即設置了自動流關閉和處置當實例完成時(客戶端代碼應該忘記這麼做)。這在OpenJDK實現中可能會有所不同,至少它看起來有點怪異。這也是我不清楚在什麼「性能的原因,」我們正在談論的那一刻...

在任何情況下,似乎在ImageInputStream實例調用close,因爲你現在做的,會正確地關閉文件描述符刪除臨時文件。

+0

感謝您的輸入。我承認我沒有深入瞭解源代碼。我也不知道緩存設置。非常感謝反饋。 – MrSilverSnorkel

+0

正確的你......已經完成並完成了。 – MrSilverSnorkel

+1

肯定有一些奇怪的事情發生,在我的例子中,JNA過於關閉處理。奇怪http://stackoverflow.com/q/38444752/32453 – rogerdpack