2012-09-10 30 views
4

在工作中,我們有一些tomcat服務器運行幾個webapps,其中大約一半需要做一些圖像處理。如何用ImageIO插件解決OutOfMemoryError問題?

在進行圖像處理之前,這些webapp會執行ImageIO.scanForPlugins()以將合適的圖像讀取器和寫入器存儲到內存中。雖然之前這只是運行任何時候需要處理圖像,我們現在只有在webapps初始化時才運行掃描(因爲我們不會在運行後添加任何jar,爲什麼不止一次運行掃描?)

幾天後,由於OutOfMemoryError,tomcat實例崩潰。幸運的是我們設置了HeapDumpOnOutOfMemoryError選項,所以我查看了堆轉儲。在轉儲中,我發現97%的內存是由javax.imageio.spi.PartialOrderIterator的實例拍攝的。大部分空間被支持java.util.LinkedList,其中有1800萬元。鏈表由javax.imageio.spi.DigraphNode組成,其中包含由ImageIO.scanForPlugins()加載的圖像閱讀器和編寫器。

「啊哈」,我想,「我們必須在某處循環運行掃描,而我們只是一遍又一遍地添加相同的元素。但是,我想我應該仔細檢查這一假設,所以我寫了下面的測試類:

import javax.imageio.ImageIO; 

public class ImageIOTesting { 

public static void main(String[] args) { 

    for (int i = 0; i < 100000; i++) { 
     ImageIO.scanForPlugins(); 
     if (i % 1000 == 0) { 
      System.out.println(Runtime.getRuntime().totalMemory()/1024); 
     } 
    } 
} 
} 

然而,當我在服務器環境中運行這個類,內存使用量永遠不會改變!

快速挖掘javax.imageio包的源代碼表明掃描檢查服務提供程序是否已經註冊,如果是這樣,它在註冊新提供程序之前註銷舊提供程序。所以現在的問題是:爲什麼我有這個巨大的服務提供商鏈接列表?爲什麼他們存儲爲有向圖?更重要的是,我如何防止這種情況發生?

回答

0

看起來像一個醜陋的內存泄漏給我,我不能猜測它在哪裏沒有看整個代碼,但我知道你應該非常仔細地檢查。

可能的原因:

_Global變量列表,添加元素,永遠不會刪除它們。

_Infinite /大循環,將大量元素加載到列表中,從不刪除它們。

此外,如果您使用的是Java剖析它將幫助,如VisualVM

如果你爲了擁有它是如何工作我一個更好的主意,我也許能改善我的答案提供更多的信息,沒有什麼我可以沒有更多的信息=)

希望它有幫助!

+0

我很欣賞評論。 這顯然是內存泄漏,但這不是開發人員在我的工作場所所能夠訪問的內容;具有巨大鏈接列表的類是javax.imageio.spi包的私有包,它隨JDK一起提供。我問是否有人知道造成這種內部泄漏的原因。 我很想使用VisualVM,但這些機器上的安全策略禁止使用jstatd,所以我無法遠程連接到它們以使用VisualVM(與VisualVM一樣好)。 – Kane

+0

您能否指定jdk/jre版本?我想重現錯誤 –

+0

Sun JDK 1.6.0_33,在Fedora 12盒子上運行。 – Kane

3

晚回答一個老問題,但無論如何:

因爲ImageIO插件註冊表(該IIORegistry)是「VM全局」,默認情況下它的工作以及與Servlet上下文中並非如此。如果您加載WEB-INF/libclasses文件夾中的插件,這一點尤其明顯,因爲OP似乎是這樣做的。

Servlet上下文動態地加載和卸載類(每個上下文使用一個新的類加載器)。如果您重新啓動應用程序,舊的類將默認保留在內存中(因爲下一次調用scanForPlugins時,它會掃描/加載類的另一個ClassLoader,因此它們將成爲註冊表中的新實例)在測試代碼循環中,始終使用相同的ClassLoader,因此實例被替換,真正的問題從未出現)。

要解決此資源泄漏問題,我建議使用ContextListener以確保顯式刪除這些「上下文本地」插件。這裏有一個例子IIOProviderContextListener我用過幾個項目,實現動態加載和卸載ImageIO插件。