Java

时间:2018-08-24 07:22:47

标签: java swing memory garbage-collection jvm

环境:

JDK:1.8u112甲骨文

JRE:10.0.2

JVM最大堆大小:〜2GB。

操作系统:Windows 10

IDE:Netbeans 8.1

RAM:DDR4 8GB

处理器:6700hq i7英特尔

上下文

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

描述

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

问题

  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可行?

代码

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                   
}

测试文件

除了test file以外,任何jpg或png图像都应做。

测试文件的输出

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 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 大小:370%内存:393MB 大小:380%内存:393MB mag:390%mem:393MB mag:400%mem:393MB 大小:410%内存: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 大小:969%内存: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

1 个答案:

答案 0 :(得分:0)

  

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

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

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

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

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


  

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

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

  

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

是的,因为无需运行GC。

  

为什么堆会这么大地扩展(约为磁石的17倍,达到2GB)

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

  

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

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


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