Java 应用程序肆意消耗内存

Java application recklessly consuming memory

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

当我第一次 运行 应用程序时,任务管理器会报告如下值: 然而,仅仅大约 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,原因不明。我没有更改代码中的任何内容,它只是停止执行。

/** 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
    }
}

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

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

The program stopped growing to 300MB after a few seconds for no apparent reason. I had not changed anything in the code, it just stopped doing it.

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

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