簡體   English   中英

java imageio內存泄漏

[英]java imageio memory leak

我有兩個Java應用程序都使用大量內存,並且都使用ImageIO.write()。 到目前為止,這是我在兩者之間找到的唯一共識。

一個循環中的圖像大小調整。 另一個循環下載圖像並將其保存到磁盤。 這是相關的代碼:

1)

for(File imageFile : imageFilesList)
{
    if(!stillRunning) return;

    File outputFile = new File(imageFile.getAbsolutePath().replace(sourceBaseFolder.getAbsolutePath(), destinationFolder.getAbsolutePath()));
    try
    {
        outputFile.mkdirs();
        BufferedImage inputImage = ImageIO.read(imageFile);
        BufferedImage resizedImage = ImageResizer.resizeImage(inputImage, maxHeight, maxWidth);
        ImageIO.write(resizedImage, "jpg", outputFile);
    }
    catch(IOException ex)
    {
        userInterface.displayMessageToUser("IOException ocurred while converting an image: " + ex.getLocalizedMessage());
        System.out.println(outputFile.getAbsolutePath());
        ex.printStackTrace();
        return;
    }
    imagesConverted++;
    userInterface.updateTotalConvertedImages(++convertedFiles);
}

2)(在循環內)

try
{
    u = new URL(urlString);
    uc = u.openConnection();
    uc.addRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    uc.connect();
    uc.getInputStream();
    in = uc.getInputStream();

    BufferedImage tempImage = ImageIO.read(in);

    String fileName = fn = ImageDownload.getFileName(u.getPath());
    fileName = outputDirString + FILE_SEPARATOR + fileName;
    while (new File(fileName).exists())
    {
        fileName = appendCopyIndicator(fileName);
    }

    ImageIO.write(tempImage, "jpg", new File(fileName));
    parent.notifyOfSuccessfulDownload(fn);
    in.close();
}
catch (FileNotFoundException ex)
{
    parent.notifyOfFailedDownload(fn);
}
catch (IOException ex)
{
    parent.handleException(ex);
}

在這兩種情況下,程序都使用大量內存。 就在一堆RAM之前。 並且當循環結束時它不會被釋放。 在這兩種情況下,我都有一個搖擺gui跑。 當圖像保存完成並且gui剛剛空閑時,程序仍然使用有時1Gb +的內存。 我已經把循環之后沒有被swing gui直接使用的每個變量設置為null 沒有效果。

我錯過了什么?

在此先感謝您的幫助。

更多信息:我只是在我的IDE(Netbeans)中分析了應用程序1。 我選擇了應用程序一,因為它只處理ImageIO(而不是網絡IO),因此它是一個更受控制的實驗。

當程序正在做它的事情(在循環中調整圖像大小)時,總內存在大約900,000,000~1,000,000,000字節之間徘徊,而使用的內存在給定時刻使用的總內存的大約30%到50%之間流動。

在GC中花費的時間從未超過1%。

一旦實際調整大小完成並且程序進入“空閑”狀態,就會發生兩件事:1)總內存停止流動並保持靜態為1,044,054,016字節,以及2)已用內存下降到~14,000,000字節(14 mb) 。

因此,看起來JVM不會回饋它不再使用的內存空間。

同意? 或者我誤讀了這個結果?

我有類似的東西,當我查看分配的內存時,我看到幾個大塊的內存(每個約10-20 MB)將無法釋放。 使用VirtualVM內存轉儲查看它們內部,似乎它們被imageIO JpegImageReader標記為“擁有”。 在這種情況下,GC不會清除它們,因為它們被標記為外部GNI調用(JpegImageReader)使用。 並且theJpegImageReader早已不復存在,所以他們會留在那里。

就我而言,它是隨機發生的。 我懷疑當有多個並行調用(來自多個線程)時,或者當ImageReader中存在內部異常時,它不會釋放其內存,但不是100%肯定。 (是的,我會刷新緩沖的圖像,處理讀取器,關閉流。似乎沒有幫助)。

我錯過了什么? 了解Java垃圾收集的工作原理;-)

內存不會立即釋放,只有在垃圾收集運行后才會釋放。 如果您願意,可以明確地調用GC。 你的操作系統也起了作用 - 例如,即使你釋放它,用Unix系統通常也不會釋放內存(用C ++術語)。

你應該在BufferedImage上使用.flush()不再可用。
例如,在您的第二個代碼中:

try
{
    u = new URL(urlString);
    uc = u.openConnection();
    uc.addRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    uc.connect();
    uc.getInputStream();
    in = uc.getInputStream();

    BufferedImage tempImage = ImageIO.read(in);

    String fileName = fn = ImageDownload.getFileName(u.getPath());
    fileName = outputDirString + FILE_SEPARATOR + fileName;
    while (new File(fileName).exists())
    {
        fileName = appendCopyIndicator(fileName);
    }

    ImageIO.write(tempImage, "jpg", new File(fileName));

    // release internal buffered memory
    tempImage.flush();

    parent.notifyOfSuccessfulDownload(fn);
    in.close();
}

我相信你在#1和#2的兩種情況下都缺少Image#flush()。 我建議你在finally子句中調用flush()。 在#2中,為了更安全,請在finally子句中調用in.close(),因為如果在該行之前遇到異常,則很可能不會根據您粘貼的代碼段關閉它。

暫無
暫無

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

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