使用 setText 时的内存泄漏

Posted

技术标签:

【中文标题】使用 setText 时的内存泄漏【英文标题】:Memory leak when using setText 【发布时间】:2012-01-17 18:27:51 【问题描述】:

我注意到我的程序中有内存泄漏。

我已经追查到问题所在了。

Clock.setText("" + h + ":" + df.format(m) + ":" + df.format(s));

我在谷歌上搜索过,这似乎是一个常见问题,但我没有找到答案。

有人知道解决办法吗?

完整代码如下:

package CodeBits;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class Countdown 

    private JFrame Timer;
    private JTextField Clock;
    private JLabel label;
    private JLabel label_1;
    static Calendar calendar = new GregorianCalendar();
    int minutes = 90;
    int count = minutes * 60;
    int h;
    int m;
    int s;
    javax.swing.Timer refreshTimer;

    /**
     * Launch the application.
     */
    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 

            public void run() 
                try 
                    Countdown window = new Countdown();
                    window.Timer.setVisible(true);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        );
    

    /**
     * Create the application.
     */
    public Countdown() 
        initialize();
    

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() 
        Timer = new JFrame();
        Timer.getContentPane().setBackground(new Color(173, 216, 230));
        Timer.setBounds(0, 0, 135, 100);
        Timer.setTitle("Aero Software");
        Timer.setIconImage(Toolkit.getDefaultToolkit().getImage("Files/Icon.jpg"));
        Timer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]0, 0, 0, 0, 0, 0;
        gridBagLayout.rowHeights = new int[]0, 0, 0, 0, 0, 0, 0, 0;
        gridBagLayout.columnWeights = new double[]0.0, 0.0, 1.0, 1.0, 0.0, Double.MIN_VALUE;
        gridBagLayout.rowWeights = new double[]0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE;
        Timer.getContentPane().setLayout(gridBagLayout);

        label = new JLabel(" ");
        GridBagConstraints gbc_label = new GridBagConstraints();
        gbc_label.insets = new Insets(0, 0, 5, 5);
        gbc_label.gridx = 0;
        gbc_label.gridy = 1;
        Timer.getContentPane().add(label, gbc_label);

        JLabel lblNewLabel = new JLabel("Countdown Timer");
        GridBagConstraints gbc_lblNewLabel = new GridBagConstraints();
        gbc_lblNewLabel.gridwidth = 4;
        gbc_lblNewLabel.insets = new Insets(0, 0, 5, 0);
        gbc_lblNewLabel.gridx = 1;
        gbc_lblNewLabel.gridy = 1;
        Timer.getContentPane().add(lblNewLabel, gbc_lblNewLabel);

        Clock = new JTextField();
        Clock.setFont(new Font("Arial", Font.BOLD, 22));
        Clock.setHorizontalAlignment(JLabel.CENTER);
        GridBagConstraints gbc_Clock = new GridBagConstraints();
        gbc_Clock.fill = GridBagConstraints.HORIZONTAL;
        gbc_Clock.gridwidth = 3;
        gbc_Clock.insets = new Insets(0, 0, 5, 5);
        gbc_Clock.gridx = 1;
        gbc_Clock.gridy = 2;
        Timer.getContentPane().add(Clock, gbc_Clock);
        Clock.setColumns(10);

        label_1 = new JLabel(" ");
        GridBagConstraints gbc_label_1 = new GridBagConstraints();
        gbc_label_1.insets = new Insets(0, 0, 5, 0);
        gbc_label_1.gridx = 4;
        gbc_label_1.gridy = 2;
        Timer.getContentPane().add(label_1, gbc_label_1);

        // Create countdown timer                       
        ActionListener refreshListener = new ActionListener() 

            // Ensure time is fotmatted as 7:04 not 7:4
            DecimalFormat df = new DecimalFormat("00");
            Calendar countdown = calendar;

            
                // Zero the time and add the number of minutes to countdown from
                countdown.set(Calendar.HOUR, 0);
                countdown.set(Calendar.MINUTE, 0);
                countdown.set(Calendar.SECOND, 0);
                countdown.set(Calendar.MINUTE, 0);
                countdown.add(Calendar.MINUTE, minutes);
            

            // Start the timer
            public void actionPerformed(ActionEvent e) 
                h = countdown.get(Calendar.HOUR_OF_DAY);
                m = countdown.get(Calendar.MINUTE);
                s = countdown.get(Calendar.SECOND);
                calendar.add(Calendar.SECOND, -1);
                Clock.setText("" + h + ":" + df.format(m) + ":" + df.format(s));

                count--;
                if (count == 0) 
                    System.out.println("Time is up!");
                    refreshTimer.stop();
                

            
        ;

        refreshTimer = new javax.swing.Timer(1000, refreshListener);
        refreshTimer.setInitialDelay(0);
        refreshTimer.start();

    

【问题讨论】:

+1 表示sscce。您的分析器显示什么? 是什么让你认为你有内存泄漏?请尊重 Java 命名约定:变量以小写字母开头。 我一直在查看 Windows 任务管理器中的内存使用情况,并且在运行时内存使用量每秒都会增加,每次使用更多的内存。那就是说一分钟后内存使用量稳定下来。我一直在恐慌。谢谢你的帮助。顺便说一句,命名约定 - 你是指 jtext 框之类的东西吗? @PAUL WHITE:是的,我的意思是你的时钟和定时器变量应该命名为时钟和定时器。变量名中也不应该有下划线 (gbc_label --> gbcLabel) 【参考方案1】:

你的程序在我看来没问题。请注意定期 GC 如何返回基线。相比之下,这个example 会泄漏主机资源。您最多可以考虑在您的setText() 调用中使用StringBuilderMessageFormat

