2011-10-14 2 views
11

며칠 및 몇 달 동안 다시 시작하지 않고 실행해야하는 Windows 콘솔 앱이 있습니다. 응용 프로그램은 MSMQ에서 "작업"을 검색하고 처리합니다. 작업 청크를 동시에 처리하는 스레드가 30 개 있습니다.대기열에서 오는 대형 객체 힙 및 문자열 객체

MSMQ에서 오는 각 작업 청크는 약 200kb이며 대부분은 단일 String 개체에 할당됩니다.

약 3 ~ 4,000 개의 이러한 작업 청크를 처리 한 후 응용 프로그램의 메모리 소비가 엄청나게 많은데 1 ~ 1.5GB의 메모리를 소비한다는 사실을 알게되었습니다.

프로필러를 통해 앱을 실행하고 대형 메모리 힙에서이 메모리 (대부분 작살 등)가 사용되지 않지만 구조가 조각났다는 사실을 알게되었습니다.

이러한 사용되지 않은 (가비지 수집 된) 바이트 중 90 %가 이전에 할당 된 문자열임을 알게되었습니다. MSMQ에서 들어오는 문자열이 할당되고 사용 된 다음 할당이 해제되어 조각화의 원인이되었다고 의심하기 시작했습니다.

GC.Collect (2 또는 GC.Max ...)와 같은 것은 대형 오브젝트 힙을 gc하지만 압축하지 않았기 때문에 도움이되지 않는다는 것을 이해합니다 (여기서 문제가 됨). 그래서 내가 필요로하는 것은 이러한 문자열을 캐싱하고 어떻게 든 다시 사용할 수 있다고 생각하지만 String은 불변이므로 StringBuilders를 사용해야합니다.

내 질문입니다. 기본 구조를 변경하지 않아도됩니까 (예 : MSMQ를 사용하여 변경할 수 없으므로) LOH를 조각 내기 위해 항상 매번 새 문자열을 초기화하지 마십시오.

감사합니다, 야 니스

UPDATE :이 "작업"덩어리가 현재 현재

을 검색하는 방법이가 MSMQ에서 WorkChunk 개체로 저장되는 정보. 이러한 각 객체는 Contents라는 문자열과 Headers라는 다른 String을 포함합니다. 이것은 실제 텍스트 데이터입니다. 필요하다면 스토리지 구조를 다른 것으로 변경하고 MSMQ가 아닌 다른 스토리지 메커니즘을 잠재적으로 변경할 수 있습니다. 작업자 노드 측면에서

현재 우리가 할

WorkChunk 덩어리 = _Queue.Receive();

따라서이 단계에서 캐시 할 수있는 정보가 거의 없습니다. 만약 우리가 구조를 어떻게 든 변경했다면 우리는 약간의 진전을 이룰 수 있다고 생각합니다. 어쨌든 우리는이 문제를 해결해야한다. 그래서 우리는 몇 달 간의 일을 포기하지 않기 위해 필요한 모든 것을 할 것이다.

업데이트 : 아래 제안 사항 중 일부를 시도해 본 결과 로컬 컴퓨터 (Windows 7 x64 및 64 비트 앱 실행)에서이 문제를 재현 할 수 없음을 알게되었습니다. 이것은 일을 훨씬 더 어렵게 만듭니다. 왜 누군가가 왜이 문제를 현지에서 재발견하는 데 정말로 도움이되는지 알고 있다면.

+0

어떻게 이러한 문자열을 수신합니까? 일단 문자열이되면 붙어 있습니다. 나는 그들이 스트림이나 바이트 []에서 왔어 몇 가지 옵션이있을 수 있습니다. –

+0

안녕하세요 Henk -이 작업 청크에 대한 자세한 정보를 얻으려는 업데이트를 확인하십시오 – Yannis

+0

하지만 실제 문제입니까? > = 8GB RAM을 갖춘 64 비트 PC에서 1.5GB를 계속 사용할 수 있어야합니다. –

답변

4

큰 개체 힙의 메모리 할당으로 인해 문제가 발생한 것 같습니다. 큰 개체 힙은 압축되지 않아 조각화의 원인이 될 수 있습니다. 당신은 이 개 세 가지 솔루션을 가지고있는 것 같습니다

Large Object Heap Uncovered

: 무슨 일이 일어나고 당신은 대형 개체 힙의 단편화를 확인하기 위해 수행 할 수있는 몇 가지 디버깅 단계를 포함한 자세한 내용이수록되어 여기에 좋은 기사가있다 :

  1. 각 청크가 85,000 바이트보다 작은 청크/짧은 문자열에 대해 처리를 수행하도록 응용 프로그램을 변경하십시오. 이렇게하면 큰 개체를 할당 할 필요가 없습니다.
  2. 응용 프로그램을 변경하여 몇 가지 큰 메모리 덩어리를 미리 할당하고 새 메시지를 할당 된 메모리로 복사하여 해당 덩어리를 다시 사용하십시오. Heap fragmentation when using byte arrays을 참조하십시오.
  3. 그대로 두십시오. 예외가 발생하지 않고 응용 프로그램이 시스템에서 실행중인 다른 응용 프로그램을 방해하지 않는 한 그대로 두어야합니다.

