可以使用哪些优化模式来提高垃圾收集器的性能?
我问的原因是我使用 Compact Framework 做了很多嵌入式软件。在慢速设备上,垃圾收集可能会成为一个问题,我想减少垃圾收集器启动的时间,当它启动时,我希望它更快地完成。我还可以看到,使用垃圾收集器而不是反对它可以帮助改进任何 .NET 或 Java 应用程序,尤其是重型 Web 应用程序。
这是我的一些想法,但我没有做过任何基准测试。
- 重用临时类/数组(减少分配计数)
- 将活动对象的数量保持在最低限度(更快的收集)
- 尝试使用结构而不是类
可以使用哪些优化模式来提高垃圾收集器的性能?
我问的原因是我使用 Compact Framework 做了很多嵌入式软件。在慢速设备上,垃圾收集可能会成为一个问题,我想减少垃圾收集器启动的时间,当它启动时,我希望它更快地完成。我还可以看到,使用垃圾收集器而不是反对它可以帮助改进任何 .NET 或 Java 应用程序,尤其是重型 Web 应用程序。
这是我的一些想法,但我没有做过任何基准测试。
关键是要了解 CF GC 是如何进行分配的。它是一个简单的标记和清除、非分代 GC,具有特定的算法来触发 GC,以及在收集后什么会导致压缩和/或倾斜。在应用程序级别几乎无法控制 GC(唯一可用的方法是收集,它的使用非常有限,因为无论如何您都无法强制压缩)。
对象重用是一个好的开始,但简单地保持对象数量较少可能是最好的工具之一,因为任何收集操作都必须遍历所有根。保持步行距离是一个好主意。如果压缩正在杀死你,那么防止段碎片将有所帮助。大于 64k 的对象在这方面可能会有所帮助,因为它们有自己的段,并且与较小的对象区别对待。
To really understand how the CF GC works, I'd recommend watching the MSDN Webcast on CF memory management.
最重要的一个方面是最小化分配率。每当分配一个对象时,它就需要稍后进行 GC。现在当然,如果对象很小或寿命很短,它将被钉在年轻代中(假设 GC 是分代的)。大型对象倾向于直接进入终身领域。但是完全避免收集会更好。
此外,如果您可以将东西扔到堆栈上,您将享受到更少的 GC 压力。您可以尝试玩弄 GC 选项,但我认为使用分配分析器会更好地帮助您,这样您就可以找到导致问题的地方。
应该注意的是标准库和框架的重量。你包裹几个对象,它会很快填满。请记住,每当 GC 堆上发生某些事情时,它通常会为 GC 簿记使用更多空间。因此,您单独分配的 1000 个指针比相同指针的数组/向量大得多,因为后者可以共享 GC 簿记。另一方面,后者可能会活得更久。
一个重要的事实是尽可能缩短对象的生命周期。
结构与类问题是一个复杂的问题……例如,您可能很容易最终使用更多的堆栈空间。而且您当然不想要可变结构。但其他点似乎是明智的,只要你不弯曲设计以适应它。
[编辑]另一个常见的问题是字符串连接;如果您在循环中进行连接,请使用 StringBuilder,它将删除很多中间字符串。可能是 GC 忙于收集所有废弃的字符串?
另一种选择是在应用程序的非高峰时间使用 GC.Collect() 手动收集垃圾(假设这在 CF 中可用)。这可以减少稍后在您的应用程序中清理所需的对象。
我在Rotor 2.0上听到了.NET Rocks节目。如果你真的是铁杆,你可以下载Rotor,调整源代码,然后使用你自己修改过的垃圾收集器。
无论如何,该播客有一些关于 GC 的重要信息。我强烈推荐听一下。