如何避免实时.NET应用程序中的垃圾回收?

时间:2020-03-05 18:59:24  来源:igfitidea点击:

我正在编写一个金融C应用程序,该应用程序从网络接收消息,根据消息类型将它们转换为不同的对象,最后将应用程序业务逻辑应用于它们。

关键是,在应用了业务逻辑之后,我非常确定我将不再需要该实例。与其等待垃圾收集器释放它们,不如明确地"删除"它们。

在C#中,是否有更好的方法可以做到这一点?我应该使用一个对象池来重用总是相同的一组实例,还是有更好的策略?

目标是避免垃圾收集在时间紧迫的过程中使用任何CPU。

解决方案

回答

不要立即删除它们。为每个对象调用垃圾回收器是一个坏主意。通常,我们真的根本不想弄乱垃圾收集器,即使时间紧迫的进程也是如此,如果它们是如此敏感,它们就正在等待比赛条件的发生。

但是,如果我们知道自己的应用将有繁忙时段与轻负载时段,则可以在达到轻负载时段时尝试使用更通用的GC.Collect(),以鼓励在下一个繁忙时段之前进行清理。

回答

在这里查看:http://msdn.microsoft.com/en-us/library/bb384202.aspx

我们可以告诉垃圾收集器我们目前正在做一些重要的事情,它将对我们有好处。

回答

池中每种类型的实例数量可能有限,然后重复使用已经完成的实例。池的大小取决于我们将处理的消息量。

回答

强制使用GC.Collect()通常是个坏主意,请让GC发挥其最大作用。听起来最好的解决方案是使用一个对象池,如果有必要,我可以成功地使用该对象池。

这样,我们不仅可以避免垃圾收集,还可以避免常规分配成本。

最后,我们确定GC会导致我们遇到问题吗?在实施任何可能导致不必要的工作的省钱的解决方案之前,我们可能应该测量并证明这一点!

回答

从理论上讲,如果CPU负载过重或者除非确实需要,GC不应运行。但是如果有必要,我们可能只想将所有对象都保存在内存中(也许是单例实例),除非准备好就不要清理它们。这可能是保证GC运行的唯一方法。

回答

如果绝对时间紧迫,那么我们应该使用确定性平台,例如C / C ++。即使调用GC.Collect()也会生成CPU周期。

问题始于建议我们要节省内存但要摆脱对象的建议。这是空间关键的优化。我们需要决定自己真正想要的是什么,因为GC比人工方法更能优化这种情况。

回答

对垃圾收集器的行为有一个很好的了解和感觉,我们将了解为什么不建议我们在这里考虑什么。除非我们真的很喜欢CLR花很多时间在内存中重新排列对象。

  • http://msdn.microsoft.com/zh-CN/magazine/bb985010.aspx
  • http://msdn.microsoft.com/zh-CN/magazine/bb985011.aspx

回答

为何每次收到消息时都不会创建对象的新实例,而是为什么不重用已经使用过的对象呢?这样,我们就不会与垃圾收集器抗争,堆内存也不会碎片化。**

对于每种消息类型,我们可以创建一个池来保存未使用的实例。每当收到网络消息时,我们都会查看消息类型,从适当的池中拉出一个等待的实例,然后应用业务逻辑。之后,将消息对象的实例放回其池中。

我们很可能希望通过实例"延迟加载"池,以便代码轻松扩展。因此,池类将需要检测何时已拉空实例,并在分发之前将其填满。然后,当调用代码将其放回池中时,它就是一个真实实例。

**"对象池是一种使用模式,它允许对象被重用而不是分配和释放,这有助于防止堆碎片以及昂贵的GC压缩。"

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

回答

尝试去猜测垃圾收集器通常是一个非常糟糕的主意。在Windows上,垃圾收集器是一个世代的垃圾收集器,可以依靠它来提高效率。该通用规则有一些值得注意的例外,最常见的是一次性事件的发生,我们知道事实是,一旦将对象提升到Gen2(寿命最长),就会导致很多旧对象死亡。挂在身边。

在我们提到的情况下,听起来好像我们正在生成许多短期对象,这些对象将导致Gen0集合。无论如何,这些情况相对经常发生,并且是最有效的。如果愿意,可以通过使用可重用的对象池来避免它们,但是最好在执行此类操作之前确定GC是否是性能问题,而CLR分析器是执行此操作的工具。

应当注意的是,紧凑型框架(可在Xbox 360和移动平台上运行)上的不同.N​​ET框架上的垃圾收集器有所不同,它是非世代的GC,因此,我们必须更加小心哪些垃圾程序生成。

回答

我们打了自己-使用对象池并重复使用这些对象。调用这些对象的语义将需要隐藏在工厂外观的后面。我们将需要以某种预定义的方式来扩展池。每次达到极限时,大小可能增加一倍-高水位算法或者固定百分比。我强烈建议我们不要调用GC.Collect()。

当池中的负载足够低时,我们可以缩小池,最终将触发垃圾回收-让CLR担心它。

回答

从它的声音看来,我们似乎在谈论确定性终结(C ++中的析构函数),而C#中不存在。我们将在Cis 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;
    }
}

回答

该应用程序强度有多大?我编写了一个应用程序,该应用程序以8KB的块捕获3个声卡(Managed DirectX,44.1KHz,立体声,16位),然后将3个流中的2个通过TCP / IP发送到另一台计算机。 UI会为3个通道中的每个通道渲染一个音频电平表和(平滑的)滚动标题/艺术家。它可以在XP,1.8GHz,512MB等PC上运行。该应用程序使用大约5%的CPU。

我没有手动调用GC方法。但是我确实不得不调整一些浪费的东西。我使用RedGate的Ant探查器来细化那些浪费的部分。一个很棒的工具!

我想使用一个预先分配的字节数组池,但是托管的DX程序集在内部分配字节缓冲区,然后将其返回给App。原来,我没有必要。

回答

"目标是避免垃圾收集在时间紧迫的过程中使用任何CPU"

问:如果时间紧迫,意思是我们正在听一些深奥的硬件,我们不能错过这个中断吗?

答:如果是这样,那么就不要使用该语言,我们需要使用Assembler,C或者C ++。

问:如果到了关键时刻,意思是尽管管道中有很多消息,并且我们不想让垃圾收集器放慢速度?

答:如果是这样,我们就不必担心了。从对象的声音来看,它们的寿命很短,这意味着垃圾收集器将非常有效地回收它们,而性能不会明显滞后。

但是,唯一可以确定的方法就是对其进行测试,将其设置为运行过夜以处理恒定的测试消息流,如果性能统计信息可以在GC启动时发现(即使我们可以发现它,如果它真的很重要,我会更加惊讶)。