여기서 중요한 것은 가상 메모리와 실제 메모리의 차이를 이해하는 것입니다. 프로세스가 많은 양의 가상 메모리를 사용하고 있어도 할당 된 개체 수가 상대적으로 적 으면 물리적 메모리 해당 프로세스의 사용이 적습니다 (사용되지 않은 메모리가 디스크로 페이징 됨). 이는 시스템의 다른 프로세스에 거의 영향을주지 않습니다. 또한 "VM Hoarding"옵션이 도움이된다는 것을 알 수 있습니다 - 자세한 정보는 "Large Object Heap Uncovered"기사를 읽으십시오.

하나의 큰 문자열 대신 바이트 배열과 짧은 하위 문자열을 사용하여 처리의 일부 또는 전체를 수행하도록 응용 프로그램을 변경해야합니다. 처리가 얼마나 어려운지에 따라 달라질 수 있습니다 네가하고있는 일.

+0

저스틴 감사합니다. 문제는 이러한 문자열이 메시지 대기열을 통해 다른 시스템에서 온다는 것입니다. 따라서 전반적인 저장소 구조를 변경하지 않는 한 현재 "작업 덩어리의 절반을 얻지 못한다"고 말할 수는 없습니다. 아이디어와 제안이 필요한 부분은 어디입니까? – Yannis

+0

@ Yannis 응용 프로그램을 변경하려면 제안을 수정하십시오. 이 작업을 수행하는 방법에 대해 수행중인 처리 종류에 대한 세부 사항이 필요할 것입니다. 내 최신 편집본 보셨 니? 당신은 당신이보고있는이 동작이 완벽하게 괜찮을 수도 있다고 생각해야합니다. (OOM 예외를 얻지 않는 한 32 비트 또는 64 비트 프로세스입니까?) – Justin

+0

저스틴 - 이것은 64 비트 프로세스이며 그 결과는 컴퓨터 (Windows 2008 Server)가 과도한 페이징으로 인해 크롤링 속도가 느려집니다. 이것은 의미가 있습니다. 내가 물어 보자 : String Content 속성을 char [] []로 char chunk가 85k (LOH에 무엇인가 넣는 제한)의 char 배열을 포함하도록 변경하면 도움이 될까요? – Yannis

1

아마도 작품을 처리하는 동안 사용할 수있는 문자열 객체 풀을 생성 한 다음 완료되면 다시 되돌릴 수 있습니다.

큰 오브젝트가 LOH에 작성되면 제거 할 수 없으므로 (AFAIK) 이러한 오브젝트를 작성하는 것을 피할 수없는 경우 오브젝트를 재사용하는 것이 가장 좋습니다.

양쪽 프로토콜을 변경할 수있는 경우 '내용'문자열을 더 작은 세트 (각각 < 80k)로 줄이면 LOH에 저장되지 않습니다.

+0

OP가 이미 말했던 것입니다. 그러나 _how_ 문자열을 재사용합니까? –

+0

추가 정보가있는 원본 게시물에 편집 내용이 추가되었습니다 – Yannis

+0

Tony - 문제는 이러한 내용을 직렬화하고 반대쪽에서이를 직렬화 해제하는 것입니다. 내가 무엇을 하든지이 객체는 이러한 "내용"을 일방적으로 또는 심지어 작은 덩어리로 포함합니다. – Yannis

2

LOH에 조각화가있는 경우 할당 된 개체가 있음을 의미합니다. 지연을 지연시킬 수 있다면, 현재 실행중인 모든 작업이 완료 될 때까지 잠시 기다려주십시오. GC.Collect()으로 전화하십시오. 참조 된 대형 오브젝트가 없으면, 모두 수집되어 LOH의 단편화를 효과적으로 제거합니다. 물론 이것은 (모든 것) 모든 큰 객체가 참조되지 않은 경우에만 작동합니다.

또한 64 비트 OS로 이동하면 가상 공간이 거의 무제한이기 때문에 조각화로 인한 메모리 부족이 64 비트 시스템에서 문제가 될 가능성이 낮기 때문에 도움이 될 수 있습니다.

+0

스티븐 나는 조각화가 객체가 (LOH에서) 있다는 것을 의미하지 않는다고 생각하지만, 그들은 한 번 거기에 있었고 결국 할당이 해제되었다. LOH에 빈 덩어리를 남긴다. 즉, 120k (말)의 덩어리가 있고 121k를 할당하려고 시도하면 이는 121k 바이트의 첫 번째 가용 연속 덩어리에 할당되어 120k 덩어리를 비워 둡니다. GC.Collect()는 불행히도 LOH 객체의 할당을 해제하고 해당 GC.Collect (GC.MaxGeneration)가 필요하므로 LOH를 압축하지 않습니다. – Yannis

+1

Steven이 GC라고 말한 것 같지 않습니다 .Collect는 작아 질 것입니다. 이동 중에도 몇 가지 개체 만 가지고있을 때 전화를한다고 생각합니다. 그렇게하면 큰 물체를 없앨 수 있습니다. 그 사이에 멋진 틈새를 남겨 두는 틈이 있습니다. – Joey

+1

@ Yannis : 내가 말하는 것은 비어있는 LOH를 조각 낼 수 없다는 것입니다. Joey는 그것을 잘 표현했다. – Steven

0

중복 참조를 제거하기 위해 String.Intern (...)을 사용하는 것은 어떻습니까? 성능 저하가 있지만 문자열에 따라 영향을 미칠 수 있습니다.

+0

헤더와 내용을 키/값 쌍으로 잘라내어 모든 키와 값에 대해 .Intern을 수행하면 더 잘 작동합니다.그런 다음 중복 데이터는 없지만 더 많은 처리가 필요한 다른 데이터 구조로 끝납니다. –