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

时间:2010-09-17 14:09:09

标签: .net asp.net out-of-memory response.write

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

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

更新

使用StringBuilder构建String:

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 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:5)

  

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

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

在看到代码后编辑

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

如果您想继续使用StringBuilder,如果您可以估算结果大小,那将会有很大帮助。添加大量保证金并使用

   StringBuilder stringbuilder = new StringBuilder(estimate);

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

答案 2 :(得分: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());

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

然后解决方案很简单:

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

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

答案 3 :(得分:3)

对我来说,最好创建一个通用处理程序页面(ashx),并将所有数据直接发送到flex。

直接到流......

以下是nice article about this issue

答案 4 :(得分:0)

首先,RAM会花多少钱?这些天硬件比程序员便宜。

如果不再继续下去,更具体的建议是一个小技巧。但是您希望避免将CSV实际构建为字符串,而是将其从数据源传输到磁盘,而不是将整个内容保存在内存中。