[英]Memory issue with image zooming in java
Env: ENV:
JDK: 1.8u112 oracle JDK:1.8u112甲骨文
JRE: 10.0.2 JRE:10.0.2
JVM max heap size: ~2GB. JVM最大堆大小:〜2GB。
OS: Windows 10 作业系统:Windows 10
IDE: Netbeans 8.1 IDE:Netbeans 8.1
RAM: DDR4 8GB 内存:DDR4 8GB
Processor: 6700hq i7 intel 处理器:6700hq i7 intel
Context 上下文
A simple GUI that opens an image file (jpg/png) and magnifies it via user input. 一个简单的GUI,可打开图像文件(jpg / png)并通过用户输入将其放大。
Desc 说明
A class extends JFrame. 一个类扩展了JFrame。 The frame's contentPane has a JButton,a JLabel & a JScrollPane.
框架的contentPane具有JButton,JLabel和JScrollPane。 Clicking the button shows a JFileChooser.
单击该按钮将显示一个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).
选择一个文件将在标签中将其打开(仅出于测试此问题jpg / png的目的打开图像文件)。 The label has a mouse wheel listener that causes zooming of image via
Image.getScaledInstance
. 标签具有鼠标滚轮侦听器,可通过
Image.getScaledInstance
缩放图像。 At each zoom, magnifiaction (ratio of new image width(or height) to corresponding original's) and Runtime.totalMemory
is printed. 每次缩放时,都会打印放大倍率(新图像宽度(或高度)与相应原始图像的比率)和
Runtime.totalMemory
。
Problem 问题
ImageIcon
objects(see code) not being gced? ImageIcon
对象(请参见代码)? Code 码
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 测试文件
Any jpg or png image should do besides test file . 除测试文件外,任何jpg或png图像都应执行。
Output from the 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 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 大小:110%内存:123MB大小:120%内存:123MB大小:130%内存:123MB大小:140%内存:123MB大小:150%内存:123MB大小:160%内存:123MB大小:150%内存:123MB大小: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大小:519%大小:541MB大小:529%大小:541MB大小:539%大小:641MB大小:549%大小:641MB大小:559%大小:641MB大小:569%大小:641MB大小:579%大小: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:1862
: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 :1862 MB 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
MB大小:1099%大小:1862MB大小:1109%大小:1815MB大小:1119%大小:1822MB大小:1129%大小:1758MB大小:1139%大小:1774MB大小:1149%大小:1711MB大小:1159%大小:1734MB大小:1169%内存:1676MB大小:1179%内存:1708MB大小 :1189%内存:1654MB
Are discarded ImageIcon objects(see code) not being gced?
是否没有给丢弃的ImageIcon对象(请参见代码)?
How they could be GC'ed without the GC running? 如果不运行GC,如何对它们进行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在需要时运行,并且通过将内存使用率保持在低于必要水平的优势,并没有什么好处。
For efficiency reasons, the GC is actually a "survivor collector": It only deals with surviving objects and what's left behind is free memory. 出于效率方面的考虑,GC实际上是一个“幸存者收集器”:它仅处理幸存的对象,而剩下的是可用内存。 Therefore it makes sense to run it ALAP as most objects die young.
因此,当大多数对象都年轻时,将其运行为ALAP是有意义的。
Expected should be around the order of 11.8*11.8*7.23KB
预期应该在11.8 * 11.8 * 7.23KB左右
No, a Java process is free to use all the memory you gave it. 不,Java进程可以自由使用您为其分配的所有内存。
Upon zooming out, the memory consumption doesn't reduce
缩小后,内存消耗不会减少
Yes, as there's no need for the GC to run. 是的,因为无需运行GC。
Why is the heap expanding so much(at around ~17 times mag, it reaches 2GB)
为什么堆扩展如此之大(约为mag的17倍,达到2GB)
The images at all intermediate sizes are unreachable, but not yet collected. 所有中间尺寸的图像均无法访问,但尚未收集。
How to make code viable for mag where mag * mag * originalImageSize(in bytes)<50% JVM max heap size?
如何使mag * mag * originalImageSize(以字节为单位)<50%JVM最大堆大小的mag可行代码?
You can't. 你不能 When the memory will be needed by the Java process, then it gets reclaimed.
当Java进程需要内存时,它将被回收。
I was lying a bit. 我在撒谎。 You can call
System.gc
manually and it'll probably help. 您可以手动调用
System.gc
,它可能会有所帮助。 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. 如果要保持较低的内存使用量,请使用
-Xmx1000M
或类似方式为Java减少内存。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.