【讨论】:

我看到了同样的行为,但你的回答更漂亮。 ;)【参考方案2】:

这里是稍作改动的代码版本的输出。

14145904
13602336
13059104
12515968
12063384
15263600
14758304
14253408
13748576
13197160
12646360
12057192
11552120
15472056
14960064
14448336
13875144
13363416
12851488
12309120
11681480
15661104
15142224
14602560
14063128
13523464
12983832
12444168
11904536
11364648
15325136
14770848
14230296
13690032
..

我看这里没有内存泄漏。

更改的代码

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.*;

public class Countdown 

    private JFrame Timer;
    private JTextField Clock;
    private JLabel label;
    private JLabel label_1;
    static Calendar calendar = new GregorianCalendar();
    int minutes = 90;
    int count = minutes * 60;
    int h;
    int m;
    int s;
    javax.swing.Timer refreshTimer;

    /**
     * Launch the application.
     */
    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 

            public void run() 
                try 
                    Countdown window = new Countdown();
                    window.Timer.setVisible(true);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        );
    

    /**
     * Create the application.
     */
    public Countdown() 
        initialize();
    

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() 
        Timer = new JFrame();
        Timer.getContentPane().setBackground(new Color(173, 216, 230));
        Timer.setBounds(0, 0, 135, 100);
        Timer.setTitle("Aero Software");
        Timer.setIconImage(Toolkit.getDefaultToolkit().getImage("Files/Icon.jpg"));
        Timer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[]0, 0, 0, 0, 0, 0;
        gridBagLayout.rowHeights = new int[]0, 0, 0, 0, 0, 0, 0, 0;
        gridBagLayout.columnWeights = new double[]0.0, 0.0, 1.0, 1.0, 0.0, Double.MIN_VALUE;
        gridBagLayout.rowWeights = new double[]0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE;
        Timer.getContentPane().setLayout(gridBagLayout);

        label = new JLabel(" ");
        GridBagConstraints gbc_label = new GridBagConstraints();
        gbc_label.insets = new Insets(0, 0, 5, 5);
        gbc_label.gridx = 0;
        gbc_label.gridy = 1;
        Timer.getContentPane().add(label, gbc_label);

        JLabel lblNewLabel = new JLabel("Countdown Timer");
        GridBagConstraints gbc_lblNewLabel = new GridBagConstraints();
        gbc_lblNewLabel.gridwidth = 4;
        gbc_lblNewLabel.insets = new Insets(0, 0, 5, 0);
        gbc_lblNewLabel.gridx = 1;
        gbc_lblNewLabel.gridy = 1;
        Timer.getContentPane().add(lblNewLabel, gbc_lblNewLabel);

        Clock = new JTextField();
        Clock.setFont(new Font("Arial", Font.BOLD, 22));
        Clock.setHorizontalAlignment(JLabel.CENTER);
        GridBagConstraints gbc_Clock = new GridBagConstraints();
        gbc_Clock.fill = GridBagConstraints.HORIZONTAL;
        gbc_Clock.gridwidth = 3;
        gbc_Clock.insets = new Insets(0, 0, 5, 5);
        gbc_Clock.gridx = 1;
        gbc_Clock.gridy = 2;
        Timer.getContentPane().add(Clock, gbc_Clock);
        Clock.setColumns(10);

        label_1 = new JLabel(" ");
        GridBagConstraints gbc_label_1 = new GridBagConstraints();
        gbc_label_1.insets = new Insets(0, 0, 5, 0);
        gbc_label_1.gridx = 4;
        gbc_label_1.gridy = 2;
        Timer.getContentPane().add(label_1, gbc_label_1);

        ActionListener memoryListener = new ActionListener() 
            public void actionPerformed(ActionEvent ae) 
                System.out.println(Runtime.getRuntime().freeMemory());
            
        ;
        Timer memTimer = new Timer(1000,memoryListener);
        memTimer.start();

        // Create countdown timer
        ActionListener refreshListener = new ActionListener() 

            // Ensure time is fotmatted as 7:04 not 7:4
            DecimalFormat df = new DecimalFormat("00");
            Calendar countdown = calendar;

            
                // Zero the time and add the number of minutes to countdown from
                countdown.set(Calendar.HOUR, 0);
                countdown.set(Calendar.MINUTE, 0);
                countdown.set(Calendar.SECOND, 0);
                countdown.set(Calendar.MINUTE, 0);
                countdown.add(Calendar.MINUTE, minutes);
            

            // Start the timer
            public void actionPerformed(ActionEvent e) 
                h = countdown.get(Calendar.HOUR_OF_DAY);
                m = countdown.get(Calendar.MINUTE);
                s = countdown.get(Calendar.SECOND);
                calendar.add(Calendar.SECOND, -1);
                Clock.setText("" + h + ":" + df.format(m) + ":" + df.format(s));

                count--;
                if (count == 0) 
                    System.out.println("Time is up!");
                    refreshTimer.stop();
                

            
        ;

        refreshTimer = new javax.swing.Timer(10, refreshListener);
        refreshTimer.setInitialDelay(0);
        refreshTimer.start();

    

【讨论】:

+1 表示精度。我是否正确地推断出 Windows 任务管理器的结果可能具有误导性? @trashgod 我所听到的一切都表明了这一点,尽管我没有专门测试过。

以上是关于使用 setText 时的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

使用图像时的内存泄漏

处理 CGMutablePath 时的内存泄漏

使用 MPMoviePlayerController 时的内存泄漏

使用 destroy() 命令时的 Perl/Tk 内存泄漏

使用 SharedResourceDictionary 时的内存泄漏

使用 NSURLSession.downloadTaskWithURL 时的内存泄漏