2

我目前正在实现一个使用 JTable 来显示一些信息的程序。该表包含不超过 10 行(所以它很小)。

在开发过程中,我通常使用该-verbose:gc选项运行程序。

如果我单击 2 列之间的线并继续向左移动,然后向右移动,以此类推,我注意到生成了很多垃圾。当我这样做时,该程序绝对没有做任何事情,因此没有其他潜在的垃圾来源。此外,如果我停止,垃圾收集器也会停止收集。

如果我继续调整这些列的大小一分钟左右,就会产生大约100MB+ 的垃圾,这似乎很多。

这不会影响我的程序的运行方式,但它看起来很奇怪。为什么它会这样?

编辑

这是一个SSCCE:

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class JTableTest
{
    public static void main (String[] args)
    {
        SwingUtilities.invokeLater (new Runnable ()
        {
            @Override public void run ()
            {
                JFrame frame = new JFrame ();
                frame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
                frame.setLayout (null);
                frame.setSize (700, 300);
                frame.setResizable (false);

                JTable table = new JTable ();
                table.setAutoResizeMode (JTable.AUTO_RESIZE_ALL_COLUMNS);

                String[] titles = { "Title 1", "Title 2", "Title 3", "Title 4", "Title 5" };

                String[][] data = {
                                { "Row 1, Column 1", "Row 1, Column 2", "Row 1, Column 3", "Row 1, Column 4", "Row 1, Column 5"},
                                { "Row 2, Column 1", "Row 2, Column 2", "Row 2, Column 3", "Row 2, Column 4", "Row 2, Column 5"},
                                { "Row 3, Column 1", "Row 3, Column 2", "Row 3, Column 3", "Row 3, Column 4", "Row 3, Column 5"},
                                { "Row 4, Column 1", "Row 4, Column 2", "Row 4, Column 3", "Row 4, Column 4", "Row 4, Column 5"},
                                { "Row 5, Column 1", "Row 5, Column 2", "Row 5, Column 3", "Row 5, Column 4", "Row 5, Column 5"}
                                };

                table.setModel (new DefaultTableModel (data, titles)
                {
                    @Override public boolean isCellEditable (int row, int column)
                    {
                        return false;
                    }
                });

                JScrollPane scrollpane = new JScrollPane (table);
                scrollpane.setHorizontalScrollBarPolicy (ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
                scrollpane.setVerticalScrollBarPolicy (ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

                scrollpane.setBounds (20, 20, 650, 250);
                frame.add (scrollpane);

                frame.setVisible (true);
            }
        });
    }
}

-verbose:gc使用该选项运行程序。运行时,在“Title x”和“Title y”(x != y)之间单击并调整这些列的大小。保持鼠标点击并保持左右移动。您将看到stdout垃圾收集器在您执行此操作时正在收集垃圾。(在我的系统上它大约每分钟 10 次)。如果/当您停止时,将不再收集垃圾。

4

2 回答 2

3

  • 代码的一些变化

  • 添加了 JTextArea

  • 您确认我们您的输出与打印到 JTextArea 的输出相同吗

(WinXP, Java6 --> 看起来永远一样)

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.DefaultCaret;

public class JTableTest {

    private javax.swing.Timer timer = null;
    private JTextArea text = new JTextArea();

    public JTableTest() {
        String[] titles = {"Title 1", "Title 2", "Title 3", "Title 4", "Title 5"};
        String[][] data = {
            {"Row 1, Column 1", "Row 1, Column 2", "Row 1, Column 3", "Row 1, Column 4", "Row 1, Column 5"},
            {"Row 2, Column 1", "Row 2, Column 2", "Row 2, Column 3", "Row 2, Column 4", "Row 2, Column 5"},
            {"Row 3, Column 1", "Row 3, Column 2", "Row 3, Column 3", "Row 3, Column 4", "Row 3, Column 5"},
            {"Row 4, Column 1", "Row 4, Column 2", "Row 4, Column 3", "Row 4, Column 4", "Row 4, Column 5"},
            {"Row 5, Column 1", "Row 5, Column 2", "Row 5, Column 3", "Row 5, Column 4", "Row 5, Column 5"}
        };
        JTable table = new JTable();
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        table.setModel(new DefaultTableModel(data, titles) {

            private static final long serialVersionUID = 1L;

            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        });
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollpane = new JScrollPane(table);
        scrollpane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrollpane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);


        DefaultCaret caret = (DefaultCaret) text.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        JScrollPane scroll = new JScrollPane(text);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocation(150, 150);
        frame.setSize(700, 300);
        frame.setResizable(false);
        frame.add(scrollpane, BorderLayout.NORTH);
        frame.add(scroll, BorderLayout.CENTER);
        frame.setVisible(true);
        start();
    }

    private void start() {
        timer = new javax.swing.Timer(1000, updateCol());
        timer.start();
    }

    public Action updateCol() {
        return new AbstractAction("text load action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                text.append("FreeMemory in Kb " + Runtime.getRuntime().freeMemory() / 1000 + "\n");
                text.append("MaxMemory in Kb " + Runtime.getRuntime().maxMemory() / 1000 + "\n");
                text.append("TotalMemory in Kb " + Runtime.getRuntime().totalMemory() / 1000 + "\n");
                text.append("UsedMemory in Kb " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000) + "\n");
                text.append("\n");
            }
        };
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JTableTest jTableTest = new JTableTest();
            }
        });
    }
}

