C# 过度垃圾收集 - 大字符串,G2 压力?
C# Excessive Garbage Collection - Large Strings, G2 pressure?
我正在 Win 2k8 (.NET 4.5) 上的 64 位 IIS 中用 C# 运行 编写一个高容量 Web 服务,它与 XML 有效载荷一起工作并执行各种对小型和大型对象的操作(其中大型对象主要是字符串,有些超过 85k(因此进入 LOH))。请求是无状态的,内存使用会随着时间的推移保持稳定。每个请求正在分配和释放大量内存,似乎没有内存泄漏。
以每秒最多 25 个事务的速度运行,平均调用持续 5 秒,根据两个分析工具,它在 GC 上花费了 40-60% 的时间,perfmon 显示稳定的 20 个 G0 和 G1 集合超过5 秒,以及超过 5 秒的 15 个 G2 收集——这意味着很多(我们认为)过早地进入 G2 以获取我们希望保留在 G0 中的数据。我读到的一切都表明这是非常过分的。我们希望系统能够以高于 25 tps 的吞吐量执行并假设 GC activity 正在阻止这种情况。
为请求提供服务的机器有很多内存 - 16GB - 负载下的应用程序在负载下一个小时时最多消耗 1GB。我知道更大的堆不一定会使事情变得更好,但是有空闲内存。
我很欣赏这对细节的理解(如果时间允许,我会尝试用一个简单的应用程序重新创建条件)——但是谁能解释为什么我们看到这么多 G2 GC activity?我应该专注于 LOH 吗?人们一直告诉我 CLR 的 GC "adapts" 到你的负载,但它并没有改变它在这种情况下的行为,而且与其他运行时不同,我似乎无法调整它(尝试过工作站 GC,但是几乎没有可观察到的差异)。
Microsoft 决定设计 String
class 以便所有字符串都作为一个完整的字符序列存储在内存中。虽然这对某些使用模式很有效,但对其他使用模式却很糟糕。
我发现非常有用的一件事是尽可能避免创建 String
的实例。如果一个方法将经常用于对提供的字符串的一部分进行操作,并且反过来要求其他方法对它的一部分进行操作,则该方法应该接受指定它们应该操作的 String
范围的参数.这将避免第一个方法的调用者需要使用 Subst
来构造一个新的 String
来让该方法起作用,并且将避免需要让方法调用 Subst
来将字符串的一部分提供给它的调用者。在我使用这种技术的某些情况下,可以用 zero.
代替数千个 String
实例的创建——有些相当大——
CLR's GC "adapts" to your load
它无法知道您愿意承受多少内存作为开销。在这里,您可能希望为应用程序提供 5GB 的堆内存,以便集合更加稀有。 GC 没有为此内置的调谐旋钮(主观说明:这很可惜)。
您可以通过在短时间内使用一种低延迟模式来强制增大堆大小。这应该会导致 GC 努力避免 G2 收集。监控 RAM 使用情况并在消耗达到 5GB 时禁用低延迟模式。
这是一个冒险的策略,但这是我认为你能做到的最好的策略。
我不会做的。您可以最大程度地获得 2 倍的吞吐量。你的 CPU 已经用完了,对吧? Workstation GC 不会扩展到多核并留下 CPUs 未使用。
我正在 Win 2k8 (.NET 4.5) 上的 64 位 IIS 中用 C# 运行 编写一个高容量 Web 服务,它与 XML 有效载荷一起工作并执行各种对小型和大型对象的操作(其中大型对象主要是字符串,有些超过 85k(因此进入 LOH))。请求是无状态的,内存使用会随着时间的推移保持稳定。每个请求正在分配和释放大量内存,似乎没有内存泄漏。
以每秒最多 25 个事务的速度运行,平均调用持续 5 秒,根据两个分析工具,它在 GC 上花费了 40-60% 的时间,perfmon 显示稳定的 20 个 G0 和 G1 集合超过5 秒,以及超过 5 秒的 15 个 G2 收集——这意味着很多(我们认为)过早地进入 G2 以获取我们希望保留在 G0 中的数据。我读到的一切都表明这是非常过分的。我们希望系统能够以高于 25 tps 的吞吐量执行并假设 GC activity 正在阻止这种情况。
为请求提供服务的机器有很多内存 - 16GB - 负载下的应用程序在负载下一个小时时最多消耗 1GB。我知道更大的堆不一定会使事情变得更好,但是有空闲内存。
我很欣赏这对细节的理解(如果时间允许,我会尝试用一个简单的应用程序重新创建条件)——但是谁能解释为什么我们看到这么多 G2 GC activity?我应该专注于 LOH 吗?人们一直告诉我 CLR 的 GC "adapts" 到你的负载,但它并没有改变它在这种情况下的行为,而且与其他运行时不同,我似乎无法调整它(尝试过工作站 GC,但是几乎没有可观察到的差异)。
Microsoft 决定设计 String
class 以便所有字符串都作为一个完整的字符序列存储在内存中。虽然这对某些使用模式很有效,但对其他使用模式却很糟糕。
我发现非常有用的一件事是尽可能避免创建 String
的实例。如果一个方法将经常用于对提供的字符串的一部分进行操作,并且反过来要求其他方法对它的一部分进行操作,则该方法应该接受指定它们应该操作的 String
范围的参数.这将避免第一个方法的调用者需要使用 Subst
来构造一个新的 String
来让该方法起作用,并且将避免需要让方法调用 Subst
来将字符串的一部分提供给它的调用者。在我使用这种技术的某些情况下,可以用 zero.
String
实例的创建——有些相当大——
CLR's GC "adapts" to your load
它无法知道您愿意承受多少内存作为开销。在这里,您可能希望为应用程序提供 5GB 的堆内存,以便集合更加稀有。 GC 没有为此内置的调谐旋钮(主观说明:这很可惜)。
您可以通过在短时间内使用一种低延迟模式来强制增大堆大小。这应该会导致 GC 努力避免 G2 收集。监控 RAM 使用情况并在消耗达到 5GB 时禁用低延迟模式。
这是一个冒险的策略,但这是我认为你能做到的最好的策略。
我不会做的。您可以最大程度地获得 2 倍的吞吐量。你的 CPU 已经用完了,对吧? Workstation GC 不会扩展到多核并留下 CPUs 未使用。