我正在编写一个Java Swing应用程序,它将从磁盘加载大约1500 png的图像,每个图像的大小从50kb到75kb不等。我不需要一次加载所有这些,但我需要一次加载50个图像。我一直在尝试使用典型的加载它们:
new javax.swing.ImageIcon(getClass().getResource("myimage.jpeg")
但是我的应用程序冻结了,在大约前30张图像后我的内存耗尽。
以这样的方式加载这些图像的最佳方法是什么?我不会重载jvm并且我将能够监视到目前为止已成功加载哪些?如果可能和必要,我不介意应用程序在加载图像时显示“加载...”屏幕,但我不知道该怎么做。
缓存有用吗?我不太明白,但我看到this question关于使用MediaTracker,我不知道如何在这里实现。
答案 0 :(得分:3)
如果您已经知道要加载多少个.png,您可能需要创建一个ImageIcon数组并从目录/目录中逐个加载它们(这将允许您显示加载...屏幕) 。
我认为应该做的是增加最小值/最大值。运行应用程序时JVM的HeapSize。您可以通过例如指定它们添加-Xmx256m作为参数(这会将max-heap设置为256MB)(也许-Xms32m [这会将最小堆设置为32mb])请参阅http://docs.sun.com/source/817-2180-10/pt_chap5.html#wp57033
您可以在启动应用时添加这些选项(例如“java -jar myApp.jar -Xmx128m”),也可以添加到系统的jvm-configuration-file或项目的构建属性中。
这段代码会加载整个目录;如果您只想加载50张图像,只需摆弄启动和停止参数即可 如前所述,您必须将max-heap(-Xmx)设置为大约300M(例如1280x1024的分辨率 - > 1310720px - > 4字节/像素 - > 5242880字节 - > 50图像 - > 256MB)。
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 :(得分:3)
为什么不为每个图像创建一个包装器对象,按需加载它们,并使用WeakReference或SoftReference s。这样,垃圾收集器可以在必要时对图像数据进行分类,并且可以在弱引用被清除时重新加载。
好处是,当需要其他用途时,可以清除图像的内存。缺点是您必须在显示之前重新加载图像(或者您对图像所做的任何事情)。
答案 2 :(得分:1)
正如Tedil所说,你应该通过启动以下内容为应用程序提供更多内存:
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;
}
}
答案 3 :(得分:0)
通过类加载器加载文件时应该非常小心,因为在类加载器处于活动状态时不会释放这些资源。
而是使用另一种方法使用java.io.File对象直接从磁盘加载文件。然后可以丢弃它而不会无形地放置。
答案 4 :(得分:0)
你打算怎么处理这些照片?你真的需要ImageIcon实例,还是Image也会这样做?在任何一种情况下,如果使用ImageIO中的同步方法而不是ImageIcon构造函数,则可能更容易控制加载。
如果在30张图像之后已经遇到OutOfMemoryErrors,我认为图像可能具有高分辨率和/或颜色深度,即使它们在磁盘上相对较小。加载图像时,图像被解压缩,并且需要比压缩文件的大小更多的内存(通常为彩色图像的4 *宽*高度字节)。除非图像非常小,否则您可能无法在合理数量的内存中缓存1500个未压缩的图像,因此您必须实施一些策略才能加载当前所需的图像。