gpt4 book ai didi

c# - 数小时后 RAM 密集型 C# 进程变慢

转载 作者:太空狗 更新时间:2023-10-30 01:13:26 24 4
gpt4 key购买 nike

我在负责持续解析 HTML 页面的服务器上运行 C# 进程(服务)。它依赖于 HTMLAgilityPack。症状是随着时间的推移越来越慢。

当我启动进程时,它每秒处理 n 页。几个小时后,速度下降到大约 n/2 页/秒。几天后它可以下降到 n/10。这种现象已被多次观察到,而且具有一定的确定性。任何时候重新启动该过程,一切都会恢复正常。

非常重要:我可以在同一个进程中运行其他计算,而且它们不会减慢:我可以随时使用任何我想要的东西来达到 100% 的 CPU。这个过程本身并不慢。只有 HTML 解析变慢。

我可以用最少的代码重现它(实际上原始服务中的行为有点极端,但这段代码仍然重现了该行为):

public static void Main(string[] args) {
string url = "https://en.wikipedia.org/wiki/History_of_Texas_A%26M_University";
string html = new HtmlWeb().Load(url).DocumentNode.OuterHtml;
while (true) {
//Processing
Stopwatch sw = new Stopwatch();
sw.Start();
Parallel.For(0, 10000, i => new HtmlDocument().LoadHtml(html));
sw.Stop();
//Logging
using(var writer = File.AppendText("c:\\parsing.log")) {
string text = DateTime.Now.ToString() + ";" + (int) sw.Elapsed.TotalSeconds;
writer.WriteLine(text);
Console.WriteLine(text);
}
}
}

使用这个最少的代码,它显示速度(每秒页数)作为自进程启动以来经过的小时数的函数:

enter image description here

已排除所有明显原因:

  • HTML 页面更大或不同(在最少的代码中,它是同一页面)
  • 完整 RAM:该进程在 32 GB 上使用了大约 500 MB
  • 其他进程使用 CPU 或 RAM

这可能与 RAM 和内存分配有关。我知道 HTMLAgilityPack 进行了大量的小对象内存分配(HTML 节点和字符串)。很明显,内存分配和多线程不能很好地协同工作。但是我不明白这个过程怎么会变得越来越慢。

您是否知道有关 CLR 或 Windows 的任何事情可能会导致某些 RAM 密集型(多次分配)处理变得越来越慢?例如以某种方式惩罚执行内存分配的线程?

最佳答案

我注意到使用 HTMLAgilityPack 时有类似的行为。

我发现,当一个 yield 的数据开始出现空间泄漏时,编译器生成的类中的局部变量开始引起问题。由于没有可用代码,这是我的急救箱。

  1. 确保设置 the right strategy ,改变app.config中的GC收集策略将有助于分片。

  2. 确保在不需要时将它们清空,一旦不需要它们,不要等待作用域清理内存,因为在调用方法和方法作用域中会调用 IEnumerables变量并且可以比你想象的活得更久!在 ILSpy 中打开您的代码并查看 <>d__0(0) 生成的类。你会看到生成的东西像 d__.X=X;在这种情况下,X 可以包含一个片段或整个页面。

  3. 您的局部变量被提升到堆中,因为如果它们不存在,则无法在 IEnumable 迭代中访问它们。

  4. 锁定开始成为一个问题,大项目在您的第 4 代 ram 中流血,实际上将开始阻塞 GC。 GC 正在暂停您的线程以执行垃圾收集。

  5. HTMLAgility 最糟糕的地方在于它 fragments that ends up being a real issue

    我敢肯定,当您开始考虑 HTML 片段的范围时,您会发现一切都会开始顺利进行。使用 WinDbg in SOS 查看您的执行情况并转储您的内存并查看。

如何做到这一点。

  1. 打开 WinDebug,按 F6 并附加到进程(在字段中输入进程 ID,然后按确定)

  2. 然后通过输入将执行加载到您的内存中

    .loadby sos clr
  3. 然后输入

    !dumpheap -stat

然后你会得到在你的应用程序中分配的内存项,内存地址和大小按类型分组并从低头到高头排序你会看到类似 System.String[] 的东西,前面有大量数字它,这是您首先要调查的内容。

现在看看谁有你可以输入的内容

!dumpheap -mt <heap address>

您将看到正在使用该内存表 (MT) 的地址及其使用的 ram 的大小。

现在它变得有趣了,而不是你输入 x100 行代码

!gcroot <address>

它将打印的是分配内存的文件和代码行、编译器生成的类和导致您痛苦的变量以及它包含的字节数。

这就是所谓的“生产调试”,如果您可以访问服务器,我想您可以访问它,它就可以工作。

关于c# - 数小时后 RAM 密集型 C# 进程变慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50282509/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com