编辑 JTable 和 GC 几列 75k 行 (82 -85Mb)

import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;


public class TriState extends JPanel {

    private static final long K = 1024;
    private static final long M = K * K;
    private static final long G = M * K;
    private static final long T = G * K;
    protected static int ctr = 1;
    private static final long serialVersionUID = 1L;
    private JButton btnShow = new JButton("Show Form");
    private JLabel lblMem = new JLabel();
    private static final DecimalFormat df = new DecimalFormat("#,##0.#");
    protected Timer updateTimer = new Timer();

    public TriState() {
        this.setLayout(new GridLayout());
        add(btnShow);
        add(lblMem);
        updateTimer.scheduleAtFixedRate(new UpdateTimerTask(), 1000, 1000);
        btnShow.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                FrmReferrals fr = new FrmReferrals();
                fr.setVisible(true);
            }
        });
    }

    class UpdateTimerTask extends TimerTask {

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

                @Override
                public void run() {
                    dumpMemoryUsage();
                }
            });
        }
    }

    protected void dumpMemoryUsage() {
        System.gc();
        Long t = Runtime.getRuntime().totalMemory();
        long f = Runtime.getRuntime().freeMemory();

        String st = convertToStringRepresentation(t);
        String sf = convertToStringRepresentation(f);
        String su = convertToStringRepresentation(t - f);
        System.out.println("Total:" + st + "(" + t + ") Free:" + sf + "(" + f + ") Used:" + su + "(" + (t - f) + ")");
        lblMem.setText(su + "/" + st);

    }

    public static String convertToStringRepresentation(final long value) {
        final long[] dividers = new long[]{T, G, M, K, 1};
        final String[] units = new String[]{"TB", "GB", "MB", "KB", "B"};
        if (value < 1) {
            throw new IllegalArgumentException("Invalid file size: " + value);
        }
        String result = null;
        for (int i = 0; i < dividers.length; i++) {
            final long divider = dividers[i];
            if (value >= divider) {
                final double dr = divider > 1 ? (double) value / (double) divider : (double) value;
                result = df.format(dr) + units[i];
                break;
            }
        }
        return result;
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("SimpleTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create and set up the content pane.
        TriState newContentPane = new TriState();
        newContentPane.setOpaque(true); // content panes must be opaque
        frame.setContentPane(newContentPane);

        // Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    protected class PopupMenu extends JPopupMenu {

        public PopupMenu() {
            JRadioButtonMenuItem item1 = new JRadioButtonMenuItem(new AbstractAction("Insert Item") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println(e.getActionCommand());
                }
            });
            item1.setActionCommand("Insert");
            add(item1);

            JRadioButtonMenuItem item2 = new JRadioButtonMenuItem(new AbstractAction("Delete Item") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println(e.getActionCommand());
                }
            });
            item2.setActionCommand("Delete");
            add(item2);
        }
    }

    public class FrmReferrals extends JFrame {

        public FrmReferrals() {
            super();
            init();
        }

        protected void init() {
            jbInit();
        }

        protected void closeIt() {
            uninit();
        }
        // variables here
        final Dimension dimPreferred = new Dimension(1270, 995);
        final JTabbedPane tabbedPane = new JTabbedPane();
        private JTable tblReferrals = null;
        private PopupMenu popMenu = new PopupMenu();

        protected void jbInit() {
            setPreferredSize(dimPreferred);
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setTitle("Referrals");
            JPanel pnl = new JPanel();
            pnl.setOpaque(false);
            pnl.setLayout(new BorderLayout());
            pnl.add(tabbedPane, BorderLayout.CENTER);
            // put it all in the frame
            add(pnl);
            pack();
            setLocationRelativeTo(null);
            // init the table and model
            ReferralsTableModel ctm = new ReferralsTableModel(buildDummyVector());
            tblReferrals = new JTable(ctm);
            tblReferrals.setComponentPopupMenu(popMenu);
            tblReferrals.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
            tabbedPane.add(new JScrollPane(tblReferrals, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));

            addWindowListener(new WindowListener() {

                @Override
                public void windowActivated(WindowEvent e) {
                }

                @Override
                public void windowClosed(WindowEvent e) {
                }

                @Override
                public void windowClosing(WindowEvent e) {
                    closeIt();
                }

                @Override
                public void windowDeactivated(WindowEvent e) {
                }

                @Override
                public void windowDeiconified(WindowEvent e) {
                }

                @Override
                public void windowIconified(WindowEvent e) {
                }

                @Override
                public void windowOpened(WindowEvent e) {
                }
            });
        }

        protected Vector<DBO_Referrals> buildDummyVector() {
            Vector<DBO_Referrals> vr = new Vector<DBO_Referrals>();
            for (int x = 0; x < 75000; x++) {
                DBO_Referrals r = new DBO_Referrals(x + (5000 * ctr));
                vr.add(r);
            }
            return vr;
        }

        protected void uninit() {
            tblReferrals.setComponentPopupMenu(null);
            for (Component c : popMenu.getComponents()) {
                PropertyChangeListener[] pl = c.getPropertyChangeListeners();
                for (PropertyChangeListener l : pl) {
                    c.removePropertyChangeListener(l);
                }
                if (c instanceof JMenuItem) {
                    ActionListener[] al = ((JMenuItem) c).getActionListeners();
                    for (ActionListener l : al) {
                        ((JMenuItem) c).removeActionListener(l);
                    }
                }
            }
            popMenu = null;
        }

        protected class DBO_Referrals {

            private long id;
            private String Employee;
            private String Rep;
            private String Asst;
            private String Client;
            private String Dates;
            private String Status;
            private String Home;

            public DBO_Referrals(long id) {
                this.id = id;
                Employee = "Employee" + id;
                Rep = "Rep" + id;
                Asst = "Asst" + id;
                Client = "Client" + id;
                Dates = "Dates" + id;
                Status = "Status" + id;
                Home = "Home" + id;
            }

            public long getId() {
                return id;
            }

            public String getEmployee() {
                return Employee;
            }

            public String getRep() {
                return Rep;
            }

            public String getAsst() {
                return Asst;
            }

            public String getClient() {
                return Client;
            }

            public String getDates() {
                return Dates;
            }

            public String getStatus() {
                return Status;
            }

            public String getHome() {
                return Home;
            }
        }

        public class ReferralsTableModel extends AbstractTableModel {

            private static final long serialVersionUID = 1L;
            private Vector<DBO_Referrals> data = new Vector<DBO_Referrals>();
            final String[] sColumns = {"id", "Employee", "Rep", "Assistant", "Client", "Date", "Status", "Home", "R"};

            public ReferralsTableModel() {
                super();
            }

            public ReferralsTableModel(Vector<DBO_Referrals> data) {
                this();
                this.data = data;
            }

            @SuppressWarnings("unchecked")
            @Override
            public Class<?> getColumnClass(int col) {
                switch (col) {
                    case 0:
                        return Long.class;
                    default:
                        return String.class;
                }
            }

            @Override
            public int getColumnCount() {
                return sColumns.length;
            }

            @Override
            public int getRowCount() {
                return data.size();
            }

            @Override
            public Object getValueAt(int row, int col) {
                if (row > data.size()) {
                    return null;
                }
                DBO_Referrals a = data.get(row);
                switch (col) {
                    case 0:
                        return a.getId();
                    case 1:
                        return a.getEmployee();
                    case 2:
                        return a.getRep();
                    case 3:
                        return a.getAsst();
                    case 4:
                        return a.getClient();
                    case 5:
                        return a.getDates();
                    case 6:
                        return a.getStatus();
                    case 7:
                        return a.getHome();
                    case 8:
                        return "+";
                    default:
                        return null;
                }
            }
        }
    }
}
于 2012-04-09T13:37:56.650 回答
3

它产生垃圾仅仅是因为它可以。Java 是一种垃圾收集语言,基本上它所做的一切都会产生垃圾。

当您按住并拖动该按钮时,java 正在执行原始活动(即鼠标按钮状态、光标位置状态、时间等)并创建事件。然后,它将事件流与旧光标位置等内容进行比较,以检测程序是否真的想要对这些信息做任何事情。当您移动它时,Java 需要绘制线条、文件矩形、绘制文本等。

所有这些活动都会留下一些东西,垃圾,最终需要收集。

只是闲置,您的程序正在创建垃圾,很可能是非常缓慢的,因为内部计时器等会死掉并被回收。尝试使用 jconsole 程序来观察堆,您会看到它随着时间的推移逐渐增加。

这是正常的,无需担心。这是一个功能,而不是一个错误。

于 2012-04-09T14:00:04.520 回答