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

发布于 2024-07-05 15:38:34 字数 243 浏览 7 评论 0原文

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

关键是,在应用业务逻辑之后,我非常确定我将永远不再需要这个实例。 我不想等待垃圾收集器释放它们,而是想显式“删除”它们。

在 C# 中是否有更好的方法来做到这一点,我应该使用对象池来始终重用同一组实例,还是有更好的策略。

目标是避免在时间关键的进程中使用任何 CPU 进行垃圾收集。

I'm writting a financial C# application which receive messages from the network, translate them into different object according to the message type and finaly apply the application business logic on them.

The point is that after the business logic is applied, I'm very sure I will never need this instance again. Rather than to wait for the garbage collector to free them, I'd like to explicitly "delete" them.

Is there a better way to do so in C#, should I use a pool of object to reuse always the same set of instance or is there a better strategy.

The goal being to avoid the garbage collection to use any CPU during a time critical process.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

阳光①夏 2024-07-12 15:38:34

“目标是避免垃圾收集期间使用任何 CPU
时间紧迫的过程”

问:如果时间关键,你的意思是你正在听一些深奥的硬件,并且你不能错过中断?

答: 如果是这样,那么 C# 不是要使用的语言,您需要使用汇编语言、C 或 C++

问: 如果按时间为 Critical,您的意思是管道中有很多消息,并且您不想让垃圾收集器减慢速度?

答:如果是这样,您的担心是不必要的,这意味着垃圾收集器将回收。它们非常有效,没有任何明显的性能滞后

但是,唯一确定的方法是对其进行测试,将其设置为整夜运行,处理持续的测试消息流,如果您的性能统计数据可以发现,我会感到震惊。当 GC 启动时(即使你能发现它,如果它真的很重要,我会更加惊讶)。

"The goal being to avoid the garbage collection to use any CPU during
a time critical process"

Q: If by time critical, you mean you're listening to some esoteric piece of hardware, and you can't afford to miss the interrupt?

A: If so then C# isn't the language to use, you want Assembler, C or C++ for that.

Q: If by time Critical, you mean while there are lots of messages in the pipe, and you don't want to let the Garbage collector slow things down?

A: If so you are worrying needlessly. By the sounds of things your objects are very short lived, this means the garbage collector will recycle them very efficiently, without any apparent lag in performance.

However, the only way to know for sure is test it, set it up to run overnight processing a constant stream of test messages, I'll be stunned if you your performance stats can spot when the GC kicks in (and even if you can spot it, I'll be even more surprised if it actually matters).

删除→记忆 2024-07-12 15:38:34

强制 GC.Collect() 通常是一个坏主意,让 GC 去做它最擅长的事情。 听起来最好的解决方案是使用一个可以在必要时增长的对象池 - 我已经成功地使用了这种模式。

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

最后,您确定 GC 给您带来了问题吗? 在实施任何节省性能的解决方案之前,您可能应该测量并证明这一点 - 您可能会给自己带来不必要的工作!

Forcing a GC.Collect() is generally a bad idea, leave the GC to do what it does best. It sounds like the best solution would be to use a pool of objects that you can grow if necessary - I've used this pattern successfully.

This way you avoid not only the garbage collection but the regular allocation cost as well.

Finally, are you sure that the GC is causing you a problem? You should probably measure and prove this before implementing any perf-saving solutions - you may be causing yourself unnecessary work!

对你再特殊 2024-07-12 15:38:34

不要立即删除它们。 为每个对象调用垃圾收集器是一个坏主意。 通常,您真的根本不想打扰垃圾收集器,即使时间关键的进程也只是等待发生的竞争条件(如果它们如此敏感)。

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

Don't delete them right away. Calling the garbage collector for each object is a bad idea. Normally you really don't want to mess with the garbage collector at all, and even time critical processes are just race conditions waiting to happen if they're that sensitive.

But if you know you'll have busy vs light load periods for your app, you might try a more general GC.Collect() when you reach a light period to encourage cleanup before the next busy period.

梦里兽 2024-07-12 15:38:34

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

你可以告诉垃圾收集器你现在正在做一些关键的事情,它会尽力对你友善。

Look here: http://msdn.microsoft.com/en-us/library/bb384202.aspx

You can tell the garbage collector that you're doing something critical at the moment, and it will try to be nice to you.

沙沙粒小 2024-07-12 15:38:34

你自己动手——使用一个对象池并重用这些对象。 对这些对象的调用的语义需要隐藏在工厂外观后面。 您需要以某种预定义的方式扩大池。 也许每次达到极限时都会将大小加倍——高水位算法,或固定百分比。 我真的强烈建议你不要调用 GC.Collect()。

当池上的负载变得足够低时,您可以缩小池,这最终会触发垃圾收集——让 CLR 担心它。

You hit in yourself -- use a pool of objects and reuse those objects. The semantics of the calls to those object would need to be hidden behind a factory facade. You'll need to grow the pool in some pre-defined way. Perhaps double the size everytime it hits the limit -- a high water algorithm, or a fixed percentage. I'd really strongly advise you not to call GC.Collect().

When the load on your pool gets low enough you could shrink the pool and that will eventually trigger a garbage collection -- let the CLR worry about it.

安穩 2024-07-12 15:38:34

试图事后猜测垃圾收集器通常是一个非常糟糕的主意。 在 Windows 上,垃圾收集器是一代垃圾收集器,并且可以值得信赖的是非常有效的。 这个一般规则有一些值得注意的例外 - 最常见的是一次性事件的发生,您知道该事件会导致许多旧对象死亡 - 一旦对象被提升到 Gen2(寿命最长的)他们倾向于闲逛。

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

