Java中图像缩放的内存问题 [英] Memory issue with image zooming in java

查看:89
本文介绍了Java中图像缩放的内存问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

环境:

JDK:1.8u112甲骨文

JDK: 1.8u112 oracle

JRE:10.0.2

JRE: 10.0.2

JVM最大堆大小:〜2GB.

JVM max heap size: ~2GB.

操作系统:Windows 10

OS: Windows 10

IDE:Netbeans 8.1

IDE: Netbeans 8.1

RAM:DDR4 8GB

RAM: DDR4 8GB

处理器:6700hq i7英特尔

Processor: 6700hq i7 intel

上下文

一个简单的GUI,可以打开图像文件(jpg/png)并通过用户输入将其放大.

A simple GUI that opens an image file (jpg/png) and magnifies it via user input.

描述

一个类扩展了JFrame.框架的contentPane具有JButton,JLabel&一个JScrollPane.单击该按钮将显示一个JFileChooser.标签在滚动窗格内.选择一个文件会在标签中将其打开(仅出于测试此问题jpg/png的目的打开图像文件).标签具有鼠标滚轮监听器,可通过Image.getScaledInstance缩放图像.每次缩放时,都会打印放大倍率(新图像宽度(或高度)与相应原始图像的比率)和Runtime.totalMemory.

A class extends JFrame. The frame's contentPane has a JButton,a JLabel & a JScrollPane. Clicking the button shows a JFileChooser. The label is inside the scrollpane. Selecting a file opens it in the label(open image files only for the purposes of this question-jpg/png tested upon). The label has a mouse wheel listener that causes zooming of image via Image.getScaledInstance. At each zoom, magnifiaction (ratio of new image width(or height) to corresponding original's) and Runtime.totalMemory is printed.

问题

  1. 放大图像时,代码似乎占用了过多的内存.对于7.23KB的png图像,任务管理器以11.8倍的放大倍数显示了1708 MB的内存使用情况.预期应为11.8 * 11.8 * 7.23KB左右
  2. 缩小时,内存消耗不会减少
  3. 为什么堆首先扩展得如此之大(约为mag的17倍,达到2GB)? 丢弃的ImageIcon对象(参见代码)是否没有被赋值?
  4. 在mag * mag * originalImageSize(以字节为单位)< 50%JVM最大堆大小的情况下,如何使代码对mag可行?
  1. Upon zooming into the image, too much memory seems to being consumed by the code. The task manager shows 1708 MB memory usage at 11.8 times magnification for a 7.23KB png image. Expected should be around the order of 11.8*11.8*7.23KB
  2. Upon zooming out, the memory consumption doesn't reduce
  3. Why is the heap expanding so much(at around ~17 times mag, it reaches 2GB) in the first place? Are discarded ImageIcon objects(see code) not being gced?
  4. How to make code viable for mag where mag * mag * originalImageSize(in bytes)<50% JVM max heap size?

代码

import java.awt.Dimension;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class gui extends javax.swing.JFrame {

Image image;
Dimension size;
private double mag = 1;
Runtime runtime = Runtime.getRuntime();

public gui() {
    initComponents();

}

private void zoom() {

    int[] newSize = {(int) (size.width * mag), (int) (size.height * mag)};

    if (newSize[0] > 0 && newSize[1] > 0) {
        label.setIcon(new ImageIcon(image.getScaledInstance(newSize[0], newSize[1], Image.SCALE_DEFAULT)));
    }

    System.out.println("mag:" + (int) (mag * 100) + "% mem:" + runtime.totalMemory() / 1024 / 1024 + "MB");

}

private void loadImage(File imgFile) throws IOException {

    String path = imgFile.getPath().toLowerCase();
    if (path.endsWith("gif")) {
        ImageIcon icon = new ImageIcon(path);

        image = icon.getImage();

        label.setIcon(icon);

    } else {
        image = ImageIO.read(imgFile);
        ImageIcon icon = new ImageIcon(image);
        label.setIcon(icon);
    }

    size = new Dimension(image.getWidth(null), image.getHeight(null));

}

@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    dialog = new javax.swing.JFileChooser();
    jScrollPane1 = new javax.swing.JScrollPane();
    label = new javax.swing.JLabel();
    button = new javax.swing.JButton();

    dialog.setCurrentDirectory(new java.io.File("D:\\"));
        dialog.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                dialogActionPerformed(evt);
            }
        });

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Image Viewer");

        label.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        label.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        label.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
                labelMouseWheelMoved(evt);
            }
        });
        jScrollPane1.setViewportView(label);

        button.setText("open");
        button.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 689, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(button)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(button)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

