简体   繁体   English

Swing中的内存泄漏

[英]Memory leak in Swing

An application Swing (GUI), a destination a destination information terminal. 应用程序Swing(GUI),目的地是目的地信息终端。 The VirtualVM profiler shows that the leakage occurs due to VirtualVM Profiler显示由于发生泄漏

java.awt.image.DataBufferInt

и и

sun.awt.image.ImageRepresentation.setPixels

, the increase of memory occurs during transitions between forms. ,在表单之间的转换期间发生内存的增加。

The application logic is that there are several forms (JFrame - JF1, JF2 ... JF7). 应用程序逻辑是有几种形式(JFrame - JF1,JF2 ... JF7)。 JF1 basic form, pressing the JButtons open other forms, and closes itself, etc. Except JF1 all other forms have buttons <>. JF1基本形式,按JButtons打开其他形式,并自行关闭等。除了JF1,所有其他形式都有按钮<>。 In forms there are many JButtons with the pictures, used FancyButton: 在表单中有许多带有图片的JButton,使用FancyButton:

public class FancyButton extends JButton {
    private static final long serialVersionUID = 1L;

    public FancyButton(Icon icon, Icon pressed) {
        super(icon);
        setFocusPainted(false);
        //setRolloverEnabled(treue);
        //setRolloverIcon(rollover);
        setPressedIcon(pressed);
        setBorderPainted(false);
        setContentAreaFilled(false);
    }
}

The JButtons on the forms are drawn as follows: 表单上的JButton绘制如下:

public class JF1 extends JFrame {

    private static final GridBagConstraints gbc;
    public static Timer tmr;

    public JF1() throws Exception{
        setUndecorated(true);
        GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
        setAlwaysOnTop(true);
        setLayout(new GridBagLayout());
        setTitle("JF1");
    }

    public void init() throws Exception{

        GlobalVars.jf2 = null;
        GlobalVars.jf3 = null;
        GlobalVars.jf4 = null;
        GlobalVars.jf5 = null;
        GlobalVars.jf6 = null;
        GlobalVars.jf7 = null;
        JXPanel contentPane = new JXPanel();
                try {
                ImagePainter ip = new ImagePainter(ImageIO.read(new File("skins/bg.jpg")));
                ip.setFillHorizontal(true);
                ip.setFillVertical(true);
                contentPane.setBackgroundPainter(ip);
             } catch (Exception e) {
                e.printStackTrace();
             }


        Panel p01 = new Panel();
        GridLayout gl01 = new GridLayout(1, 8, 2, 2);
        p01.setLayout(gl01);
        p01.setLocation(200, 300);
        ResultSet rs = GlobalVars.st.executeQuery("select * from last_use_service order by nomer");
        Icon i1;
        Icon i2;
        while (rs.next()){
            final int l = rs.getInt(2);
            i1 = new ImageIcon("skins/oper_logos/" + l + ".png");
            i2 = new ImageIcon("skins/oper_logos/" + l + "_off.png");

            FancyButton jbt = new FancyButton(i1,i2 );
            jbt.setBounds(10, 100, 100, 100);

            jbt.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    tmr.stop();     

                    if(GlobalVars.jf3==null)
                                            GlobalVars.jf3 = new JF3();
                    GlobalVars.jf3.init(); 
                    GlobalVars.jf3.setVisible(true);            // Так открывается новая форма
                    setVisible(false);                              // и закрывается текущая
                    dispose();
                }
            });
            p01.add(jbt);
        }
        rs.close();
        addComponent(panel, p01, 0, 1, 2, 1, GridBagConstraints.WEST,GridBagConstraints.NORTHWEST);
...

The main class which starts first: 首先开始的主要课程:

public class Main {

    public static class GlobalVars{

        public static String TypeDB = "MySQL";  
        public static Connection DataBase;
        public static Statement st;     

        public static JF1 jf1;          // JFrame
        public static JF2 jf2;          // JFrame
        public static JF3 jf3;          // JFrame
        ...
    }

