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 源代码...... )
对于我正在进行的项目,我的任务是创建一种将图像转换为非加密哈希的方法,以便可以轻松地将其与类似图像进行比较,但是我 运行 遇到了问题尽管 Java 监控和管理控制台未报告内存消耗有任何增加,但 JVM 会开始肆无忌惮地消耗内存。
当我第一次 运行 应用程序时,任务管理器会报告如下值:
我使用 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 源代码...... )