由于Swing Timer,我在Swing应用程序中遇到了内存泄漏问题。
我使用Timer在 Page1 中显示图像的幻灯片。
当我分析应用程序时,我注意到当导航到 Page2 时,Timer对象,Page1对象和Page1对象中的任何对象都不是Garbage Collected。
我开始知道stopping Timer允许它被垃圾收集。
我假设如果没有引用任何对象,它就可以进行垃圾回收了。但在这种情况下,这种假设失败了。
下面的代码总结了我的应用程序,没有内存泄漏。要查看内存泄漏,请注释我调用stopTimer
的{{1}}方法的行。
Timer
可能的原因是什么?
答案 0 :(得分:2)
我在一个人工小的堆中描述了你的例子,如here所示。该配置文件显示了预期的结果:定期垃圾收集将使用的堆空间返回到基线,如here所示。为轮廓的后半部分选择第二页导致较小的振幅收集。采样内存显示第一页上的Timer
实例已在第二页上迅速收集;没有实例激增。一些额外的建议:
在event dispatch thread上构建和操作仅的Swing GUI对象。
考虑使用CardLayout
动态切换页面。
评论[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");
}
}