2011-11-05 4 views
11

LOH 조각화를 일으키지 않고 큰 문자열에서 RegExes (일치하는 항목을 찾기 위해)를 많이 실행하려면 어떻게해야합니까?RegEx, StringBuilder 및 Large Object 힙 조각화

.NET Framework 4.0이므로 StringBuilder을 사용하고 있지만 LOH에는 RegEx를 실행하는 즉시 LOH에있는 StringBuilder.ToString()을 호출해야합니다.

이 문제를 해결하려면 어떤 해결책이 있습니까? 큰 문자열과 RegExes를 처리하는 응용 프로그램을 오래 실행하는 것은 사실상 불가능합니다.

아이디어

이 문제를 해결하려면이 문제에 대해 생각하는 동안

을, 나는 더러운 해결책을 찾은 것 같아요.

주어진 시간에 나는 단지 5 개의 문자열을 가지고 있으며이 5 개의 문자열 (85KB보다 큽니다)은 RegEx.Match으로 전달됩니다.

새로운 객체가 LOH의 빈 공간에 적합하지 않기 때문에 단편화가 발생하기 때문에,이 문제를 해결해야 최대로

  1. PadRight 모든 문자열을. 허용 크기는의는
  2. 어떤 분열로 인해이되지 않습니다 이전 문자열이 범위를 벗어나 이미 이미 비워진 메모리를 맞는 때문에 모든 새로운 문자열을 이렇게함으로써
  3. (나는 StringBuider이 작업을 수행해야 할 수도 있습니다) 1,024킬로바이트을 가정 해 봅시다 객체 크기는 항상 동일하므로 주어진 시간에 1024 * 5 만 할당하고 LOH의 이러한 공간은이 문자열간에 공유됩니다.

나는 다른 큰 개체를 응용 프로그램이 어쩌면 더 악화 조각화 1,024킬로바이트 문자열을 많이 할당하는 원인이 LOH에서이 위치를 할당 할 경우 발생하는이 디자인의 가장 큰 문제를 가정합니다. fixed 문을 사용하면 고정 메모리 주소에없는 새 문자열을 실제로 만들지 않고도 RegEx에 고정 문자열을 보내려면 어떻게해야합니까?

이 이론에 대한 아이디어가 있으십니까? (불행히도 나는 쉽게 문제를 재현 할 수 없다. 나는 일반적으로 메모리 프로파일 러를 사용하여 변경 사항을 관찰하고 어떤 종류의 분리 된 테스트 케이스를 쓸 수 있는지 확실하지 않다.)

+2

대형 오브젝트 힙이 단편화되어 있는지 확실합니까? 커다란 (수 백 킬로바이트) 문자열로 많은 작업을하고 LOH 단편화 문제를 겪어 본 적이 없습니다. –

+1

예, 확실합니다. 응용 프로그램은 메모리 부족으로 인해 실제로 영향을받는 데 시간이 오래 걸릴 수 있습니다. 실제로 메모리 프로파일 링을 수행하면 영향을 미치지 만 앱을 크래시 할 정도는 아닙니다. –

+1

예, 쉽습니다. 뚱뚱한 백 달러가 당신에게 64 비트 운영 체제를 사주고 있습니다. 어떤 종류의 프로그래밍 노력도 그와 일치 할 수 없습니다. –

답변

6

좋아,이 시도는 꽤 일반적인 방법으로이 문제를 해결하지만 몇 가지 명백한 한계가 있습니다. 내가이 조언을 어디에도 보지 못했고 모든 사람들이 LOH Fragmentation에 대해 징징 대고 있기 때문에 나는 나의 설계와 가정이 옳았다는 것을 확인하기위한 코드를 공유하기를 원했다.

이론 :

  1. (이 우리가 스트림에서 읽을에서 읽은 큰 문자열을 저장하는 것입니다) 공유 대규모의 StringBuilder 만들기 - new StringBuilder(ChunkSize * 5);
  2. 은 대규모 문자열 만들기 (보다 큰 수있다 최대 허용 크기)은 빈 공간으로 초기화해야합니다. - 새 문자열 ('', ChunkSize * 10);
  3. 문자열 객체가 메모리에 고정되므로 GC가 문제를 일으키지 않습니다. GCHandle.Alloc(pinnedText, GCHandleType.Pinned).LOH 객체가 일반적으로 고정되어 있어도 성능을 향상시키는 것으로 보입니다. 어쩌면 unsafe 코드 공유의 StringBuilder에
  4. 읽기 스트림 및 인덱서를
  5. 를 사용하여 pinnedText에 다음 안전하지 않은 사본을 아래 코드는 그냥 같이 작동이 구현으로 정규식

에 pinnedText 전달의 더 LOH는 없기 때문에 배당. 내가 outofmemory exception

