Java Swing: читать много файлов изображений без проблем с памятью? - PullRequest
2 голосов
/ 02 ноября 2009

Я пишу приложение Java Swing, которое будет загружать с диска около 1500 изображений PNG размером от 50 до 75 КБ каждое. Мне не нужно загружать их все сразу, но мне нужно будет загружать до 50 изображений одновременно. Я пытался загрузить их, используя типичные:

new javax.swing.ImageIcon(getClass().getResource("myimage.jpeg")

но мое приложение зависает, и мне не хватает памяти примерно после первых 30 изображений или около того.

Каков наилучший способ загрузки этих изображений таким образом, чтобы я не перегружал jvm и чтобы я мог отслеживать успешно загруженные изображения? Если бы это было возможно и необходимо, я бы не возражал, если бы приложение показывало экран «загрузка ...» во время загрузки изображений, но я не уверен, как это сделать.

Кэширование здесь полезно? Я не совсем понимаю, но я видел этот вопрос об использовании MediaTracker, и я не уверен, как это можно реализовать здесь.

Ответы [ 5 ]

3 голосов
/ 02 ноября 2009

Почему бы не создать объект-оболочку для каждого изображения, загрузить их по требованию и использовать WeakReference с или SoftReference с. Таким образом, сборщик мусора может при необходимости скопировать данные изображения, и вы можете перезагрузить как / когда слабая ссылка очищена.

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

3 голосов
/ 02 ноября 2009

Если вы уже знаете, сколько .png вы собираетесь загрузить, вы можете создать массив ImageIcon и загружать их по одному из каталога / каталогов (что позволит вам отобразить экран загрузки ...) ,

То, что я должен сделать , я думаю, это увеличение мин / макс. HeapSize JVM при запуске приложения. Вы можете указать их, например, добавление -Xmx256m в качестве параметра (это устанавливает максимальную кучу равной 256 МБ) (и, возможно, -Xms32m [это устанавливает минимальную кучу равной 32 МБ]), см. http://docs.sun.com/source/817-2180-10/pt_chap5.html#wp57033

Вы добавите эти параметры либо при запуске приложения (например, "java -jar myApp.jar -Xmx128m"), либо в файл jvm-конфигурации вашей системы, либо в свойства сборки вашего проекта.

Этот фрагмент кода будет загружать весь каталог; если вы хотите, чтобы загружалось только 50 изображений, просто поиграйте с параметрами start и stop.
Как уже было сказано, вам нужно установить максимальную кучу (-Xmx) примерно на 300 МБ (например, разрешение 1280x1024 -> 1310720px -> 4 байта / пиксель -> 5242880 байт -> 50 изображений -> 256 МБ).

File dir = new File("/path/to/directory");
File[] files = dir.listFiles();
BufferedImage[] images = new BufferedImage[files.length];
for (int i = 0; i < files.length; i++)
{
   try
   {
     images[i] = ImageIO.read(files[i]);
   } catch (IOException ex){}
}
1 голос
/ 07 ноября 2009

Как сказал Тедил, вы должны дать больше памяти приложению, запустив что-то вроде:

java -Xmx256m -classpath yourclasspath YourMainClass

Чтобы загрузить изображения с помощью экрана загрузки «Пожалуйста, подождите» и индикатор выполнения достаточно сложен. Это уже в сфере Advanced Swing. Если вы используете Java 6, я рекомендую прочитать класс SwingWorker.

Вот демонстрация, которая показывает вам один подход:

package com.barbarysoftware;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.List;

public class ImageLoadingDemo {

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setPreferredSize(new Dimension(600, 400));
        frame.getContentPane().add(new JLabel("I'm the main app frame", JLabel.CENTER));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        final JDialog pleaseWaitDialog = new JDialog(frame, "Loading images", true);

        final int imageCount = 50;
        final JProgressBar progressBar = new JProgressBar(0, imageCount);

        final BufferedImage[] images = loadImages(frame, pleaseWaitDialog, imageCount, progressBar);
        System.out.println("images = " + images);
    }

    private static BufferedImage[] loadImages(JFrame frame, final JDialog pleaseWaitDialog, final int imageCount, final JProgressBar progressBar) {
        final BufferedImage[] images = new BufferedImage[imageCount];
        SwingWorker<Void, Integer> swingWorker = new SwingWorker<Void, Integer>() {
            @Override
            protected Void doInBackground() throws Exception {
                for (int i = 0; i < imageCount; i++) {
                    System.out.println("i = " + i);
                    publish(i);
                    Thread.sleep(1000); // to simulate the time needed to load an image
//                    images[i] = ImageIO.read(new File("... path to an image file ..."));
                }
                return null;
            }

            @Override
            protected void process(List<Integer> chunks) {
                final Integer integer = chunks.get(chunks.size() - 1);
                progressBar.setValue(integer);
            }

            @Override
            protected void done() {
                pleaseWaitDialog.setVisible(false);
            }
        };
        JPanel panel = new JPanel();
        panel.add(progressBar);
        panel.add(new JButton(new AbstractAction("Cancel") {
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        }));
        pleaseWaitDialog.getContentPane().add(panel);
        pleaseWaitDialog.pack();
        pleaseWaitDialog.setLocationRelativeTo(frame);
        swingWorker.execute();
        pleaseWaitDialog.setVisible(true);
        return images;
    }
}
0 голосов
/ 02 ноября 2009

Что вы собираетесь делать с изображениями? Вам действительно нужны экземпляры ImageIcon, или Image тоже подойдет? В любом случае вам будет проще контролировать загрузку, если вы будете использовать синхронные методы в ImageIO вместо конструкторов ImageIcon.

Если вы столкнулись с OutOfMemoryErrors уже после 30 изображений, я предполагаю, что изображения могут иметь высокое разрешение и / или глубину цвета, даже если они относительно малы на диске. Когда вы загружаете изображение, оно распаковывается и требует гораздо больше памяти (обычно 4 * ширина * высота байтов для цветного изображения), чем размер сжатого файла. Если изображения не очень маленькие, вы, вероятно, не сможете кэшировать 1500 несжатых изображений в пределах разумного объема памяти, поэтому вам придется реализовать некоторую стратегию, чтобы загружать только те изображения, которые вам нужны в данный момент.

0 голосов
/ 02 ноября 2009

Вы должны быть очень осторожны с загрузкой файлов через загрузчик классов, поскольку эти ресурсы не освобождаются, пока загрузчик классов активен.

Вместо этого используйте другой подход для загрузки файлов непосредственно с диска, используя объект java.io.File. Затем их можно выбросить, не оставляя невидимым вокруг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...