简体   繁体   English

AWT/Swing 中的 Memory 泄漏?

[英]Memory leak in AWT/Swing?

I have a fullscreen overlay with 3 different photos.我有一个包含 3 张不同照片的全屏叠加层。 I'm storing BufferedImage instances in ArrayList, and switch between photos using CardLayout listeneing to KeyEvent s (reacting to pressing arrow keys).我将BufferedImage实例存储在 ArrayList 中,并使用CardLayout侦听KeyEvent (对按下箭头键作出反应)在照片之间切换。

When I test this overlay isolated, it works just fine.当我单独测试这个叠加层时,它工作得很好。

But when I open it as part of my application, I get the following error:但是当我将它作为应用程序的一部分打开时,出现以下错误:


Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
        at java.awt.image.Raster.createPackedRaster(Raster.java:467)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
        at java.awt.image.BufferedImage.<init>(BufferedImage.java:331)
        at sun.java2d.pipe.DrawImage.makeBufferedImage(DrawImage.java:324)
        at sun.java2d.pipe.DrawImage.renderImageXform(DrawImage.java:392)
        at sun.java2d.d3d.D3DDrawImage.renderImageXform(D3DDrawImage.java:77)
        at sun.java2d.pipe.DrawImage.transformImage(DrawImage.java:264)
        at sun.java2d.pipe.DrawImage.scaleImage(DrawImage.java:124)
        at sun.java2d.pipe.DrawImage.scaleImage(DrawImage.java:1046)
        at sun.java2d.pipe.ValidatePipe.scaleImage(ValidatePipe.java:207)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3127)
        at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3067)
        at xmessenger.Overlay$ImageViewer.paintComponent(Overlay.java:143)
        at javax.swing.JComponent.paint(JComponent.java:1055)
        at javax.swing.JComponent.paintChildren(JComponent.java:888)
        at javax.swing.JComponent.paint(JComponent.java:1064)
        at javax.swing.JComponent.paintChildren(JComponent.java:888)
        at javax.swing.JComponent.paint(JComponent.java:1064)
        at javax.swing.JComponent.paintChildren(JComponent.java:888)
        at javax.swing.JComponent.paint(JComponent.java:1064)
        at javax.swing.JComponent.paintToOffscreen(JComponent.java:5232)
        at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
        at javax.swing.RepaintManager.paint(RepaintManager.java:1249)
        at javax.swing.JComponent._paintImmediately(JComponent.java:5180)
        at javax.swing.JComponent.paintImmediately(JComponent.java:4991)
        at javax.swing.RepaintManager$3.run(RepaintManager.java:808)
        at javax.swing.RepaintManager$3.run(RepaintManager.java:796)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:769)

Here is my Overlay class:这是我的Overlay class:


public class Overlay extends JFrame {

        public Overlay(ArrayList<BufferedImage> imgs) {
            super();
            this.setUndecorated(true);
            getRootPane().setOpaque(true);
            pane = new JPanel();
            pane.setOpaque(false);
            add(pane);
            pane.setLayout(new CardLayout());

            Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
            imgWidth = (int) (dim.width * 0.7 + 0.4);
            imgHeight = (int) (imgWidth / 16 * 9 + 0.4);

            pos = 0;

            images = imgs;

            for (BufferedImage image: images) {
                addFrame(image);
            }

            ((CardLayout)pane.getLayout()).first(pane);
            pane.invalidate();

            this.addKeyListener(new KeyListener() {

                @Override
                public void keyTyped(KeyEvent e) {}

                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        setVisible(false);
                        dispose();
                    }
                    else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                        ((CardLayout)pane.getLayout()).previous(pane);
                        pos--;
                        if (pos < 0) pos = images.size() - 1;
                    }
                    else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                        ((CardLayout)pane.getLayout()).next(pane);
                        pos++;
                        if (pos > images.size() - 1) pos = 0;
                    }
                }

                @Override
                public void keyReleased(KeyEvent e) {}
            });
        }

        public class ImageViewer extends JPanel {

            public ImageViewer(String src) {
                this.src = src;
                setOpaque(false);
                try {
                    img = ImageIO.read(new URL(src));
                    int imgHeight = img.getHeight(observer);
                    if (imgHeight > 0) {
                        w = (int) (h * ((double) img.getWidth(null) / imgHeight));
                    }
                } catch (IOException ex) {}
            }

            public ImageViewer(BufferedImage image, int width, int height) {
                if (image == null) return;
                setOpaque(false);
                setPreferredSize(new Dimension(width, height));
                setMinimumSize(new Dimension(width, height));
                setMaximumSize(new Dimension(width, height));
                System.out.println(width + "x" +  height);
                w = width;
                h = height;

                img = image;
                int imgHeight = img.getHeight(observer);
                if (imgHeight > 0) {
                    w = (int) (h * ((double) img.getWidth(null) / imgHeight));
                }
            }

            public ImageViewer(String src, int width, int height) {
                setOpaque(false);
                setPreferredSize(new Dimension(width, height));
                setMinimumSize(new Dimension(width, height));
                setMaximumSize(new Dimension(width, height));
                w = width;
                h = height;
                this.src = src;
                try {
                    img = ImageIO.read(new URL(src));
                    int imgHeight = img.getHeight(observer);
                    if (imgHeight > 0) {
                        w = (int) (h * ((double) img.getWidth(null) / imgHeight));
                    }
                } catch (IOException ex) {}
            }

            ImageObserver observer = new ImageObserver() {
                @Override
                public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
                    if (height == 0) return false;
                    w = (int) (h * ((double) width / height));
                    if (getParent() != null) getParent().repaint();
                    return true;
                }
            };

            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (img == null) return;
                Graphics2D g2d = (Graphics2D) g;
                int x = (int) (getWidth() - w) / 2;
                int y = (int) (getHeight() - h) / 2;
                System.out.println(x + ", " + y);
                g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                g2d.clearRect(x, y, w, h);
                g2d.drawImage(img, x, y, w, h, null);
            }

            private int w;
            private int h;

            private Image img;
            private String src = null;
        }

        public void addFrame(BufferedImage image) {
            panel = new JPanel();
            panel.setBackground(new Color(12,12,12));

            pane.add(panel);
            panel.setBorder(BorderFactory.createEmptyBorder(90, 90, 90, 90));

            ImageViewer viewer = new ImageViewer(image, imgWidth, imgHeight);
            panel.setLayout(new BorderLayout());
            panel.add(viewer);

            int number = pane.getComponentCount();
            JLabel status = new JLabel(number + " / " + images.size());
            status.setForeground(new Color(235, 235, 235));
            status.setFont(new Font("SegoeUI", Font.PLAIN, 17));
            status.setHorizontalAlignment(SwingConstants.CENTER);
            panel.add(status, BorderLayout.PAGE_END);
        }

        public void prevImage() {
            ((CardLayout)pane.getLayout()).previous(pane);
            pos--;
            if (pos < 0) pos = images.size() - 1;
        }

        public void nextImage() {
            ((CardLayout)pane.getLayout()).next(pane);
            pos++;
            if (pos > images.size() - 1) pos = 0;
        }

        private int imgWidth = 0;
        private int imgHeight = 0;

        private ArrayList<BufferedImage> images;
        private int pos = 0;

        public void showContent() {
            try {
                GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
                setVisible(true);
            } catch (Exception error) {}
        }

        public static void main(String[] args) {
            ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
            try {
                images.add(ImageIO.read(new File("D:/My Documents/NetBeansProjects/XMessenger/image.jpg")));
                images.add(ImageIO.read(new URL("http://popov654.pp.ru/copybox/photo.jpg")));
                images.add(ImageIO.read(new URL("http://popov654.pp.ru/copybox/DSCN2155.jpg")));
            } catch (IOException ex) {}
            Overlay o = new Overlay(images);
            o.showContent();
        }

        JPanel pane;
        JPanel panel;
    }

