Вопрос по c#, memory, garbage-collection, stringbuilder – интересное исключение OutOfMemory с StringBuilder

11

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

Что в основном происходит здесь, я создаю строку, используяXmlWriter с участиемStringBuilder на основании некоторых данных. Затем я вызываю метод из внешней библиотеки, который преобразует эту строку XML в другую строку. После этого преобразованная строка сохраняется в базе данных. Все это повторяется в цикле около 100 раз для разных данных.

Строки сами по себе не слишком велики (менее 500 Кбайт каждая), и память процесса не увеличивается во время этого цикла. Но все же, иногда я получаюOutOfMemeoryExcpetion вStringBuilder.Append, Интересно, что это исключение не приводит к падению. Я могу поймать это исключение и продолжить цикл.

Что здесь происходит? Зачем мне получатьOutOfMemoryException хотя в системе все еще достаточно свободной памяти? Это какая-то проблема с кучей GC?

Учитывая, что я не могу обойти преобразование всех этих строк, что я мог сделать, чтобы сделать это надежно? Должен ли я форсировать сбор GC? Следует положитьThread.Sleep в петлю? Должен ли я прекратить использованиеStringBuilder? Следует просто повторить попытку при столкновении сOutOfMemoryException?

Ваш Ответ

3   ответа
3

ных.

После или до использования просто сбросьте размер StringBuilder на 0 и начните добавлять. Это уменьшит количество выделений и, возможно, сделает ситуацию OutOfMemory очень редкой.

Чтобы проиллюстрировать мою точку зрения:

void MainProgram()
{
    StringBuilder builder = new StringBuilder(2 * 1024); //2 Kb

    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
}

void PerformOperation(StringBuilder builder)
{
    builder.Length = 0;

    //
    // do the work here builder.Append(...);
    //
}
3

Куча больших объектов (LOH) фрагментация.

Повторное использование объектов StringBuilder не является прямым решением, вам нужно получить контроль над базовыми буферами.
Если возможно, рассчитайте или оцените размер заранее и предварительно выделите.

И это может помочь, если вы вокругвверх ассигнования, скажем, кратно 20 КБ или около того. Это может улучшить повторное использование.

bitbonk, да, что-то в этом роде. Худший шаблон для LOH - это выделение блоков с увеличивающимся размером и / или чередование коротких и долгоживущих блоков. Henk Holterman
Почему повторное использование объектов StringBuilder не является прямым решением? Вы просто подразумеваете, что повторное использование + предварительное распределение (размера, который подходит всем будущим строкам) будет решением? bitbonk
14

но нет смежного сегмента, который может обрабатывать размер вашего строителя строк. Вы должны знать, что каждый раз, когда буфер строителя строк слишком короткий, его размер удваивается. Если вы можете определить (в ctor) размер вашего компоновщика, это лучше. Вы МОЖЕТЕ позвонитьGC.Collect() когда вы закончите с большой коллекцией объектов.

На самом деле, когда у вас есть OutOfMemory, он, как правило, показывает плохой дизайн, вы можете использовать жесткий диск (временные файлы) вместо памяти, вы не должны выделять память снова и снова (попробуйте повторно использовать объекты / буферы / ...) ,

Я настоятельно советую вам прочитать этот пост«Недостаточно памяти» не относится к физической памяти от Эрика Липперта.

Ну, это может перераспределить, но если в мусоре нет другого SB, это будет намного более эффективно. Guillaume
Не совсем бессмысленно: вы можете избежать фрагментации памяти, которая кажется настоящей проблемой здесь. Guillaume
Если я буду использовать один и тот же StringBuilder снова и снова, буду ли я использовать один и тот же сегмент памяти (кроме случаев, когда SB нужно увеличить себя)? Разве это не решит все мои проблемы (скорее всего)? bitbonk
Я не уверен, что вам нужно будет попытаться выяснить, поддерживает ли сборщик строк свою емкость после очистки ... Guillaume
SB имеет буфер, если вы повторно используете SB, вы можете повторно использовать буфер. Если вы создаете новый SB каждый раз, вы создаете новый буфер каждый раз, а старые буферы находятся в мусоре. Даже если размер повторно используемого строителя строк уменьшен, мусора будет намного меньше, поскольку есть только буферы от этого сборщика, а не все буферы от всех строителей строк предыдущих операций. Guillaume

Похожие вопросы