OK、これが私の試みですが、かなり一般的な方法でこの問題を解決しますが、いくつかの明らかな制限があります。私はこのアドバイスをどこにも見たことがなく、誰もがLOHフラグメンテーションについて泣き言を言っているので、コードを共有して、私の設計と仮定が正しいことを確認したいと思いました。
仮説:
- 共有の大規模なStringBuilderを作成します(これは、ストリームから読み取った大きな文字列を格納するためのものです)-
new StringBuilder(ChunkSize * 5);
- 大規模な文字列を作成し(最大許容サイズよりも大きくする必要があります)、空のスペースで初期化する必要があります。-新しい文字列(''、ChunkSize * 10);
- 文字列オブジェクトをメモリに固定して、GCが混乱しないようにします。
GCHandle.Alloc(pinnedText, GCHandleType.Pinned)
。LOHオブジェクトは通常固定されていますが、これによりパフォーマンスが向上するようです。たぶんunsafe
コードのせい
- ストリームを共有StringBuilderに読み込み、インデクサーを使用して安全でない方法でpinnedTextにコピーします
- pinnedTextをRegExに渡します
この実装では、以下のコードはLOH割り当てがない場合と同じように機能します。new string(' ')
静的コードを使用する代わりに割り当てに切り替えるStringBuilder
か、コードを使用すると、クラッシュする前に300%少ないメモリStringBuilder.ToString()
を割り当てることができますoutofmemory exception
また、この実装にはLOHフラグメンテーションがないことを、メモリプロファイラーで確認しました。正規表現が予期しない問題を引き起こさない理由はまだわかりません。また、異なる高価な正規表現パターンでテストしましたが、結果は同じで、断片化はありません。
コード:
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));
}
}
}
}