나는 HTML 페이지를 지속적으로 파싱 할 책임이있는 서버에서 C # 프로세스 (서비스)를 실행한다. HTMLAgilityPack을 사용합니다. 증상은 시간이 지남에 따라 느려지고 느려집니다.
프로세스를 시작할 때 n 페이지 / s를 처리합니다. 몇 시간 후에 속도는 약 n / 2 페이지 / s로 떨어집니다. 며칠 후 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 노드와 문자열)을 만든다는 것을 알고 있습니다. 명확한 메모리 할당과 멀티 스레딩은 잘 작동하지 않습니다. 그러나 나는 그 과정이 어떻게 느리고 느리게 될 수 있는지 이해하지 못한다.
일부 RAM 집중 (많은 할당) 처리 속도가 느려지고 느려질 수있는 CLR 또는 Windows에 대해 알고 있습니까? 예를 들어 특정 방식으로 메모리 할당을하는 스레드에 불이익을주는 것과 같은가?
HTMLAgilityPack을 사용하여 비슷한 동작을 발견했습니다.
하나의 수확량 데이터가 문제를 일으키기 시작하는 컴파일러 생성 클래스에서 로컬 변수의 공간 누출을 시작한다는 것을 알았습니다. 사용할 수있는 코드가 없으므로 여기 응급 처치 키트가 있습니다.
HTMLAgility의 최악의 점은 실제 문제가되는 조각입니다.
나는 당신이 당신의 HTML 조각의 범위를 고려하기 시작할 때 일이 잘 시작될 것임을 확신합니다. SOS에서 WinDbg를 사용하여 실행을 살펴보고 메모리를 덤프하고 살펴보십시오.
그렇게하는 방법.
다음을 입력하여 메모리에 실행을로드하십시오.
.loadby sos clr
그런 다음 입력
!dumpheap -stat
그런 다음 응용 프로그램에서 메모리 주소와 크기로 유형별로 그룹화되고 낮은 헤더에서 높은 헤더로 정렬 된 메모리 항목을 가져옵니다. 앞에 거대한 숫자가있는 System.String []과 같은 것을 볼 수 있습니다. 먼저 조사하고 싶은 것들.
누가 당신이 입력 할 수 있는지 볼 수
!dumpheap -mt <heap address>
그리고 해당 메모리 테이블 (MT)을 사용하는 주소와 그것이 사용하는 램의 크기를 볼 수 있습니다.
입력 한 x100 줄의 코드를 거치지 않고 흥미롭게됩니다.
!gcroot <address>
그것이 인쇄 할 것은 메모리를 할당 한 파일과 코드 라인, 컴파일러 생성 클래스 및 슬픔을 유발하는 변수 및 보유 바이트 수입니다.
이것은 "프로덕션 디버깅"이라고 할 수있는 것으로, 서버에 액세스 할 수 있으면 작동합니다.