Swing Timer不是垃圾收集的

时间:2017-07-15 19:09:57

标签: java swing timer garbage-collection

由于Swing Timer,我在Swing应用程序中遇到了内存泄漏问题。

我使用Timer在 Page1 中显示图像的幻灯片。

当我分析应用程序时,我注意到当导航到 Page2 时,Timer对象,Page1对象和Page1对象中的任何对象都不是Garbage Collected。

我开始知道stopping Timer允许它被垃圾收集。

我假设如果没有引用任何对象,它就可以进行垃圾回收了。但在这种情况下,这种假设失败了。

下面的代码总结了我的应用程序,没有内存泄漏。要查看内存泄漏,请注释我调用stopTimer的{​​{1}}方法的行。

Timer

可能的原因是什么?

1 个答案:

答案 0 :(得分:2)

我在一个人工小的堆中描述了你的例子,如here所示。该配置文件显示了预期的结果:定期垃圾收集将使用的堆空间返回到基线,如here所示。为轮廓的后半部分选择第二页导致较小的振幅收集。采样内存显示第一页上的Timer实例已在第二页上迅速收集;没有实例激增。一些额外的建议:

image

  

评论[out]我调用[{] stopTimer()方法的行。

同样的结果占上风。请注意,Swing Timer“的实例共享相同的,预先存在的计时器线程。”当Timer运行时,出现在名为javax.swing.Timer$1的探查器中的内部类DoPostEvent的实例将暂时累积。他们也将被收集,尽管最终将在垃圾收集的后期阶段收集。根据建议here,您可以单击执行GC 按钮以实现更积极的收集。单击 Sampler 选项卡中的 Deltas 按钮,查看在执行计时器ActionListener过程中暂时累积的其他实例;再次单击执行GC 以查看效果。

控制台:

$ jvisualvm &
$ java TimerMemoryLeak.java
$ java -Xms32m -Xmx32m TimerMemoryLeak

代码,经过测试:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

public class TimerMemoryLeak {

    public static void main(String[] args) {
        TimerMemoryLeak timer = new TimerMemoryLeak();
        timer.buildUI();
    }

    public void buildUI() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        showPanel1();
        frame.setSize(600, 400);
        frame.setVisible(true);
    }

    public void showPanel1() {
        Page1 page1 = new Page1();
        if (currentPanel != null) {
            pane.remove(((Page2) currentPanel).getPanel());
        }
        pane.add(page1.getPanel());
        currentPanel = page1;
        page1.startTimer();

        page1.setNextAction(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showPanel2();
            }
        });
        pane.revalidate();
        pane.repaint();
    }

    public void showPanel2() {
        Page2 page2 = new Page2();
        if (currentPanel != null) {
            ((Page1) currentPanel).stopTimer();
            pane.remove(((Page1) currentPanel).getPanel());
        }
        pane.add(page2.getPanel());
        currentPanel = page2;

        page2.setPreviousAction(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showPanel1();
            }
        });

        pane.revalidate();
        pane.repaint();
    }

    private JFrame frame = new JFrame();
    private Container pane = frame.getContentPane();
    private Object currentPanel;

    private static class Page1 {

        public Page1() {
            panel.add(title, BorderLayout.NORTH);
            panel.add(textTimer);
            panel.add(btnNext, BorderLayout.SOUTH);
        }

        public void setNextAction(ActionListener listener) {
            btnNext.addActionListener(listener);
        }

        public JPanel getPanel() {
            return panel;
        }

        public void startTimer() {
            timer.setInitialDelay(0);
            timer.start();
        }

        public void stopTimer() {
            timer.stop();
        }

        private JPanel panel = new JPanel(new BorderLayout());
        private JLabel title = new JLabel("Panel 1");
        private JButton btnNext = new JButton("Next");
        private JLabel textTimer = new JLabel();
        private int timerInterval = 1000;
        private ActionListener timerAction = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textTimer.setText(Math.random() + "");
            }
        };
        private Timer timer = new Timer(timerInterval, timerAction);
    }

    private static class Page2 {

        public Page2() {
            panel.add(title, BorderLayout.NORTH);
            panel.add(btnPrev, BorderLayout.SOUTH);
        }

        public void setPreviousAction(ActionListener listener) {
            btnPrev.addActionListener(listener);
        }

        public JPanel getPanel() {
            return panel;
        }

        private JPanel panel = new JPanel(new BorderLayout());
        private JLabel title = new JLabel("Panel 2");
        private JButton btnPrev = new JButton("Previous");
    }
}