Ответ 1
ОК, вот моя попытка решить эту проблему довольно общим образом, но с некоторыми очевидными ограничениями. Поскольку я не видел этот совет нигде, и все скулят о LOH Fragmentation, я хотел бы поделиться кодом, чтобы подтвердить, что мой дизайн и предположения верны.
Теория:
- Создайте общий массив StringBuilder (это для хранения больших строк, которые читаются из чтения из потоков) -
new StringBuilder(ChunkSize * 5);
- Создание массивной строки (должно быть больше, чем максимально допустимый размер), должно быть инициализировано пустым пространством. - новая строка ('', ChunkSize * 10);
- Вставьте строковый объект в память, чтобы GC не возился с ним.
GCHandle.Alloc(pinnedText, GCHandleType.Pinned)
. Хотя объекты LOH обычно закреплены, это, по-видимому, улучшает производительность. Возможно, из-заunsafe
code - Прочитайте поток в общий StringBuilder, а затем небезопасно скопируйте его в pinnedText с помощью индексаторов
- Передайте pinnedText в RegEx
С этой реализацией код ниже работает так же, как нет распределения LOH. Если я переключусь на new string(' ')
, вместо использования статического StringBuilder
или использующего StringBuilder.ToString()
кода, можно выделить на 300% меньше памяти перед сбоем с помощью outofmemory exception
Я также подтвердил результаты с помощью профилировщика памяти, что в этой реализации нет фрагментации LOH. Я до сих пор не понимаю, почему RegEx не вызывает никаких неожиданных проблем. Я также тестировал различные и дорогие шаблоны RegEx, и результаты одинаковы, без фрагментации.
Код:
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));
}
}
}
}