1

我正在用 C# 编写一个高容量 Web 服务,该服务在 Win 2k8(.NET 4.5)上的 64 位 IIS 中运行,它与 XML 有效负载一起使用,并对小型和大型对象(主要是大型对象)执行各种操作字符串,一些超过 85k(所以进入 LOH))。请求是无状态的,并且内存使用随着时间的推移保持稳定。每个请求正在分配和释放大量内存,似乎没有内存泄漏。

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

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

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

4

2 回答 2

0

微软决定设计这个String类,以便所有字符串都作为一个单一的字符序列存储在内存中。虽然这适用于某些使用模式,但它对其他使用模式非常有效。

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

于 2015-07-13T19:14:50.283 回答
0

CLR 的 GC“适应”您的负载

它无法知道您愿意承受多少内存作为开销。在这里,您可能希望为应用程序提供 5GB 的堆空间,这样集合就更少了。GC 没有内置的调谐旋钮(主观说明:这是一个小问题)。

您可以通过在短时间内使用一种低延迟模式来强制更大的堆大小。这应该会导致 GC 努力避免 G2 收集。监控 RAM 使用情况并在消耗达到 5GB 时禁用低延迟模式。

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

我不会这样做。您可以最大程度地获得 2 倍的吞吐量。你的CPU已经用完了,对吧?Workstation GC 无法扩展到多个内核,并且 CPU 未被使用。

于 2015-07-13T19:36:37.587 回答