Управление использованием памяти при получении большого количества скриншотов в Java

Я разрабатываю программное обеспечение на Java, которое постоянно делает снимки экрана рабочего стола, сохраняет их в объекте BufferedImage, а затем записывает в файлы jpg. В какой-то момент программа вызывает java.lang.OutOfMemoryError: Исключение пространства кучи Java. Поскольку я делаю снимок экрана и сохраняю его как файл, мне больше не нужны предыдущие объекты 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();
}

person Paul    schedule 27.01.2012    source источник
comment
вы можете стать профессионалом и использовать встроенную функцию Java. Я еще не использовал его, но он позволяет вам использовать c/c++ в вашем java-коде, что обеспечивает более высокую производительность.   -  person L7ColWinters    schedule 27.01.2012
comment
System.gc() не гарантирует сбор мусора во время вызова, поэтому этот вариант не гарантируется. Опубликуйте свой код, который может помочь в выявлении любых утечек памяти.   -  person kosa    schedule 27.01.2012
comment
Вы вызывали flush() для буферизованного изображения?   -  person Scorpion    schedule 27.01.2012
comment
@ Скорпион нет, не видел. Как флеш помогает освободить память?   -  person Paul    schedule 27.01.2012


Ответы (2)


Вам просто нужно удалить ссылочные переменные ранее использовавшихся объектов BufferedImage. Тогда сборщик мусора будет автоматически освобождать занимаемую ими память, когда это необходимо.

Поскольку вы постоянно делаете снимки экрана, я думаю, что у вас будет своего рода зацикливание, и вы будете создавать только новые переменные, но ранее использованные переменные также будут присутствовать в памяти, поскольку их ссылка все еще существует.

Поэтому попробуйте удалить их ссылочные переменные. Затем, когда память будет почти заполнена, Java Garbage Collector автоматически вступит в игру и освободит вашу память для вас.

person Community    schedule 27.01.2012
comment
Что вы подразумеваете под удалением ссылочных переменных? Основываясь на коде, который я только что опубликовал, поскольку я вызываю метод getScreenShot() и передаю его в качестве параметра другому методу, я думаю, что все переменные в методе getScreenShot(), а также созданный объект BufferedImage будут разыменован ... этого достаточно, чтобы разыменовать его? Или вы имеете в виду что-то другое? - person Paul; 27.01.2012
comment
@Paul Я думаю, что проблемы находятся в вашем разделе цикла while, вы создаете объекты изображения IIOImage на каждой итерации. Таким образом, он будет продолжать добавлять новые переменные вместе с ранее использованными переменными. Попробуйте использовать только одну переменную (объявив IIOImage вне цикла). - person gprathour; 27.01.2012

Да, когда использование переменной завершено, скажите сборщику мусора, что она больше не используется. Вы можете сказать это, сославшись на объект как на null.

В вашем случае после этой строки

writer.write(null, image, iwp); 

попробуйте добавить эту строку

image=null;
person Jayy    schedule 27.01.2012
comment
Какой размер кучи указан по умолчанию? Я предполагаю, что ваша максимальная куча позволяет только один скриншот данных. С вашим текущим кодом в данный момент времени у вас будет 2 данных скриншотов. - person Jayy; 27.01.2012