private void dialogActionPerformed(java.awt.event.ActionEvent evt) {                                       
    // TODO add your handling code here:

    if (evt.getActionCommand().equals(JFileChooser.APPROVE_SELECTION)) {

        try {
            File file = dialog.getSelectedFile();

            loadImage(file);

            setTitle(file.getPath());
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

}                                      

private void labelMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {                                      

    if (image != null) {
        int amt = -evt.getWheelRotation();
        double newMag = mag + amt * 0.1;

        if (newMag > 0) {
            mag = newMag;
            zoom();

        }

    }


}                                     

private void buttonActionPerformed(java.awt.event.ActionEvent evt) {                                       
    // TODO add your handling code here:
    dialog.showOpenDialog(this);
}                                      

public static void main(String args[]) throws Exception {

    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {

            new gui().setVisible(true);

        }
    });
}

// Variables declaration - do not modify                     
private javax.swing.JButton button;
private javax.swing.JFileChooser dialog;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel label;
// End of variables declaration                   
}

测试文件

除了测试文件之外,任何jpg或png图像都应做.

Any jpg or png image should do besides test file.

测试文件的输出

mag:110%mem:123MB mag:120%mem:123MB mag:130%mem:123MB mag:140%mem:123MB mag:150%mem:123MB mag:160%mem:123MB mag:150%mem:123MB mag:160%mem:123MB mag:170%mem:123MB 大小:180%内存:155MB 大小:190%内存:155MB 大小:200%内存:155MB mag:210%mem:155MB 大小:220%内存:155MB mag:230%mem:157MB mag:240%mem:157MB mag:250%mem:157MB 大小:260%内存:157MB mag:270%mem:253MB mag:280%mem:253MB 大小:290%内存:253MB mag:300%mem:253MB mag:310%mem:253MB mag:320%mem:256MB mag:330%mem:256MB mag:340%mem:256MB mag:350%mem:256MB mag:360%mem:256MB mag:370%mem:393MB mag:380%mem:393MB mag:390%mem:393MB mag:400%mem:393MB mag:410%mem:393MB mag:420%mem:393MB mag:430%mem:466MB mag:440%mem:466MB mag:450%mem:466MB mag:460%mem:466MB mag:470%mem:466MB mag:480%mem:466MB mag:489%mem:541MB mag:499%mem:541MB mag:509%mem:541MB mag:519%mem:541MB mag:529%mem:541MB mag:539%mem:641MB mag:549%mem:641MB mag:559%mem:641MB mag:569%mem:641MB mag:579%mem:641MB mag:589%mem:825MB mag:599%mem:825MB mag:609%mem:825MB mag:619%mem:825MB mag:609%mem:825MB mag:619%mem:825MB mag:629%mem:892MB mag:639%mem:892MB mag:649%mem:892MB mag:659%mem:892MB mag:669%mem:892MB mag:679%mem:892MB mag:689%mem:881MB mag:699%mem:881MB mag:709%mem:881MB mag:719%mem:881MB mag:729%mem:1029MB mag:739%mem:1029MB mag:749%mem:1029MB mag:759%mem:1029MB 大小:769%内存:1104MB mag:779%mem:1104MB mag:789%mem:1104MB mag:799%mem:1104MB mag:809%mem:1075MB mag:819%mem:1075MB mag:829%mem:1075MB mag:839%mem:1182MB mag:849%mem:1182MB mag:859%mem:1182MB mag:869%mem:1289MB mag:879%mem:1289MB mag:889%mem:1542MB mag:899%mem:1542MB mag:909%mem:1542MB mag:919%mem:1569MB mag:929%mem:1569MB mag:939%mem:1569MB 大小:949%内存:1480MB mag:959%mem:1480MB mag:969%mem:1548MB mag:979%mem:1548MB 大小:989%内存:1655MB 大小:999%内存:1655MB mag:1009%mem:1707MB mag:1019%mem:1707MB mag:1029%mem:1802MB mag:1039%mem:1850MB mag:1049%mem:1850MB mag:1059%mem:1871MB mag:1069%mem:1871MB mag:1079%mem:1801MB mag:1089%mem:1862MB mag:1099%mem:1862MB mag:1109%mem:1815MB mag:1119%mem:1822MB mag:1129%mem:1758MB mag:1139%mem:1774MB mag:1149%mem:1711MB mag:1159%mem:1734MB mag:1169%mem:1676MB mag:1179%mem:1708MB mag:1189%mem:1654MB

