【问题标题】:Memory Leak(?) when displaying images in java在java中显示图像时出现内存泄漏(?)
【发布时间】:2014-11-05 14:57:05
【问题描述】:

今天我有点在 Java 中摆弄图像打开/缩放/显示,并编写了一些代码来打开一个图像文件,随机缩放它并在短时间内显示它。

问题是:显示100-1000次后,我的“javaw.exe”使用的内存越来越大,甚至达到了1 GB的内存空间。

我不知道我的代码中的内存泄漏在哪里,因为唯一吃内存的东西是我的图片,并且只有 2 个(原始图像和正在缩放的​​图像,它总是分配给同一个变量(temp ) 所以“旧”的应该被 GC 挑选出来),也许你们可以看看它,它很简单。

1) 您从硬盘驱动器中选择图像

2) 随机缩放

3) 它会显示一小段时间然后消失

4) 转到 2)

为了缩放图像,我使用了这个库:http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;

import org.imgscalr.Scalr;

public static void main(String[] args) throws IOException, InterruptedException {


    JFileChooser chooser = new JFileChooser();
    chooser.showOpenDialog(null);

    BufferedImage originalImage = ImageIO.read(chooser.getSelectedFile());
    BufferedImage temp;


    while(true){

        int width = (int) ((Math.random()*1000)+1); 
        int height = (int) ((Math.random()*1000)+1);

        Thread.sleep(1000);

        temp = Scalr.resize(originalImage,Scalr.Mode.FIT_EXACT, width, height);


        showImage(temp, 800);

    }

}

static void showImage(BufferedImage v,long length) throws InterruptedException {


    JFrame frame = new JFrame();
    frame.add(new JLabel(new ImageIcon(v)));
    frame.setSize(v.getWidth(), v.getHeight());

    frame.setVisible(true);
    Thread.sleep(length);
    frame.setVisible(false);


}

这是我第一次在这里发帖,如果我不清楚,请提出问题

提前致谢!

编辑:我监控了 javaw.exe 需要的内存

显示1张图片:75M 100张显示:330M 1000张图片显示:2,4G

编辑 2:

我现在已经采纳了您的有用建议,但我的内存量仍在增长,我的图像不再显示。JFrames 是空的。

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

import org.imgscalr.Scalr;

public class App {

    public static void main(String[] args) throws IOException, InterruptedException {


        JFileChooser chooser = new JFileChooser();
        chooser.showOpenDialog(null);

        BufferedImage originalImage = ImageIO.read(chooser.getSelectedFile());
        BufferedImage temp;

        JFrame frame = new JFrame();


        while(true){

            int width = (int) ((Math.random()*1000)+1); 
            int height = (int) ((Math.random()*1000)+1);

            Thread.sleep(1000);

            temp = Scalr.resize(originalImage,Scalr.Mode.FIT_EXACT, width, height);


            showImage(temp, 500, frame);

        }

    }

    static void showImage(BufferedImage v,long length, JFrame frame) throws InterruptedException {

        SwingUtilities.invokeLater(
                () -> {


                    frame.removeAll();
                    frame.revalidate();
                    frame.repaint();

                    frame.add(new JLabel(new ImageIcon(v)));
                    frame.setSize(v.getWidth(), v.getHeight());

                    frame.setVisible(true);
                    try {
                        Thread.sleep(length);
                    } catch (Exception e) {}
                    frame.setVisible(false);

                    frame.dispose();
                });



    }

}

也许我把你的建议放在了我的代码中的错误位置。

【问题讨论】:

  • 你给 Java 多少内存?它最终会耗尽内存,还是最终会启动垃圾收集?
  • 您在循环中创建新框架,但从不关闭它们(您只是使它们不可见),并且每个框架都有一个调整大小的图像。你应该得到内存泄漏。只要这些框架存在,垃圾收集就永远不会发生。另外,你在主线程上。这永远不会奏效,你最终会耗尽内存并使 VM 崩溃。
  • 您可能需要修改 vm 配置文件并为其提供更多内存。
  • @hagubear 这个问题与VM内存无关。该代码只是隐藏 JFrames 和其中的图像。这是一个应用程序逻辑问题。没有任何东西会被垃圾收集。
  • 啊,我什至没有看到 showImage() 方法。尝试调用 frame.dispose() 而不是 frame.setVisible(false)。这样一来,框架的资源就被释放了,而不仅仅是隐藏了。

标签: java image memory-leaks


【解决方案1】:

下面的代码应该可以满足您的需求。我使用了 Timer 而不是 Thread.sleep。您正在占用 EDT。我也只是在容器中绘制图像。您可能应该改用 JPanel(将其添加到 JFrame 并覆盖其 paintComponent 方法)。我还清理了一些方法。

import java.awt.image.BufferedImage;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Graphics;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.imgscalr.Scalr;

public class App extends JFrame implements ActionListener{

        BufferedImage originalImage = null;
        BufferedImage temp = null;
        JFileChooser chooser = null;

        public App(){

                setVisible(true);
        }

        public static void main(String[] args) throws IOException, InterruptedException {


                SwingUtilities.invokeLater(
                                () -> {
                                        App app = new App();

                                        Timer timer = new Timer(1000, app);
                                        timer.start();

                                });

        }

        @Override
        public void actionPerformed(ActionEvent ae){
                if(null == chooser){
                        chooser = new JFileChooser();
                        chooser.showOpenDialog(this);
                        loadImage();
                }
                showImage();
                repaint();
        }

        @Override
        public void paint(Graphics g){
                super.paint(g);
                if(null == temp){
                        return;
                }
                g.drawImage(temp, 0, 0, null);
        }

        public void loadImage(){

                try{
                        originalImage = ImageIO.read(chooser.getSelectedFile());
                } catch(IOException ioe){
                        ioe.printStackTrace();
                }
        }

        public void showImage() {
                int width = (int) ((Math.random()*1000)+1); 
                int height = (int) ((Math.random()*1000)+1);
                temp = Scalr.resize(originalImage,Scalr.Mode.BEST_FIT_BOTH, width, height);
                setSize(width, height);
        }
}

【讨论】:

  • 是的,这正是我想要做的。非常感谢!
【解决方案2】:

你可以试试

originalImage.flush();
originalImage = null;
temp.flush();
temp = null;

但无法保证您的图像何时会被垃圾回收

除此之外,您还应该考虑清除和重用同一个 JFrame。

removeAll();//or remove the previous JLabel
revalidate();
repaint();

另外,显示 JFrame 的正确方法是使用 SwingUtilities 的 invokeLater 方法确保此“作业”放置在事件调度线程 (EDT) 上。

 // schedule this for the event dispatch thread (edt)
 SwingUtilities.invokeLater(yourJFrame);

【讨论】:

  • 这行不通,因为他保留了 JFrame。那些需要清理。它在主应用程序线程上。这也需要解决。如果你能解决所有问题,我会投票赞成。
  • @KaterinaA。好的,感谢您的帮助,但我目前不清楚在哪里插入这个有用的代码。我编辑了我的原始帖子以显示我的程序的新版本
猜你喜欢
  • 1970-01-01
  • 2013-05-10
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多