【问题标题】:c# Memory leak system.timersc#内存泄漏system.timers
【发布时间】:2012-05-26 02:58:18
【问题描述】:

我目前正在编写一个与 I/O 设备交互的程序,并且需要一种每隔 x 秒轮询一次设备的方法,以检查输入/输出连接。为此,我使用了一个按钮,该按钮使用计时器和计时器事件句柄创建了一个线程来进行轮询。但是,我注意到在任务管理器中,随着时间的流逝,它正在慢慢消耗更多的内存。以下是(我认为)与我的问题相关的一些 sn-ps 代码。

创建线程按钮:

private void btnConnect_Click(object sender, EventArgs e)
    {
        new Thread(start).Start();
    }

包含计时器的线程:

        public void start()
    {
        timer = new System.Timers.Timer(1000);
        timer.Elapsed += new ElapsedEventHandler(timerElapsed);
        timer.Enabled = true;
    }

ElapsedEventHandler:

public void timerElapsed(object sender, ElapsedEventArgs e)
    {
        connect();
    }

最后是方法connect();:

public void connect()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(txtIPseg1.Text + "." + txtIPseg2.Text + "." + txtIPseg3.Text + "." + txtIPseg4.Text);
        int Port = int.Parse(txtPort.Text);
        string address = sb.ToString();

        //send data
        byte[] bData = new byte[71];
        bData[0] = 240;
        bData[1] = 240;
        bData[2] = 0;
        bData[3] = 1;
        bData[68] = 240;
        bData[69] = 240;
        bData[70] = this.CalculateCheckSum(bData);

        try
        {
            byte[] result = this.SendCommandResult(address, Port, bData, 72);
            if (result != null)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    txtOutput1.Text = (result[4] == 0x00 ? "HIGH" : "LOW"); // runs on UI thread
                });
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

    }

我很确定泄漏要么来自计时器,要么来自方法 connect(); 中使用的匿名委托,有人有什么想法吗?

【问题讨论】:

  • 你确定不是GC没有运行?
  • 你确定你有内存泄漏吗?也许 GC 还没有运行,所以没有理由恐慌。
  • 这与推测的内存泄漏无关,但是为什么要启动一个新线程只是为了启动一个计时器呢?无论如何,一旦创建计时器,线程似乎就结束了。
  • GC 多久运行一次...?我们在谈分钟吗?我会让它运行 10 分钟,看看会发生什么。
  • System.Timers.Timer 默认启动自己的线程。您当前的代码启动一个线程,该线程启动一个线程。你到底做了多少调试?我向您保证 System.Timers.Timer 中没有内存泄漏,至少不是您认为您可能发现的。

标签: c# memory delegates timer memory-leaks


【解决方案1】:

每次单击按钮时,您都会创建一个新计时器。此外,您没有保留对它的引用,因此它将被垃圾收集器销毁。无需在新线程上启动计时器,因为计时器会在新线程上引发 Elapsed 事件。

class Form1 ...
{
    private System.Timers.Timer timer = null;

    public void start()
    {
    if (timer == null)
        {
        timer = new System.Timers.Timer(1000);
        timer.Elapsed += new ElapsedEventHandler(timerElapsed);
        }
    timer.Enabled = true;
    }

    ...
}

就内存泄漏而言,我不会仅仅因为您在应用程序运行时看到内存使用量看似随机波动就认为存在内存泄漏。当您在 .NET 等复杂框架中运行时,这是正常行为。每次计时器触发时,它都会调用您的 connect 方法来创建新对象。这些对象将保留在内存中,直到垃圾收集器开始清理它们。因此,看到内存慢慢上升,然后在几分钟后突然又下降,这并不奇怪。我不会怀疑有问题,除非它在很长一段时间内不断失控。

您使用 StringBuilder 类的方式也很奇怪。你在做什么:

StringBuilder sb = new StringBuilder();
sb.Append(txtIPseg1.Text + "." + txtIPseg2.Text + "." + txtIPseg3.Text + "." + txtIPseg4.Text);
string address = sb.ToString();

并没有比仅仅这样做更好(实际上它更糟):

string address = txtIPseg1.Text + "." + txtIPseg2.Text + "." + txtIPseg3.Text + "." + txtIPseg4.Text;

如果您正在寻找一种更高效且可能更易于阅读的方法,请尝试类似的方法

string address = string.Format("{0}.{1}.{2}.{3}", txtIPseg1.Text, txtIPseg2.Text, txtIPseg3.Text, txtIPseg4.Text);

然而,没有什么是会导致内存泄漏的,所以除非你有充分的理由这么想,否则我不会担心。

【讨论】:

  • 实现了你的方法(效果很好),我仍然在缓慢增加内存,大约每秒 4k。您对正常行为可能是正确的,所以我会让它运行一段时间,看看是否发生任何荒谬的事情。我也刚刚注意到在开始该过程之前也会发生这种内存使用情况。也许只是我偏执。
  • 你查看的是进程的内存使用量还是总内存使用量?通过查看总内存使用情况,您不会获得任何有用的信息。
  • @Shane'Shamus'Coulter 每次计时器触发时,它都会调用您的连接方法来创建新对象。这些对象将保留在内存中,直到垃圾收集器开始清理它们。因此,看到内存慢慢上升,然后在几分钟后突然又下降,这并不奇怪。我不会怀疑有问题,除非它在很长一段时间内不断失控。
  • 我要让它运行一会儿。单击连接后,它从大约 4900k 开始。完成一些测试后,我会回复你。
  • 在很短的时间之后,我想如果 GC 会采取行动的话。现在它已经上升到超过 7000k,这在 7 分钟内内存增加了 40%。为了测试它来自定时器,我尝试更改按钮,以便它手动调用 connect() onclick,这已经停止了内存使用量的增加,就像使用定时器一样。可能是因为计时器越来越大并且需要更多内存来保持当前时间?重置计时器有什么作用吗?
猜你喜欢
  • 2016-01-27
  • 2010-11-11
  • 2017-02-18
  • 1970-01-01
  • 2015-11-19
  • 2014-11-01
  • 2020-03-31
  • 2016-03-15
  • 2015-07-25
相关资源
最近更新 更多