应该注意的是,垃圾收集器在不同的 .NET 框架上是不同的 - 在紧凑框架(在 Xbox 360 和移动平台上运行)上,它是非分代 GC,因此您必须更加小心你的程序产生的垃圾。

Attempting to second-guess the garbage collector is generally a very bad idea. On Windows, the garbage collector is a generational one and can be relied upon to be pretty efficient. There are some noted exceptions to this general rule - the most common being the occurrence of a one-time event that you know for a fact will have caused a lot of old objects to die - once objects are promoted to Gen2 (the longest lived) they tend to hang around.

In the case you mention, you sound as though you are generating a number of short-lived objects - these will result in Gen0 collections. These happen relatively often anyway, and are the most efficient. You could avoid them by having a reusable pool of objects, if you prefer, but it is best to ascertain for certain if GC is a performance problem before taking such action - the CLR profiler is the tool for doing this.

It should be noted that the garbage collector is different on different .NET frameworks - on the compact framework (which runs on the Xbox 360 and on mobile platforms) it is a non-generational GC and as such you must be much more careful about what garbage your program generates.

爱要勇敢去追 2024-07-12 15:38:34

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

我没有手动调用 GC 方法。 但我确实必须调整一些浪费的东西。 我使用 RedGate 的 Ant 分析器来研究浪费的部分。 一个很棒的工具!

我想使用预先分配的字节数组池,但托管 DX 程序集在内部分配字节缓冲区,然后将其返回给应用程序。 事实证明我不必这样做。

How intensive is the app? I wrote an app that captures 3 sound cards (Managed DirectX, 44.1KHz, Stereo, 16-bit), in 8KB blocks, and sends 2 of the 3 streams to another computer via TCP/IP. The UI renders an audio level meter and (smooth) scrolling title/artist for each of the 3 channels. This runs on PCs with XP, 1.8GHz, 512MB, etc. The App uses about 5% of the CPU.

I stayed clear of manually calling GC methods. But I did have to tune a few things that were wasteful. I used RedGate's Ant profiler to hone in on the wasteful portions. An awesome tool!

I wanted to use a pool of pre-allocated byte arrays, but the managed DX Assembly allocates byte buffers internally, then returns that to the App. It turned out that I didn't have to.

ι不睡觉的鱼゛ 2024-07-12 15:38:34

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

你的问题首先建议你想要节省内存但摆脱对象。 这是一个空间关键的优化。 您需要决定您真正想要什么,因为 GC 比人类更擅长优化这种情况。

If it is absolutely time critical then you should use a deterministic platform like C/C++. Even calling GC.Collect() will generate CPU cycles.

Your question starts off with the suggestion that you want to save memory but getting rid of objects. This is a space critical optimization. You need to decide what you really want because the GC is better at optimizing this situation than a human.

坦然微笑 2024-07-12 15:38:34

充分了解和感受垃圾收集器的行为方式,您就会明白为什么不推荐您在这里想到的内容。 除非您确实希望 CLR 花费大量时间重新排列内存中的对象。

Get a good understanding and feel on how the Garbage Collector behaves, and you will understand why what you are thinking of here is not recommended. unless you really like the CLR to spend time rearranging objects in memory alot.

弄潮 2024-07-12 15:38:34

与其每次收到消息时都创建一个新的对象实例,为什么不重用已经使用过的对象呢? 这样,您就不会与垃圾收集器作斗争,并且堆内存也不会产生碎片。**

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

您很可能希望使用实例“延迟加载”池,以便您的代码轻松扩展。 因此,您的池类需要检测空实例何时被拉出并在分发之前将其填充。 然后,当调用代码将其放回池中时,它就是一个真实的实例。

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

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

Instead of creating a new instance of an object every time you get a message, why don't you reuse objects that have already been used? This way you won't be fighting against the garbage collector and your heap memory won't be getting fragmented.**

For each message type, you can create a pool to hold the instances that are not in use. Whenever you receive a network message, you look at the message type, pull a waiting instance out of the appropriate pool and apply your business logic. After that, you put that instance of the message object back into it's pool.

You will most likely want to "lazy load" your pool with instances so your code scales easily. Therefore, your pool class will need to detect when a null instance has been pulled and fill it up before handing it out. Then when the calling code puts it back in the pool it's a real instance.

** "Object pooling is a pattern to use that allows objects to be reused rather than allocated and deallocated, which helps to prevent heap fragmentation as well as costly GC compactions."

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

定格我的天空 2024-07-12 15:38:34

您可以在池中拥有有限数量的每种类型的实例,并重用已完成的实例。 池的大小取决于您要处理的消息量。

You could have a limited amount of instances of each type in a pool, and reuse the already done with instances. The size of the pool would depend on the amount of messages you'll be processing.

泛泛之交 2024-07-12 15:38:34

从声音上看,您似乎在谈论确定性终结(C++ 中的析构函数),而 C# 中不存在这种确定性终结。 C# 中最接近的就是一次性模式。 基本上,您实现了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;
    }
}

From the sound of it, it seems like you're talking about deterministic finalization (destructors in C++), which doesn't exist in C#. The closest thing that you will find in C# is the Disposable pattern. Basically you implement the IDisposable interface.

The basic pattern is this:

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;
    }
}
薔薇婲 2024-07-12 15:38:34

理论上,如果您的 CPU 负载过重或除非确实需要,则 GC 不应运行。 但如果必须的话,您可能只想将所有对象保留在内存中,也许是一个单例实例,并且除非准备好,否则永远不要清理它们。 这可能是保证 GC 何时运行的唯一方法。

In theory the GC shouldn't run if your CPU is under heavy load or unless it really needs to. But if you have to, you may want to just keep all of your objects in memory, perhaps a singleton instance, and never clean them up unless you're ready. That's probably the only way to guarantee when the GC runs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文