2011-11-05 5 views
11

LOHフラグメンテーションを引き起こさずに大きな文字列でRegExes(一致するものを見つける)をたくさん実行するにはどうすればよいですか?RegEx、StringBuilderおよびラージオブジェクトヒープフラグメンテーション

私はStringBuilderを使用していますので、LOHにはありませんが、RegExを実行する必要があるとすぐに、StringBuilder.ToString()と呼ぶ必要があります。つまり、LOHになります。

この問題の解決方法はありますか?このような大きな文字列やRegExesを扱う長時間実行されるアプリケーションを持つことは事実上不可能です。

この問題を解決するためのアイデア:

この問題について考えている間、私は汚い解決策を見つけたと思います。

与えられた時間に私は5つの文字列しか持たず、これら5つの文字列(85KBより大きい)はRegEx.Matchに渡されます。

新しいオブジェクトはLOHで空のスペースに収まらないので、断片化が発生するので、これは問題解決する必要があります:maxに

  1. PadRightすべての文字列を。受け入れられたサイズなので、すべての新しい文字列を行うことにより
  2. (私はStringBuiderでこれを行うために必要がある場合があります)のは、1024キロバイトを言わせて、前の文字列が範囲外に既にあるとして、既に空にするためにメモリに適合します
  3. はので、任意の断片化が存在することはありませんオブジェクトのサイズは常に同じですので、私は与えられた時刻に1024 * 5しか割り当てません。LOHのこれらのスペースはこれらの文字列の間で共有されます。

他の大きなオブジェクトがこの場所をLOHに割り当てると、アプリケーションが1024 KBの文字列をたくさん割り当ててしまう可能性があります。 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に危険なコピーには以下のコードは何のLOHがないだけのように動作します。この実装では正規表現

にpinnedTextを渡します割り当て。私は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

1つの代替案は、非配列ベースのデータ構造にREG-EXマッチを実行するいくつかの方法を見つけることであろう。残念なことに、速いGoogleは、ストリームベースのreg-exライブラリに関してはあまり効果がありませんでした。 reg-exアルゴリズムでは、ストリームによってサポートされていない多くのバックトラッキングが必要になると思います。

正規表現は完全に必要ですか?おそらく85kb以下の文字列のリンクリストで動作する簡単な検索関数を実装することができますか?

また、LOHフラグメンテーションは、大きなオブジェクト参照を長期間保持すると実際に問題を引き起こします。あなたがそれらを絶えず作成して破壊しているなら、LOHは成長してはいけません。

FWIW、RedGate ANTS memory profilerは、LOHのオブジェクトとフラグメンテーションのレベルを追跡するのに非常に優れています。

+0

"LOHフラグメンテーションは、大きなオブジェクト参照を長時間保持すると実際に問題を引き起こすだけです" AFAIKこれは正しくありません。85KBを超えるものは、保持している時間に関係なくLOHに配置されます。 ANTS Profilerを使用していますが、実際にはかなり良いです。 –

+0

はい、RegExのフルパワーが必要です –

+0

申し訳ありませんが、私は、参照が長期間にわたって行われているときにLOHに問題が見られたことを意味しました。あなたは85k以上のものがLOHに入るということは間違いありません。 LOHの文字列の間に他のより長い期間のオブジェクトが割り当てられているため、メモリが不足するまで文字列割り当てをLOHの上に押し上げる問題がありますか? – SimonC