    public static void main(String[] args) throws Exception {
        if(GlobalVars.TypeDB.equals("MySQL")){
            Class.forName("com.mysql.jdbc.Driver");
            GlobalVars.DataBase = DriverManager.getConnection("jdbc:mysql://localhost:3306/terminal?lc_ctype=UTF8", "root","123");

                if(GlobalVars.jf1==null)
                    GlobalVars.jf1 = new JF1();
        GlobalVars.jf1.init();
        GlobalVars.jf1.setVisible(true);
        }
...
       }

Still in Init method of Forms has a timer which, after a time, opens the main form and closes the current one: 仍然在Init的Forms方法有一个计时器,一段时间后,打开主窗体并关闭当前的窗体:

...
tmr = new Timer( s * 1000, updateCursorAction);
tmr.start();
...
private Action updateCursorAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            if(GlobalVars.jf1==null){
                try {
                    GlobalVars.jf1= new JF1();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            tmr.stop();
            try {
                GlobalVars.jf1.init();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            GlobalVars.jf1.setVisible(true);
            GlobalVars.jf2 = null;
            setVisible(false);
            dispose();      
        }
    };

HEAP DUMP Please help fix a memory leak. HEAP DUMP请帮助修复内存泄漏。

I had changed all Panel to JPanel and that is code of JF1: 我已将所有Panel更改为JPanel,这是JF1的代码:

package PlatService;

import java.awt.*;

public class JF1 extends JFrame {
    private static final long serialVersionUID = 1L;
    //private static final Insets insets = new Insets(0, 0, 0, 0);
    private static String[] arrLang = { "rus", "eng", "taj" };
    private static final GridBagConstraints gbc;
    public static Timer tmr;

    static {

    public JF1() throws Exception{
        setUndecorated(true);
        GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
        setAlwaysOnTop(true);
        setLayout(new GridBagLayout());
        setTitle("JF1");
    }

    public void init() throws Exception{

        GlobalVars.jf2 = null;
        GlobalVars.jf3 = null;
        GlobalVars.jf4 = null;
        GlobalVars.jf5 = null;
        GlobalVars.jf6 = null;
        GlobalVars.jf7 = null;
        JXPanel contentPane = new JXPanel();
        try {
                ImagePainter ip = new ImagePainter(ImageIO.read(new File("skins/bg.jpg")));
                ip.setFillHorizontal(true);
                ip.setFillVertical(true);
                contentPane.setBackgroundPainter(ip);
        } catch (Exception e) {
                e.printStackTrace();
        }
        //setContentPane(contentPane);
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        addComponent(this, panel, 0, 0, 1, 1, GridBagConstraints.CENTER ,GridBagConstraints.BOTH);

        JPanel p0 = new JPanel();
        GridLayout gl0 = new GridLayout(1, 1, 1, 1);
        final JLabel jl = new JLabel(new ImageIcon("skins/logo.png"));
        p0.setLayout(gl0);
        p0.add(jl);
        addComponent(panel, p0, 0, 0, 2, 1, GridBagConstraints.NORTH ,GridBagConstraints.NORTH);


        JPanel p01 = new JPanel();
        GridLayout gl01 = new GridLayout(1, 8, 2, 2);
        p01.setLayout(gl01);
        p01.setLocation(200, 300);
        ResultSet rs = GlobalVars.st.executeQuery("select * from last_use_service order by nomer");
        Icon i1;
        Icon i2;
        while (rs.next()){
            final int l = rs.getInt(2);
            i1 = new ImageIcon("skins/oper_logos/" + l + ".png");
            i2 = new ImageIcon("skins/oper_logos/" + l + "_off.png");
            FancyButton jbt = new FancyButton(i1,i2 );
            jbt.setBounds(10, 100, 100, 100);
            jbt.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    tmr.stop();
                    GlobalVars.OperId = l;
                    GlobalVars.getCashCode=false;
                    if(GlobalVars.jf3==null)GlobalVars.jf3 = new JF3();
                    GlobalVars.jf3.init(); // = new JF3();
                    GlobalVars.jf3.setVisible(true);
                    setVisible(false);
                    dispose();
                }
            });
            p01.add(jbt);
        }
        rs.close();
        addComponent(panel, p01, 0, 1, 2, 1, GridBagConstraints.WEST,GridBagConstraints.NORTHWEST);

        if (GlobalVars.LangId < 0 || GlobalVars.LangId > 2)GlobalVars.LangId = 0;

        String sql = "SELECT * FROM OpGroup WHERE parent=0 order by enable desc, order_n";
        PreparedStatement psmnt = GlobalVars.DataBase.prepareStatement(sql);
        ResultSet rs2 = psmnt.executeQuery();

        //rs = GlobalVars.st.executeQuery();
        JPanel p = new JPanel();
        GridLayout gl = new GridLayout(0, 2, 2, 2);
        p.setLayout(gl);
        p.setSize(300, 400);
        p.setLocation(200, 300);        
        while (rs2.next()){
            final int l = rs2.getInt(2);
            i1 = new ImageIcon("skins/"+ arrLang[GlobalVars.LangId]+"/services/" + l + ".png");
            i2 = new ImageIcon("skins/"+ arrLang[GlobalVars.LangId]+"/services/" + l + "_off.png");
            FancyButton jbt = new FancyButton(i1,i2);
            jbt.setBounds(10, 100, 100, 100);
            if(rs2.getInt("enable")==1){
                jbt.setEnabled(true);
            }else{
                jbt.setEnabled(false);
            }
            jbt.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    GlobalVars.getCashCode=false;
                    try {
                        tmr.stop();
                        ActionPerformed(event, GlobalVars.LangId, l);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            p.add(jbt);
        }
        addComponent(panel, p, 0, 2, 1, 1, GridBagConstraints.NORTH,GridBagConstraints.NORTH);
        rs2.close();
        JPanel p1 = new JPanel();
        GridLayout gl1 = new GridLayout(5, 1, 5, 5);
        // setLayout(new GridLayout(3, 4, 2, 2));
        p1.setLayout(gl1);
        // p2.setSize(300, 400);
        // p2.setLocation(200, 300);

        for (int i = 0; i < arrLang.length; i++) {
            final int l = i;
            i1 = new ImageIcon("skins/button_" + arrLang[i] + ".png");
            i2 = new ImageIcon("skins/button_" + arrLang[i] + "_off.png");
            FancyButton jbt = new FancyButton(i1, i2);
            jbt.setBounds(10, 100, 100, 100);
            //if (i == GlobalVars.LangId) {jbt.setEnabled(false);}
            jbt.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    //Play.stop();
                    GlobalVars.LangId = l;
                    GlobalVars.getCashCode=false;
                    GlobalVars.jf1 = null;
                    try {
                        GlobalVars.jf1 = new JF1();
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                    try {
                        GlobalVars.jf1.init();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    tmr.stop();
                    GlobalVars.jf1.setVisible(true);
                    setVisible(false);
                }
            });
            p1.add(jbt);
        }
        i1 = new ImageIcon("skins/" + arrLang[GlobalVars.LangId] + "/services/button_help.png");
        i2 = new ImageIcon("skins/" + arrLang[GlobalVars.LangId] + "/services/button_help_off.png");
        FancyButton jbt_help = new FancyButton(i1,i2);
        jbt_help.setBounds(10, 100, 100, 100);
        jbt_help.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                tmr.stop();
                GlobalVars.getCashCode=false;
                GlobalVars.jf1 = null;
                try {
                    GlobalVars.jf1 = new JF1();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
                try {
                    GlobalVars.jf1.init();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                GlobalVars.jf1.setVisible(true);
                setVisible(false);
            }
        });
        p1.add(jbt_help);
        i1 = new ImageIcon("skins/" + arrLang[GlobalVars.LangId] + "/services/button_about.png");
        i2 = new ImageIcon("skins/" + arrLang[GlobalVars.LangId] + "/services/button_about_off.png");
        FancyButton jbt_about = new FancyButton(i1,i2);
        jbt_about.setBounds(10, 100, 100, 100);

