OutOfMemoryException в Regex Matches при обработке больших файлов

У меня есть журнал исключений из одного из выпусков производственного кода.

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.Text.RegularExpressions.Match..ctor(Regex regex, Int32 capcount, String text, Int32 begpos, Int32 len, Int32 startpos)
   at System.Text.RegularExpressions.RegexRunner.InitMatch()
   at System.Text.RegularExpressions.RegexRunner.Scan(Regex regex, String text, Int32 textbeg, Int32 textend, Int32 textstart, Int32 prevlen, Boolean quick)
   at System.Text.RegularExpressions.Regex.Run(Boolean quick, Int32 prevlen, String input, Int32 beginning, Int32 length, Int32 startat)
   at System.Text.RegularExpressions.MatchCollection.GetMatch(Int32 i)
   at System.Text.RegularExpressions.MatchEnumerator.MoveNext()

Данные, которые он пытается обработать, составляют около 800 КБ.

В моих локальных тестах он работает отлично. Вы когда-нибудь видели подобное поведение, в чем может быть причина?

Должен ли я разделить текст перед его обработкой, но, очевидно, в этом случае регулярное выражение может не совпадать, потому что исходный файл отделяется от случайного места.

Мои регулярные выражения:

ИЗМЕНИТЬ 2:

Я думаю, что именно этот RegEx вызывает проблему, когда я тестирую его в изолированной среде, он мгновенно съедает память.

((?:( |\.\.|\.|""|'|=)[\/|\?](?:[\w#!:\.\?\+=&@!$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})*)( |\.|\.\.|""|'| ))?

ИЗМЕНИТЬ

Я ошибся с моим местным тестом. Я загружал большую строку, а затем добавлял к ней что-то, что вызывало головокружение у .NET Framework, а затем выдавал исключение OOM во время RegEx, а не во время строковых операций (или случайным образом, поэтому игнорируйте предыдущие вещи, которые я сказал).

Это приложение .NET Framework 2.0.


person dr. evil    schedule 07.04.2009    source источник
comment
Можете ли вы перечислить код, который использует регулярное выражение?   -  person Seibar    schedule 07.04.2009


Ответы (3)


Не видя вашего регулярного выражения, я не знаю наверняка, но иногда вы можете столкнуться с такими проблемами, потому что ваши совпадения являются жадными, а не ленивыми.

Механизм регулярных выражений должен хранить много информации внутри себя, и жадные совпадения могут привести к тому, что регулярные выражения будут многократно выбирать большие разделы вашей строки 800k.

Полезная информация об этом есть здесь.

person Russ Clarke    schedule 07.04.2009
comment
поставить мои регулярные выражения на вопрос. - person dr. evil; 07.04.2009
comment
У вас есть небольшой фрагмент того, что вы пытаетесь сопоставить? Это HTML или это текст, который может содержать URL-адреса? - person Russ Clarke; 07.04.2009
comment
Что произойдет в последнем регулярном выражении, если вы измените последний «» на « - person Russ Clarke; 07.04.2009
comment
Это HTML, просто используйте исходный код Yahoo, на самом деле для всего требуется целая вечность. Сейчас попробую это изменение. - person dr. evil; 07.04.2009
comment
Похоже, у меня не сработало, это регулярное выражение максимально увеличивает мою тестовую программу с 30-килобайтным html-файлом для циклов ЦП! - person Russ Clarke; 07.04.2009
comment
это работает, но больше не соответствует тому, что я хочу. Попробуйте это и увидите разницу /cool.htm dontmatch На самом деле регулярное выражение само по себе является мусором, я уверен, что есть лучший способ написать это. - person dr. evil; 07.04.2009
comment
Что регулярное выражение действительно хочет сопоставить? Как вы сказали, я думаю, что он, вероятно, делает неправильную вещь. Пример, который вы мне дали, на самом деле сгенерировал 19 совпадений! - person Russ Clarke; 07.04.2009
comment
Это работает намного быстрее, но я не знаю, правильно ли это: ( |\.\.|\.||'|=)[\/|\?](?:[\w#!:\.\? \+=&@!$'~*,;\/()[]\-]|%[0-9a-f]{2})*( |\.|\.\.||'| ) - person Russ Clarke; 07.04.2009
comment
Мне это кажется лучше, оно не соответствует /css в ссылке type=text/css... (?:=?)([\/|\?](?:[\w#!:\. \?\+=&@!$'~*,;\/()[]\-]|%[0-9a-f]{2})*) - person Russ Clarke; 07.04.2009

Основываясь на вашем редактировании, похоже, что ваш код может создавать строки, которые занимают большой объем памяти. Это будет означать, что хотя исключение нехватки памяти генерируется внутри кода Regex, на самом деле это происходит не потому, что само Regex занимает слишком много памяти. Поэтому, если использование StringBuilder в вашем собственном коде решает проблему, то вы должны сделать именно это.

person kvb    schedule 07.04.2009
comment
точно вы правы, я только что понял это и обновил свой образец. Однако это не было поведением исходного кода, это был тестируемый код. Проверьте мое последнее редактирование. - person dr. evil; 07.04.2009

Первое, что я бы попробовал, если это возможно для вашего приложения, - разделить ввод.

Можно ли будет прочитать файл (если ввод является файлом) построчно, применяя регулярное выражение таким образом?

Вы должны посмотреть с помощью профилировщика CLR. Обучение использованию может занять некоторое время, но оно того стоит. Это поможет вам визуализировать, сколько памяти используют ваши объекты.

person Seibar    schedule 07.04.2009
comment
2 проблемы с этим. Я изначально использую SingleLine в качестве параметров в регулярном выражении. Поэтому чтение его построчно может привести к поломке некоторых регулярных выражений. Во-вторых, это будет плохо влиять на производительность более коротких файлов (очевидно, я могу переключаться в зависимости от размера, но звучит грязно :), однако, если я не могу это исправить, это хорошая идея) - person dr. evil; 07.04.2009
comment
Держу пари, что разница в производительности между построчным выполнением небольших файлов и одновременным применением регулярных выражений будет достаточно минимальной, чтобы не оказать заметного влияния. - person Seibar; 07.04.2009
comment
Я знаю, что stringbuilder хорош для этого, но я имел в виду, что использование stringbuilder с Regex звучит так, будто помогает RegEx runner более эффективно использовать память, что для меня совершенно ново. - person dr. evil; 07.04.2009
comment
Это возможно, и я не уверен, что StringBuilder каким-то образом позволяет Regex работать более эффективно, но IMO это не то, на что вам следует полагаться. Если у вас заканчивается память, это может свидетельствовать о более серьезной проблеме проектирования, которую необходимо решить. - person Seibar; 07.04.2009
comment
По-видимому, я испортил свой локальный тест, проверьте, пожалуйста, мое окончательное редактирование. - person dr. evil; 07.04.2009