【发布时间】:2011-06-30 11:36:40
【问题描述】:
我已经使用 VS2010 分析器分析了我的应用程序,并启用了对象生命周期收集。
看到名为“Record”的特定结构的大多数实例都被 GC 收集为第 2 代实例,我感到非常惊讶。我很沮丧,因为“记录”结构的实例每个(理论上)应该活不到 500 毫秒。
这些结构是 6xInt32 左右的简单时间序列数据,在流中读取,在大小为 1000 的队列中排队/出队,传递给处理器,该处理器根据数百万“记录”触发一些逻辑依次。我不需要一次保存超过 50 条记录。
所以我的问题是:为什么这些对象的寿命足够长,主要最终成为第二代引用,我可以做些什么来确保它们在每次计算后真的被丢弃。
编辑:
我之所以这样问,是因为我注意到更大样本量(即记录编号)的性能急剧下降:如果 N 需要 T 分钟,2N 需要 2,5T 分钟左右,依此类推。
所以显然存在泄漏某处。
编辑 2: 我的坏:创建结构实例不会导致垃圾收集 我已将其更改为类,到目前为止没有发现任何重大改进。 这次我将使用类而不是结构再次运行分析器)并查看它提供了什么
编辑 3: 许多答案怀疑拳击/拆箱发生在某个地方。 我确实使用类型化的泛型集合和类型化的队列。并且“记录”永远不会作为成员附加到任何类。它们由事件单独处理。 ex-Struct(Now Class)实现了一个接口,并在调用时由它强制转换(这是相当常见的用法),我放弃了该接口。没有改善。
编辑 4:
我再次运行探查器,按类替换 struct。我有相同的结果:
CLASS“记录”的大多数实例最终仍被收集为 Gen2 实例
编辑 5: Record 类的生产者是许多并行的BackGroundWorkers(字节读取器),并且有一个消费者线程在执行一些检查后将 Records 分派给其他方法。此外,我使用 Events 和 Delegates 在不同部分之间进行通信。我不会取消注册这些事件,因为它们在整个过程中都很有用(在那一点上我可能错了)
【问题讨论】:
-
您是否观察到可以连接到 gen2 中的记录的任何性能下降?如果不是,您为什么要担心 GC 的运作方式?
-
它们是结构吗?不是类?
-
@Mika A)问题中写的一半时间与现实略有相似,B)我认为结构,除非作为类的字段,通常不会去到 .NET Fram 的“标准”实现中的堆(因此没有收集 GC)。和 C)在 6x4 字节的结构中,您超出了结构的“标准”“好”限制。
-
关于“为什么”,您超出了良好的限制:每次将结构插入 Queue
时(您是在使用通用集合,还是在老式集合中装箱?),结构被复制(所以24个字节被读取和写入)。每次结构出列时,都会在局部变量(24 字节)中读取和写入。每次函数接收结构作为参数时,都会读取和复制 24 个字节。这就是为什么结构应该很小! (假设不超过 4 sizeof(ptr),所以 32 位系统上的 GUID 是一个好的结构的最大大小)。解释器可以优化。可以,不应该! -
@mika:发生这种情况的原因有很多,解决方案也很多。您没有提供足够的信息来获得真正的帮助。我们只能猜测,而您只能得到猜测。
标签: c# .net performance memory-management