在ASP.NET中创建大字符串时出现OutOfMemoryException

Lie*_*oen 2 .net asp.net response.write out-of-memory

将大量数据导出到字符串(csv格式)时,我得到一个OutOfMemoryException.解决这个问题的最佳方法是什么?该字符串将返回到Flex应用程序.

我要做的是将csv导出到服务器磁盘并返回给Flex的URL.像这样,我可以刷新写入磁盘的流.

更新:

String是使用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();
Run Code Online (Sandbox Code Playgroud)

该字符串将发送回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");
}
Run Code Online (Sandbox Code Playgroud)

Joe*_*orn 6

您的CSV字符串增长到超过80000字节,最后是LargeObjectHeap.LOH不是以与其他代相同的方式进行垃圾收集,并且可能会随着时间的推移而碎片化,例如在向服务器发出许多请求之后,或者如果使用字符串连接(淘气!)来构建此csv数据.结果是您的程序保留了比实际使用的内存更多的内存,并抛出了OutOfMemory异常.

此实例中的修复是将csv数据直接写入Response流而不是字符串变量甚至StringBuilder.这不仅可以避免大对象堆,而且可以降低整体内存使用率并开始更快地将数据推送给用户.


Hen*_*man 5

我要做的是将csv导出到服务器磁盘

听起来像是正确的想法.但是你先创建完整的字符串吗?如果你逐行写,你不应该得到OOM

看到代码后编辑

您只是使用StringBuilder.Append(x),因此您可以稍微重新构建代码以替换它_context.Response.Output.Write(x).这可能会牺牲你的一点点分离,但这是一个很好的理由.

如果你想继续使用StringBuilder,如果你能估算出最终的大小,它会有很大的帮助.添加大量保证金并使用

   StringBuilder stringbuilder = new StringBuilder(estimate);
Run Code Online (Sandbox Code Playgroud)

这节省了StringBuilder的增长(复制)并减少了LOH上的内存使用和碎片.

  • 如果使用写入流(文件或响应)替换追加到StringBuilder,则可以避免所有内存问题. (2认同)

Ran*_*pho 5

String使用StringBuilder创建,并通过HttpContext.Reponse.Output.Write(result)传回Flex应用程序; - Lieven Cardoen

所以你基本上做的是:

StringBuilder str = new StringBuilder();
while(whatever)
{
    str.Append(thisNextLineIGuess);
}
Response.Output.Write(str.ToString());
Run Code Online (Sandbox Code Playgroud)

正确?这可能不是您的确切代码,但听起来像是一个很好的近似值.

然后解决方案很简单:

while(whatever)
{
    Response.Output.Write(thisNextLineIGuess);
}
Run Code Online (Sandbox Code Playgroud)

流式传输通常比缓冲整个事物并将其作为一个整体发送出去更好.