        p1.add(jbt_about);
        addComponent(panel, p1, 1, 2, 1, 1, GridBagConstraints.EAST,GridBagConstraints.EAST);

        JPanel p011 = new JPanel();
        GridLayout gl011 = new GridLayout( 1, 1, 1, 1);
        gl011.setVgap(1);
        JLabel jl12 = new JLabel("<html><hr></html>", JLabel.LEFT);
        jl12.setAlignmentX(TOP_ALIGNMENT);
        jl12.setBackground(Color.red);
        p011.setLayout(gl011);
        p011.add(jl12);
        p011.setSize(10, 90);
        addComponent(panel, p011, 0, 3, 4, 1, GridBagConstraints.WEST, GridBagConstraints.NORTH);


        JPanel p0112 = new JPanel();
        GridLayout gl0112 = new GridLayout( 1, 1, 1, 1);
        gl0112.setVgap(1);
        JLabel jl122 = new JLabel("<html><hr><H2>"+GlobalVars.StatusMessage[GlobalVars.LangId]+"</H2></html>", JLabel.CENTER);
        jl122.setAlignmentX(TOP_ALIGNMENT);
        p0112.setLayout(gl0112);
        p0112.add(jl122);
        p0112.setSize(10, 90);

        addComponent(this, p0112, 0, 5, 5, 1, GridBagConstraints.SOUTH, GridBagConstraints.BOTH);

