保持'new'使EventHandler结果产生太多垃圾

本文关键字:太多 EventHandler new 保持 结果 | 更新日期: 2023-09-27 18:17:39

我正在编写一个c# Windows应用程序来从串行端口获取输入,我参考如下所示的示例代码:

private void serialPort_DataReceived_1 (object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    if (this.serialPort.IsOpen == true)
        {
            this.BeginInvoke(new EventHandler(delegate { this.textBox1.AppendText(this.serialPort.ReadExisting()); }));                
        }
}

代码运行良好。但我只是想知道如果BeginInvoke运行了很多次,它会在内存中创建许多"新的eventandler"吗?

保持'new'使EventHandler结果产生太多垃圾

一些幕后计算。串行端口最高运行速率为115,000波特,也就是每秒11,500字节。绝对最坏的情况是每个单独的字节都有DataReceived fire,技术上是可能的,尽管考虑到Control.BeginInvoke()的开销,这将是一个非常糟糕的结果。有些事你得解决

对于32位版本的CLR,委托对象需要32字节。因此,您最多将消耗11500 x 32 = 368 KB/秒的堆内存。这些都是在第0代堆中分配的,并且几乎可以保证被第0代垃圾收集完全删除,因为委托对象存活的时间很短。

默认的gen #0堆大小是2兆字节。当堆处于压力下时,它会增长,当第0代收集经常将对象移动到第1代时,它会增加到8兆字节或更多。在这种情况下不太可能,因为委托对象是如此短暂。

因此,第0代堆需要2048/368 = 5.4秒才能被委托对象填充并触发垃圾收集。第0代收集平均需要5毫秒,尽管你不太可能需要这么长时间,因为需要移动的幸存对象太少了。

因此,在最坏情况下,用于收集这些对象的处理时间百分比为0.005/5.4 = 0.09%。

每个匿名委托完成后都有资格进行垃圾收集,所以这可能不是什么大问题。

如果你真的担心它,你可以创建一个单一的EventHandler作为类字段:

EventHandler readFromSerialHandler;

然后在构造函数中将其设置为

readFromSerialHandler = (s, e) => 
           this.textBox1.AppendText(this.serialPort.ReadExisting());

那么现在你的DataReceived处理程序将简单地是:

private void serialPort_DataReceived_1 (object sender, SerialDataReceivedEventArgs e) {
    if (this.serialPort.IsOpen == true) {
            this.BeginInvoke(this.readFromSerialHandler);                
    }
}