我正在编写一个金融C#应用程序,它接收来自网络的消息,根据消息类型将它们转换为不同的对象,最后将应用程序业务逻辑应用于它们。
重点是在应用业务逻辑之后,我非常确定我将永远不再需要这个实例。我不想等待垃圾收集器释放它们,而是明确地“删除”它们。
有没有更好的方法在C#中这样做,我应该使用一个对象池来重复使用同一组实例,还是有更好的策略。
目标是避免垃圾收集在时间关键过程中使用任何CPU。
答案 0 :(得分:21)
不要马上删除它们。为每个对象调用垃圾收集器是个坏主意。通常你真的根本不想弄乱垃圾收集器,即使是时间关键的进程只是竞争条件等待发生,如果他们那么敏感。
但是如果你知道你的应用程序对于轻载时段很忙,那么当你到达一个光照期以鼓励在下一个繁忙时段之前进行清理时,你可以尝试更通用的GC.Collect()。
答案 1 :(得分:18)
请看这里:http://msdn.microsoft.com/en-us/library/bb384202.aspx
你可以告诉垃圾收集器你现在正在做一些关键的事情,它会对你很好。
答案 2 :(得分:11)
你自己打了 - 使用一个对象池并重用这些对象。对这些对象的调用的语义需要隐藏在工厂外观后面。您需要以某种预定义的方式扩展池。每次达到极限时可能会增加一倍的尺寸 - 高水算法或固定百分比。我强烈建议你不要调用GC.Collect()。
当您的池上的负载变得足够低时,您可以缩小池并最终触发垃圾收集 - 让CLR担心它。
答案 3 :(得分:7)
尝试对垃圾收集器进行二次猜测通常是一个非常糟糕的主意。在Windows上,垃圾收集器是a generational one,并且可以依赖它非常有效。这个一般规则有一些值得注意的例外 - 最常见的是一个事件的发生,你知道一个事实会导致很多旧的对象死亡 - 一旦对象被提升为Gen2(最长寿命)他们往往会四处闲逛。
在你提到的情况下,你听起来好像在生成一些短命的对象 - 这些将导致Gen0集合。无论如何,这些都经常发生,并且是最有效的。如果您愿意,可以通过拥有可重用的对象池来避免它们,但最好在采取此类操作之前确定GC是否是性能问题 - CLR分析器是执行此操作的工具。
应该注意的是,垃圾收集器在不同的.NET框架上是不同的 - 在紧凑的框架上(在Xbox 360和移动平台上运行)它是一个非世代GC,因此你必须更多小心程序生成的垃圾。
答案 4 :(得分:5)
强制GC.Collect()通常是一个坏主意,让GC做它最擅长的事情。听起来最好的解决方案是使用可以在必要时增长的对象池 - 我已经成功使用了这种模式。
这样,您不仅可以避免垃圾收集,还可以避免常规分配成本。
最后,您确定GC导致您出现问题吗?在实施任何节能解决方案之前,您应该测量并证明这一点 - 您可能会导致自己不必要的工作!
答案 5 :(得分:5)
“目标是避免垃圾收集在时间关键过程中使用任何CPU”
问:如果时间紧迫,你的意思是说你正在听一些神秘的硬件,你不能错过中断?
答:如果是这样,那么C#就不是要使用的语言,你需要汇编程序,C或C ++。
问:如果时间严重,那么你的意思是管道中有很多消息,你不想让垃圾收集器放慢速度吗?
A:如果是这样,你会不必要地担心。通过物体的声音,你的物体非常短暂,这意味着垃圾收集器将非常有效地回收它们,而没有任何明显的性能滞后。
但是,唯一可以确定的方法是测试它,将其设置为隔夜处理一系列测试消息,如果你的GC性能统计数据可以在GC启动时发现,我会感到震惊(甚至如果你能发现它,如果真的很重要,我会更加惊讶。)
答案 6 :(得分:3)
对垃圾收集器的行为有一个很好的理解和感受,并且您将理解为什么不推荐您在这里想到的。除非你真的希望CLR花时间在内存中重新排列对象很多。
答案 7 :(得分:3)
应用程序的密集程度如何?我写了一个应用程序,以8KB块捕获3个声卡(Managed DirectX,44.1KHz,立体声,16位),并通过TCP / IP将3个流中的2个发送到另一台计算机。 UI为3个频道中的每个频道呈现音频电平表和(平滑)滚动标题/艺术家。这适用于具有XP,1.8GHz,512MB等的PC。该应用程序使用大约5%的CPU。
我没有动手调用GC方法。但我确实需要调整一些浪费的东西。我使用RedGate的Ant分析器来磨练浪费的部分。一个很棒的工具!
我想使用预先分配的字节数组池,但托管DX程序集在内部分配字节缓冲区,然后将其返回给App。事实证明,我没有。
答案 8 :(得分:2)
如果它绝对是时间关键,那么你应该使用像C / C ++这样的确定性平台。即使调用GC.Collect()也会生成CPU周期。
你的问题始于你想要节省内存但摆脱对象的建议。这是一个空间关键优化。您需要确定您真正想要的是什么,因为GC比人类更善于优化这种情况。
答案 9 :(得分:2)
从它的声音来看,似乎你在谈论确定性终结(C ++中的析构函数),这在C#中是不存在的。你在C#中找到的最接近的东西是Disposable模式。基本上你实现了 IDisposable 界面。
基本模式是:
public class MyClass: IDisposable
{
private bool _disposed;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing )
{
if( _disposed )
return;
if( disposing )
{
// Dispose managed resources here
}
_disposed = true;
}
}
答案 10 :(得分:1)
您可以在池中拥有有限数量的每种类型的实例,并重用已经完成的实例。池的大小取决于您要处理的消息量。
答案 11 :(得分:1)
不是每次收到消息时都创建对象的新实例,为什么不重用已经使用过的对象?这样你就不会与垃圾收集器作斗争,你的堆内存也不会碎片化。**
对于每种消息类型,您可以创建一个池来保存未使用的实例。每当收到网络消息时,您都会查看消息类型,从适当的池中提取等待的实例并应用业务逻辑。之后,将该消息对象的实例放回其池中。
您很可能希望使用实例“延迟加载”池,以便您的代码可以轻松扩展。因此,您的池类将需要检测何时已拉出空实例并在将其移出之前将其填满。然后当调用代码将它放回池中时,它就是一个真实的实例。
**“对象池是一种使用模式,允许重用对象而不是分配和释放对象,这有助于防止堆碎片以及昂贵的GC压缩。”
答案 12 :(得分:0)
理论上,如果您的CPU处于高负载或除非确实需要,GC不应运行。但是如果你必须这样做,你可能只想将所有对象保存在内存中,也许只是一个单例实例,除非你准备好,否则永远不要清理它们。这可能是保证GC运行的唯一方法。