        if(!GlobalVars.stTerminal.equals("301")){
            GlobalVars.stTerminal = "301";
            String sql1 = "INSERT INTO perif_status (perif,state,date_change) values('terminal','"+GlobalVars.stTerminal+"',UNIX_TIMESTAMP())";
            //System.out.println(sql1);
            try {
                GlobalVars.st.execute(sql1);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        GlobalVars.NomerAb="";
        GlobalVars.GroupId = 0;
        GlobalVars.getCashCode=true;
        tmr = new Timer(1000, updateCursorAction);
        tmr.start();

        System.gc();
    }

    public void update(){

    private Action updateCursorAction = new AbstractAction() {
        public void actionPerformed(ActionEvent e) {
            if(GlobalVars.doBlock){
                if(!GlobalVars.stTerminal.equals("303")){
                    GlobalVars.stTerminal = "303";
                    String sql1 = "INSERT INTO perif_status (perif,state,date_change) values('terminal','"+GlobalVars.stTerminal+"',UNIX_TIMESTAMP())";
                    String sql2 = "UPDATE settings set value='"+GlobalVars.stTerminal+"' WHERE variable='terminal_state'";
                    System.out.println(sql1);
                    System.out.println(sql2);
                    try {
                        GlobalVars.st.execute(sql1);
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
                    try {
                        GlobalVars.st.execute(sql2);
                    } catch (SQLException e1) {
                        e1.printStackTrace();
                    }
                }       
                String sql1 = "UPDATE commands SET status=1, date_execute=UNIX_TIMESTAMP() WHERE id_on_server="+GlobalVars.doCommandId;
                System.out.println(sql1);
                try {
                    GlobalVars.st.execute(sql1);
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                if(GlobalVars.jf7==null)
                    try {
                        GlobalVars.jf7= new JF7();
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                try {
                    GlobalVars.jf7.init();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }


                GlobalVars.doBlock=false;
                GlobalVars.doCommandId = 0;
                GlobalVars.jf7.setVisible(true);
                GlobalVars.jf1 = null;
                setVisible(false);
                tmr.stop();
                setVisible(false);
                dispose();
            }
        }
    };

    private static void addComponent(Container container, Component component,int gridx, int gridy, int gridwidth, int gridheight, int anchor,int fill) {
        Insets ins = new Insets(0, 0, 0, 0); 
        GridBagConstraints gbc1 = new GridBagConstraints(gridx, gridy,gridwidth, gridheight, 1.0, 1.0, anchor, fill, ins, 0, 0);
        container.add(component, gbc1);
    }

    public void ActionPerformed(ActionEvent event, int lID,int gId) throws Exception {
        GlobalVars.GroupId = gId;
        if(GlobalVars.jf2==null)GlobalVars.jf2 = new JF2();
        GlobalVars.jf2.init(); 
        GlobalVars.jf2.setVisible(true);
        setVisible(false);
        dispose();
    }
}

And thats new dump 那就是新的转储

You do have a leak, but just looking at the number of loaded classes will not help you to find out what it is. 你确实有泄漏,但只是查看加载的类的数量将无法帮助你找出它是什么。

If you load your snapshot in JProfiler (disclaimer: my company develops JProfiler) and look at the biggest objects view, you can see that used memory is due to the double buffering of multiple JF1 frames and the panel instances that belong to those frames. 如果您在JProfiler中加载快照(免责声明:我的公司开发JProfiler)并查看最大的对象视图,您可以看到使用的内存是由于多个JF1和属于这些帧的面板实例的双缓冲造成的。

在此输入图像描述

Your problem is that the JF1 frames are hidden but not disposed . 您的问题是JF1帧被隐藏但未被丢弃 A search for GC roots shows that all 3 invisible frames are contained in java.awt.Window.allWindows which cannot the case if dispose() is called. 对GC根的搜索显示,所有3个不可见的帧都包含在java.awt.Window.allWindows ,如果调用dispose()则不然。 You call a lot of code outside the event dispatch thread. 你在事件派遣线程之外调用了很多代码。 For example you should not call setVisible() from the timer thread. 例如,您不应该从计时器线程调用setVisible()。 Try printing out the creation of JF1 frames and the call to their dispose methods and check where the do not they match. 尝试打印出JF1帧的创建和对其dispose方法的调用,并检查它们不匹配的位置。

在此输入图像描述

you create lots of Top-level Containers , with JComponents or Images inside them, and these Objects in this form never gone for JVM Used_Memory , you have to clean-up contents of un-used Top-level Containers , 你创建了许多顶级容器 ,里面有JComponentsImages ,而且这种形式的这些Objects永远不会用于JVM Used_Memory ,你必须清理未使用的Top-level Containers

better would be 会更好

create JFrame only once, and for another popup windows create only one JDialog/JWindow put here JPanel , re-use this Container by removing JComponents from JPanel , 只创建一次JFrame ,对于另一个弹出窗口,只创建一个JDialog/JWindow放在这里JPanel ,通过从JPanel删除JComponents来重用这个Container,

JPanel.removeAll();

DefaultCloseOperation would be DefaultCloseOperation将是

JDialog#setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE) 的JDialog#setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE)

or you can set 或者你可以设置

JDialog#setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE) and then you can call only JDialog#setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE)然后你只能打电话

JDialog#setVisible(false / true)

EDIT 编辑

and you have to close all JDBC ResultSet, Statement, PreparedStatement in finally block, because these Object never gone from JVM UsedMemory too 并且你必须关闭finally块中的所有JDBC ResultSet,Statement,PreparedStatement,因为这些Object也永远不会从JVM UsedMemory中删除

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

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