在Java中获取多个屏幕截图时如何管理内存使用

3
我正在使用Java开发一款软件,它可以不断地对桌面截屏并将其保存在BufferedImage对象中,然后写入jpg文件。但是,在某个时刻,程序会导致java.lang.OutOfMemoryError: Java heap space异常。由于我已经将屏幕截图保存为文件,所以我不再需要内存中的以前的BufferedImage对象,因此可以将它们丢弃。但Java似乎无法识别这些对象,所以它们可能仍然存在于内存中。有没有办法从JVM的内存中删除这些对象,以便它不会耗尽内存?连续调用System.gc()可能没有帮助。有什么想法吗?
以下是参考代码:
public BufferedImage getScreenShot() {

    BufferedImage screenImage = null;
    try {

        GraphicsEnvironment ge= GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] screens=ge.getScreenDevices();
        Rectangle allScreenBounds=new Rectangle();
        for(GraphicsDevice screen: screens)
        {
            Rectangle screenbounds = screen.getDefaultConfiguration().getBounds();
            allScreenBounds.width+=screenbounds.width;
            allScreenBounds.height=Math.max(allScreenBounds.height, screenbounds.height);
        }


        Robot robot = new Robot();
        screenImage = robot.createScreenCapture(allScreenBounds);
    } catch (Exception ex) {
        Logger.getLogger(Screen.class.getName()).log(Level.SEVERE, null, ex);
    }
    return screenImage;
}

然后,我只需使用一个循环不断地调用该方法来生成BufferedImage对象,将其调整大小,并像这样写入文件:

while(true){
  FileImageOutputStream out = new FileImageOutputStream(f);
  writer.setOutput(out);
  IIOImage image = new IIOImage(ImageUtils.resize(getScreenShot(), width, height),null, null);
  writer.write(null, image, iwp);
  writer.dispose();
}

你可以选择专业,并使用本地的Java函数。我还没有使用过它,但它允许您在Java代码中使用C/C++,从而实现更高的性能。 - L7ColWinters
System.gc() 不保证在调用时进行GC,因此该选项不被保证。请发布您的代码,这可能有助于发现任何内存泄漏。 - kosa
你在缓冲图像上调用了flush()方法吗? - Scorpion
@Scorpion 没有,flush 如何帮助释放内存? - Paul
2个回答

5
你只需要删除先前使用的BufferedImage对象的引用变量。然后,当需要时,垃圾收集器将自动释放它们占用的内存。
由于你不断地截屏,所以我认为你会有某种循环操作,并且你只会创建新的变量,但早期使用的变量仍将存在于内存中,因为它们的引用仍然存在。
因此,请尝试删除它们的引用变量。然后,当内存几乎满时,Java垃圾收集器将自动发挥作用,并为您释放内存。

什么是移除引用变量的意思?根据我刚刚发布的代码,由于我正在调用getScreenShot()方法并将其作为参数传递给另一个方法,我想所有在getScreenShot()方法中的变量以及生成的BufferedImage对象都将被取消引用...这样就足够取消引用了吗?还是你指的是其他什么东西? - Paul
@Paul 我认为问题出在你的 while 循环部分,你在每次迭代中都创建 IIOImage 图像对象。因此,它将继续添加新变量,并保留先前使用过的变量。尝试仅使用一个变量(在循环外声明 IIOImage)。 - gprathour

1

是的,当变量的使用完成后,请告诉垃圾收集器它不再被使用。您可以通过将对象引用设置为空来表达这一点。

在您的情况下,在此行之后

writer.write(null, image, iwp); 

尝试添加这行代码

image=null;

你的默认堆大小是多少?我猜你的最大堆只允许存储一张数据截图。使用你当前的代码,在某个时间点,你将有两张数据截图。 - Jayy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接