当将大量数据导出到字符串(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");
}
答案 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)
答案 4 :(得分:0)
首先,RAM会花多少钱?这些天硬件比程序员便宜。
如果不再继续下去,更具体的建议是一个小技巧。但是您希望避免将CSV实际构建为字符串,而是将其从数据源传输到磁盘,而不是将整个内容保存在内存中。