와 충돌하기 전에 300 % 더 적은 메모리를 할당 할 수 있습니다 정적 StringBuilder 또는 사용 StringBuilder.ToString() 코드를 사용하는 대신 new string(' ') 할당로 전환하면 나는 또한이에는 LOH 조각이 없다는 것을, 메모리 프로파일 러와 결과를 확인 이행. RegEx가 예상치 못한 문제를 일으키지 않는 이유는 아직도 이해할 수 없습니다. 나는 또한 다른 비싼 RegEx 패턴으로 테스트했고 결과는 동일하고 분열이 없다.

코드 :

http://pastebin.com/ZuuBUXk3

using System; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Text.RegularExpressions; 

namespace LOH_RegEx 
{ 
    internal class Program 
    { 
     private static List<string> storage = new List<string>(); 
     private const int ChunkSize = 100000; 
     private static StringBuilder _sb = new StringBuilder(ChunkSize * 5); 


     private static void Main(string[] args) 
     { 
      var pinnedText = new string(' ', ChunkSize * 10); 
      var sourceCodePin = GCHandle.Alloc(pinnedText, GCHandleType.Pinned); 

      var rgx = new Regex("A", RegexOptions.CultureInvariant | RegexOptions.Compiled); 

      try 
      { 

       for (var i = 0; i < 30000; i++) 
       {     
        //Simulate that we read data from stream to SB 
        UpdateSB(i); 
        CopyInto(pinnedText);     
        var rgxMatch = rgx.Match(pinnedText); 

        if (!rgxMatch.Success) 
        { 
         Console.WriteLine("RegEx failed!"); 
         Console.ReadLine(); 
        } 

        //Extra buffer to fragment LoH 
        storage.Add(new string('z', 50000)); 
        if ((i%100) == 0) 
        { 
         Console.Write(i + ","); 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.ToString()); 
       Console.WriteLine("OOM Crash!"); 
       Console.ReadLine(); 
      } 
     } 


     private static unsafe void CopyInto(string text) 
     { 
      fixed (char* pChar = text) 
      { 
       int i; 
       for (i = 0; i < _sb.Length; i++) 
       { 
        pChar[i] = _sb[i]; 
       } 

       pChar[i + 1] = '\0'; 
      } 
     } 

     private static void UpdateSB(int extraSize) 
     { 
      _sb.Remove(0,_sb.Length); 

      var rnd = new Random(); 
      for (var i = 0; i < ChunkSize + extraSize; i++) 
      { 
       _sb.Append((char)rnd.Next(60, 80)); 
      } 
     } 
    } 
} 
0

어떤 시점에 언로드되는 AppDomain?

+0

공유 저장소를 사용하고 메모리 또는 파일에서 직접 스트림으로 데이터를 읽지 않는 한 여전히 결과를 공유해야하지만 문제는 여전히 동일합니다. . 왜냐하면 만약 당신이 어떤 방법으로 리모트를 사용한다면 LOH와 현재 두 appdomain 모두에있는 커다란 배열이나 문자열을 다시 만들 것입니다. 메모리, 메모리 매핑 파일 등을 공유하는 것은 실제로 해결책이지만 대규모 응용 프로그램에서는 실제로 복잡해지며 성능이 상당히 떨어집니다. –

0

하나의 대안이 REG-전을 수행하는 몇 가지 방법을 찾아야하는 것은 비 배열 기반 데이터 구조에 일치합니다. 불행히도 빠른 Google은 스트림 기반 reg-ex 라이브러리와 관련하여 많은 것을 불러 오지 않았습니다. 나는 reg-ex 알고리즘이 스트림에 의해 지원되지 않는 많은 역 추적을 필요로한다고 생각한다.

정규 표현식의 모든 기능을 꼭 사용해야합니까? 아마도 85kb 이하의 문자열의 링크 된 목록에서 작동 할 수있는 간단한 검색 기능을 구현할 수 있습니까?

또한 LOH 단편화는 오랜 기간 동안 대형 오브젝트 참조를 유지하는 경우에만 실제로 문제를 일으 킵니다. 끊임없이 창조하고 파괴한다면, LOH는 성장해서는 안됩니다.

FWIW, 나는 RedGate ANTS memory profiler에 LOH 및 조각 수준의 개체를 추적하는 데 매우 효과적입니다.

+0

"LOH 단편화는 오랜 기간 동안 대형 오브젝트 참조를 유지하는 경우에만 실제로 문제를 일으 킵니다."AFAIK 이것이 올바르지 않은 경우 보유한 시간과 상관없이 LOH에 85KB보다 큰 항목이 위치합니다. ANTS Profiler를 사용하고 있습니다. 실제로 꽤 좋습니다. –

+0

예 RegEx의 모든 기능이 필요합니다. –

+0

죄송합니다. 오랜 기간 동안 참조가 보류 된 경우 LOH 문제 만 보았습니다. 당신은 85k 이상이 LOH에 들어가는 것이 맞습니다. 내가 이해할 수 있듯이 LOH의 문자열 사이에 다른 장기간의 객체가 할당되어있어 문자열 할당이 메모리가 부족해질 때까지 LOH 위로 밀어 올려지는 문제가 있습니까? – SimonC