c#过度垃圾回收-大字符串,G2压力

本文关键字:字符串 G2 压力 | 更新日期: 2023-09-27 18:02:24

我正在用c#编写一个大容量的web服务,运行在win2k8上的64位IIS上。NET 4.5),它可以处理XML有效负载,并对大小对象进行各种操作(其中大对象主要是字符串,有些超过85k(因此进入LOH))。请求是无状态的,内存使用随着时间的推移保持稳定。每个请求都有大量的内存被分配和释放,没有内存泄漏。

以每秒最多25个事务的速度运行,平均调用持续时间为5秒,根据两个分析工具,它在GC中花费了40-60%的时间,并且perfmon显示在5秒内稳定地进行了20次G0和G1收集,在5秒内进行了15次G2收集——这意味着(我们认为)很多我们希望留在G0中的数据过早地提升到G2。我所读到的一切都表明这是非常过分的。我们期望系统能够以高于25tps的吞吐量执行,并假设GC活动正在阻止这种情况发生。

处理请求的机器有大量内存(16GB),负载下的应用程序在一个小时内最多消耗1GB内存。我知道一个更大的堆不一定会使事情变得更好,但是有空闲的内存。

我很欣赏这些细节(如果时间允许,我会尝试用一个简单的应用程序重新创建条件)-但是有人能解释为什么我们看到这么多G2 GC活动吗?我应该关注LOH吗?人们一直告诉我CLR的GC"适应"您的负载,但在这种情况下它并没有改变它的行为,而且,与其他运行时不同,我似乎无法对其进行调优(我尝试过工作站GC,但几乎没有可观察到的差异)。

c#过度垃圾回收-大字符串,G2压力

Microsoft决定设计String类,以便所有字符串都作为一个整体字符序列存储在内存中。虽然这对于某些使用模式工作得很好,但对于其他使用模式却非常糟糕。

我发现非常有用的一件事是尽可能避免创建String的实例。如果一个方法将经常用于操作所提供字符串的一部分,并且将反过来要求其他方法对其部分进行操作,则这些方法应该接受指定其操作的String范围的参数。这将避免需要第一个方法的调用者使用Subst来构建一个新的String来对方法进行操作,并且将避免需要让方法调用Subst来将字符串的部分提供给其调用者。在我使用这种技术的某些情况下,可以用 0 代替创建数千个String实例(有些相当大)。

CLR的GC"适应"你的负载

它不知道你愿意容忍多少内存作为开销。在这里,你可能想给应用程序5GB的堆,这样收集就会少得多。GC没有内置的调优旋钮(主观注意:这很遗憾)。

可以通过在短时间内使用一种低延迟模式来强制使用更大的堆大小。这将导致GC努力避免G2收集。当内存使用达到5GB时,监控内存使用并禁用低延迟模式。

这是一个冒险的策略,但这是我认为你能做的最好的。

我不会这么做。您最多可以获得2倍的吞吐量。你的CPU已经满了,对吧?工作站GC不能扩展到多核,导致cpu闲置。