If I run it, it works OK.如果我运行它,它工作正常。 The problem appears when I create an instance from outside code:当我从外部代码创建实例时出现问题:

   private void addHyperlinkListener(JTextPane pane) {
        pane.addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
                if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    String src = (String) e.getSourceElement().getAttributes().getAttribute(HTML.Attribute.SRC);
                    System.err.println("Image src: " + src);
                    HTMLDocument doc = (HTMLDocument) msgContent.getStyledDocument();
                    Element[] imgs = getElementsByTagName(HTML.Tag.IMG, doc);

                    ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
                    ArrayList<String> srcs = new ArrayList<String>();
                    
                    int index = -1;
                    for (Element img: imgs) {
                        String source = (String) img.getAttributes().getAttribute(HTML.Attribute.SRC);
                        srcs.add(source);
                        try {
                            images.add(ImageIO.read(new URL(source)));
                        } catch (IOException ex) {}
                        if (!source.equals(src) && index < 0) {
                            index++;
                        }
                    }
                    
                    Overlay overlay = new Overlay(images);
                    for (int i = 0; i < index; i++) {
                        overlay.nextImage();
                    }
                    XMessengerApp.getMainWindow().getFrame().setVisible(false);
                    overlay.showContent();
                    
                }
            }
        });
    }

By the way, if I don't hide my current application frame, it keeps blinking through some areas of my overlay, which is also very strange.顺便说一句,如果我不隐藏我当前的应用程序框架,它会在我覆盖的某些区域中不断闪烁,这也很奇怪。 I thought that is the reason of failure (too many repaints because of that thing), but hiding it from the screen also does not help to avoid the errors.我认为这是失败的原因(因为那个东西重绘太多),但是将它从屏幕上隐藏也无助于避免错误。

Also when iI encounter this error, the blocks in my overlay's CardLayout stop reacting to key presses at all.此外,当我遇到此错误时,我覆盖的CardLayout中的块根本停止对按键做出反应。

What am I doing wrong?我究竟做错了什么?

The application is not very heavy, the most "big" part of it is a JTextPane with HTML content, but that is ridiculously small: one 2-line paragraph of text and 3 images after it.该应用程序不是很重,它最“大”的部分是一个包含 HTML 内容的 JTextPane,但它小得离谱:一段 2 行的文本和后面的 3 张图像。

The app is made using the JDesktop framework.该应用程序是使用 JDesktop 框架制作的。 I'm running the code with JDK 7.我正在使用 JDK 7 运行代码。

Also look here, please:也请看这里:

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6567435 https://www.codeproject.com/Questions/5311994/How-to-fix-a-memory-leak-caused-by-bufferedimage-ghttps://bugs.java.com/bugdatabase/view_bug.do?bug_id=6567435 https://www.codeproject.com/Questions/5311994/How-to-fix-a-memory-leak-caused-by-bufferedimage -G

Maybe there is a bug leading to this in JDK/JRE?也许在 JDK/JRE 中存在导致此问题的错误? Except I am reading JPEGs, and not TIFFs, but with a much similar resolution.除了我正在阅读 JPEG,而不是 TIFF,但分辨率非常相似。

The solution is to increase the heap size for the VM:解决方案是增加 VM 的堆大小:

-Xmx512m

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM