我在负责连续解析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);
}
}
}
使用此最小代码,显示速度(每秒页数),作为自流程启动以来经过的小时数的函数:
排除了每一个明显的原因:
这可能是关于RAM和内存分配的事情。我知道HTMLAgilityPack会进行大量的小对象内存分配(HTML节点和字符串)。很明显,内存分配和多线程不能很好地协同工作。但我不明白这个过程会变得越来越慢。
您是否知道有关CLR或Windows的任何内容可能导致某些RAM密集型(许多分配)处理变得越来越慢?例如惩罚以某种方式执行内存分配的线程?
答案 0 :(得分:4)
我注意到使用HTMLAgilityPack的类似行为。
我发现当一个yield的数据开始太空时,会在编译器生成的类上泄漏本地变量,从而导致出现问题。因为没有代码可用...... bla bla,这是我的急救工具包
HTMLAgility最糟糕的是fragments that ends up being a real issue
我很确定当你开始考虑HTML片段的范围时,你会发现事情会很顺利。使用WinDbg in SOS查看您的执行并转储您的内存并查看。
怎么做。
然后输入
将执行加载到您的记忆中.loadby sos clr
然后输入
!dumpheap -stat
然后,您将获得在应用程序中分配的内存项,其中包含内存地址和按类型分组的大小,并从低标题到高标题排序,您将看到类似System.String []的内容,前面有一个masive数字它,这就是你想先调查的后缀。
现在看看谁可以输入
!dumpheap -mt <heap address>
您将看到使用该内存表(MT)的地址以及它使用的ram的大小。
现在它变得有趣了,而不是你输入你输入的x100行yode
!gcroot <address>
它将打印的是分配内存的文件和代码行,编译器生成的类和变量导致您悲伤以及它保存的字节。
这就是所谓的“生产调试”,如果你有权访问服务器,那我觉得你有。
希望得到帮助,
沃尔特