【问题标题】:Java application recklessly consuming memoryJava应用程序肆意消耗内存
【发布时间】:2015-06-07 01:47:31
【问题描述】:

对于我正在进行的一个项目,我的任务是创建一种将图像转换为非加密哈希的方法,以便可以轻松地将其与类似图像进行比较,但是我遇到了 JVM 开始处理的问题鲁莽地消耗内存,尽管 Java 监控和管理控制台没有报告内存消耗有任何增加。

当我第一次运行应用程序时,任务管理器会报告如下值: 然而,仅在大约 30 秒后,这些值就会增加一倍或三倍。

我使用 JMMC 创建了进程的转储,但它只报告了大约 1.3MB 的使用量:

对我来说最奇怪的部分是应用程序执行一个持续大约 15 秒的操作,然后等待 100 秒(调试),并且在线程休眠的 100 秒期间,使用的内存翻了一番。

这是我的两个课程:

ImageHashGenerator.java

package com.arkazex.srcbot;

import java.awt.Color;
import java.awt.Image;
import java.awt.image.BufferedImage;

public class ImageHashGenerator {

    public static byte[] generateHash(Image image, int resolution) {
        //Resize the image
        Image rscaled = image.getScaledInstance(resolution, resolution, Image.SCALE_SMOOTH);
        //Convert the scaled image into a buffered image
        BufferedImage scaled = convert(rscaled);
        //Create the hash array
        byte[] hash = new byte[resolution*resolution*3];
        //Variables
        Color color;
        int index = 0;
        //Generate the hash
        for(int x = 0; x < resolution; x++) {
            for(int y = 0; y < resolution; y++) {
                //Get the color
                color = new Color(scaled.getRGB(x, y));
                //Save the colors
                hash[index++] = (byte) color.getRed();
                hash[index++] = (byte) color.getGreen();
                hash[index++] = (byte) color.getBlue();
            }
        }
        //Return the generated hash
        return hash;
    }

    //Convert Image to BufferedImage
    private static BufferedImage convert(Image img) {
        //Create a new bufferedImage
        BufferedImage image = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_3BYTE_BGR);
        //Get the graphics
        image.getGraphics().drawImage(img, 0, 0, null);
        //Return the image
        return image;
    }
}

Test.java

package com.arkazex.srcbot;

import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Test {

    public static void main(String[] args) throws IOException {
        //Create a hash
        byte[] hash = ImageHashGenerator.generateHash(ImageIO.read(new File("img1.JPG")), 8);    //Memory grows to around 150MB here
        System.out.println(new String(hash));
        try{ Thread.sleep(100000); } catch(Exception e) {}    //Memory grows to around 300MB here
    }
}

编辑:该程序在几秒钟后停止增长到 300MB,原因不明。我没有更改代码中的任何内容,它只是停止了它。

【问题讨论】:

  • “Reckless”的意思是“我不知道它为什么要抢夺那段记忆”?除非它不断增长以填充最大空间,否则我不会将其称为“泄漏”本身。我倾向于 (a) 将其归结为 JVM 执行 JVM 所做的事情,或者 (b) 使用分析工具深入挖掘。
  • 程序运行得怎么样?
  • @immibis 我在 Eclipse 中运行它,但即使我尝试将它作为导出的 jar 文件运行,它仍然存在过度消耗问题。
  • 进行堆转储并运行 eclipse MAT
  • “我没有更改代码中的任何内容,它只是停止了它。” -- 嗯......它可能只是垃圾收集:-)

标签: java memory memory-management memory-leaks


【解决方案1】:

我认为您在这里缺少的是某些图像类使用堆外内存。这对 JMMC 来说(可能)是不可见的,因为它只被告知堆上的使用情况。操作系统级别的内存使用监控会看到它......因为它正在查看运行您的应用程序的 JVM 的总资源消耗。

问题是堆外内存块只有在相应的堆上图像对象最终确定时才会被回收。只有当它们被垃圾回收时才会发生这种情况。

程序在几秒钟后无缘无故停止增长到 300MB。我没有更改代码中的任何内容,它只是停止了它。

我希望 JVM 决定是时候进行一次完整的 GC(或类似的事情),这会导致它在堆外内存池中释放大量空间。这意味着 JVM 不再需要继续增长池。

(我故意含糊其辞,因为我实际上并不知道现代 JVM 中堆外内存分配是如何工作的。但如果你想研究,可以下载 JVM 源代码......)

【讨论】:

    【解决方案2】:

    /** comments */中的解释

    public class Test {
    
        public static void main(String[] args) throws IOException {
            //Create a hash
    
            /** Here it allocates (3 * resolution^2 )bytes of memory to a byte array */
            byte[] hash = ImageHashGenerator.generateHash(ImageIO.read(new File("img1.JPG")), 8);    //Memory grows to around 150MB here
    
            /** And here it again allocates the same memory to a String 
            Why print a String of 150 million chars? */
            System.out.println(new String(hash));
            try{ Thread.sleep(100000); } catch(Exception e) {}    //Memory grows to around 300MB here
        }
    }
    

    【讨论】:

    • 即使我删除了System.out.println(new String(hash)); 代码,应用程序仍然消耗相同数量的内存。 (由任务管理器报告)
    • @NicholasHollander 好的,那不是问题,感谢您的检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2013-07-17
    • 2015-04-01
    • 2016-10-08
    相关资源
    最近更新 更多