OutOfMemoryException при создании огромной строки в ASP.NET - PullRequest
2 голосов
/ 17 сентября 2010

При экспорте большого количества данных в строку (формат csv) я получаю исключение OutOfMemoryException.Какой лучший способ справиться с этим?Строка возвращается в приложение Flex.

Что бы я сделал, это экспортировал csv на диск сервера и вернул URL во Flex.Таким образом, я могу сбросить поток записи на диск.

Обновление:

Строка строится с помощью StringBuilder:

StringBuilder stringbuilder = new StringBuilder();
string delimiter = ";";
bool showUserData = true;

// Get the data from the sessionwarehouse
List<DwhSessionDto> collection 
     =_dwhSessionRepository.GetByTreeStructureId(treeStructureId);

// ADD THE HEADERS
stringbuilder.Append("UserId" + delimiter);
if (showUserData)
{
    stringbuilder.Append("FirstName" + delimiter);
    stringbuilder.Append("LastName" + delimiter);
}
stringbuilder.Append("SessionId" + delimiter);
stringbuilder.Append("TreeStructureId" + delimiter);
stringbuilder.Append("Name" + delimiter);
stringbuilder.Append("Score" + delimiter);
stringbuilder.Append("MaximumScore" + delimiter);
stringbuilder.Append("MinimumScore" + delimiter);
stringbuilder.Append("ReducedScore" + delimiter);
stringbuilder.Append("ReducedMaximumScore" + delimiter);
stringbuilder.Append("Duration" + delimiter);
stringbuilder.AppendLine("Category" + delimiter);

foreach (var dwhSessionDto in collection)
{
    stringbuilder.Append(
        getPackageItemsInCsvFromDwhSessionDto(
            dwhSessionDto, delimiter, showUserData));
}

return stringbuilder.ToString();

Строка отправляется обратно во Flexкак это:

var contentType = "text/csv";
string result = exportSessionService.ExportPackage(treeStructureId);
// Write the export to the response
_context.Response.ContentType = contentType;
_context.Response.AddHeader("content-disposition", 
    String.Format("attachment; filename={0}", treeStructureId + ".csv"));

// do not Omit the Vary star so the caching at the client side will be disabled
_context.Response.Cache.SetOmitVaryStar(false);
_context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

if (!String.IsNullOrEmpty(result))
{
    _context.Response.Output.Write(result);
    _context.Response.Output.Close();
}
else
{
    _context.Response.Output.Write("No logs");
}

Ответы [ 5 ]

6 голосов
/ 17 сентября 2010

Строки CSV увеличиваются до 80000 байт и попадают в LargeObjectHeap.LOH не является сборщиком мусора таким же образом, как другие поколения, и может со временем фрагментироваться, например, после многочисленных запросов к вашему серверу или если вы используете конкатенацию строк (непослушный!) Для создания этих данных CSV.В результате ваша программа резервирует гораздо больше памяти, чем на самом деле использует, и генерируется исключение OutOfMemory.

Исправление в этом случае - запись ваших данных CSV напрямую в поток ответа, а не в строковые переменные или даже StringBuilder,Это не только позволит избежать кучи больших объектов, но также снизит общее использование памяти и ускорит передачу данных пользователю.

5 голосов
/ 17 сентября 2010

String создается с помощью StringBuilder и передается обратно в приложение Flex с помощью HttpContext.Reponse.Output.Write (result); - Ливен Кардоен

Итак, что вы в основном делаете, это:

StringBuilder str = new StringBuilder();
while(whatever)
{
    str.Append(thisNextLineIGuess);
}
Response.Output.Write(str.ToString());

Правильно? Это может быть не ваш точный код, но звучит как хорошее приближение.

Тогда решение простое:

while(whatever)
{
    Response.Output.Write(thisNextLineIGuess);
}

Потоковая передача, как правило, лучше, чем буферизация всего этого и отправка его в виде комка.

5 голосов
/ 17 сентября 2010

Что бы я сделал, это экспортировать CSV-диск на серверный диск

Звучит как правильная идея.Но вы сначала создаете строку complete ?Если вы пишете построчно, вы не должны получать OOM

Edit, после просмотра кода

Вы используете только StringBuilder.Append(x), так что вы можете немного реструктурировать свой код, чтобы заменить его на_context.Response.Output.Write(x).Это может принести в жертву немного вашего разлуки, но это для хорошего дела.

И если вы хотите продолжать использовать StringBuilder, было бы очень полезно, если бы вы могли оценить результирующий размер.Добавьте щедрое поле и используйте

   StringBuilder stringbuilder = new StringBuilder(estimate);

Это экономит на увеличении (копировании) StringBuilder и уменьшает использование памяти и фрагментацию на LOH.

3 голосов
/ 17 сентября 2010

Для меня лучше создать общую страницу обработчика (ashx) и отправить все ваши данные напрямую во flex.

Прямо в поток ...

Вот хорошая статья об этой проблеме

0 голосов
/ 17 сентября 2010

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

Без дальнейших разъяснений более конкретные рекомендации являются хитростью. Но вы хотите избежать создания CSV-файла в виде строки, а скорее передавать его из источника данных на диск, а не хранить весь объект в памяти.

...