mag:110% mem:123MB mag:120% mem:123MB mag:130% mem:123MB mag:140% mem:123MB mag:150% mem:123MB mag:160% mem:123MB mag:150% mem:123MB mag:160% mem:123MB mag:170% mem:123MB mag:180% mem:155MB mag:190% mem:155MB mag:200% mem:155MB mag:210% mem:155MB mag:220% mem:155MB mag:230% mem:157MB mag:240% mem:157MB mag:250% mem:157MB mag:260% mem:157MB mag:270% mem:253MB mag:280% mem:253MB mag:290% mem:253MB mag:300% mem:253MB mag:310% mem:253MB mag:320% mem:256MB mag:330% mem:256MB mag:340% mem:256MB mag:350% mem:256MB mag:360% mem:256MB mag:370% mem:393MB mag:380% mem:393MB mag:390% mem:393MB mag:400% mem:393MB mag:410% mem:393MB mag:420% mem:393MB mag:430% mem:466MB mag:440% mem:466MB mag:450% mem:466MB mag:460% mem:466MB mag:470% mem:466MB mag:480% mem:466MB mag:489% mem:541MB mag:499% mem:541MB mag:509% mem:541MB mag:519% mem:541MB mag:529% mem:541MB mag:539% mem:641MB mag:549% mem:641MB mag:559% mem:641MB mag:569% mem:641MB mag:579% mem:641MB mag:589% mem:825MB mag:599% mem:825MB mag:609% mem:825MB mag:619% mem:825MB mag:609% mem:825MB mag:619% mem:825MB mag:629% mem:892MB mag:639% mem:892MB mag:649% mem:892MB mag:659% mem:892MB mag:669% mem:892MB mag:679% mem:892MB mag:689% mem:881MB mag:699% mem:881MB mag:709% mem:881MB mag:719% mem:881MB mag:729% mem:1029MB mag:739% mem:1029MB mag:749% mem:1029MB mag:759% mem:1029MB mag:769% mem:1104MB mag:779% mem:1104MB mag:789% mem:1104MB mag:799% mem:1104MB mag:809% mem:1075MB mag:819% mem:1075MB mag:829% mem:1075MB mag:839% mem:1182MB mag:849% mem:1182MB mag:859% mem:1182MB mag:869% mem:1289MB mag:879% mem:1289MB mag:889% mem:1542MB mag:899% mem:1542MB mag:909% mem:1542MB mag:919% mem:1569MB mag:929% mem:1569MB mag:939% mem:1569MB mag:949% mem:1480MB mag:959% mem:1480MB mag:969% mem:1548MB mag:979% mem:1548MB mag:989% mem:1655MB mag:999% mem:1655MB mag:1009% mem:1707MB mag:1019% mem:1707MB mag:1029% mem:1802MB mag:1039% mem:1850MB mag:1049% mem:1850MB mag:1059% mem:1871MB mag:1069% mem:1871MB mag:1079% mem:1801MB mag:1089% mem:1862MB mag:1099% mem:1862MB mag:1109% mem:1815MB mag:1119% mem:1822MB mag:1129% mem:1758MB mag:1139% mem:1774MB mag:1149% mem:1711MB mag:1159% mem:1734MB mag:1169% mem:1676MB mag:1179% mem:1708MB mag:1189% mem:1654MB

推荐答案

丢弃的ImageIcon对象(请参见代码)是否未设置大小?

Are discarded ImageIcon objects(see code) not being gced?

如何在不运行GC的情况下对它们进行GC处理?

How they could be GC'ed without the GC running?

当内存足够时,为什么要运行GC?

Why should the GC run when the memory suffices?

就是这样. GC会在需要时运行,并且通过将内存使用率保持在低于必要水平来赢不了多少.

That's it. The GC runs when needed and there isn't much to win by keeping the memory usage lower than necessary.

出于效率方面的考虑,GC实际上是一个幸存者收集器":它只处理幸存的对象,而剩下的是可用内存.因此,在大多数对象都年轻时就将其运行为ALAP是很有意义的.


For efficiency reasons, the GC is actually a "survivor collector": It only deals with surviving objects and what's left behind is free memory. Therefore it makes sense to run it ALAP as most objects die young.

预计应为11.8 * 11.8 * 7.23KB左右

Expected should be around the order of 11.8*11.8*7.23KB

否,Java进程可以自由使用您为其分配的所有内存.

No, a Java process is free to use all the memory you gave it.

缩小时,内存消耗不会减少

Upon zooming out, the memory consumption doesn't reduce

是的,因为无需运行GC.

Yes, as there's no need for the GC to run.

为什么堆会如此之大(约为磁石的17倍,达到2GB)

Why is the heap expanding so much(at around ~17 times mag, it reaches 2GB)

所有中间尺寸的图像均无法访问,但尚未收集.

The images at all intermediate sizes are unreachable, but not yet collected.

如何在mag * mag * originalImageSize(以字节为单位)<50%JVM最大堆大小的情况下使代码对mag可行?

How to make code viable for mag where mag * mag * originalImageSize(in bytes)<50% JVM max heap size?

不能.当Java进程需要内存时,它将被回收.

You can't. When the memory will be needed by the Java process, then it gets reclaimed.

我在撒谎.您可以手动调用System.gc,它可能会有所帮助.但是不要这样做.虽然这回答了最后一个问题,但没有解决任何实际问题.如果要保持较低的内存使用量,请使用-Xmx1000M或类似方法为Java减少内存用量.

I was lying a bit. You can call System.gc manually and it'll probably help. But don't do it. While this answers the last question, it solves no real problem. If you want to keep the memory usage low, then give Java less memory using -Xmx1000M or alike.

这篇关于Java中图像缩放的内存问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