【问题标题】:MVC Razor view 'System.OutOfMemoryException' with large value string具有大值字符串的 MVC Razor 视图“System.OutOfMemoryException”
【发布时间】:2017-06-25 14:44:57
【问题描述】:

我有一个大的 JSON 对象,我需要将它传递给 Web 浏览器中显示的视图。因为我不能有一个带有 Action 的控制器来返回这个 JSON 对象,所以我想在 Razor 视图中添加 JSON 对象。

  1. @Html.Hidden("fileContent", fileContent);

  2. <textarea style="display:none"> @fileContent </textarea>

以上都不是我期望和给我的方式,

引发了“System.OutOfMemoryException”类型的异常。

说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

异常详细信息:System.OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。

是的,我同意最好以另一种方式重组流程(may be as Mediator suggest 或采取行动返回 JSON 对象。)

  1. 这是 MVC 的限制,我们可以为 MVC 视图设置的最大大小?
  2. 这是因为 IIS Express 配置吗?
  3. 还有其他方法可以解决这个问题吗?或者将大对象传递给客户端浏览器的最佳方式。

感谢您的宝贵时间。任何帮助将不胜感激。

编辑

控制器

var file = System.IO.File.ReadAllText(HttpContext.Server.MapPath("~/content/data.csv"));
ViewData.Add("file", file);
return View();

堆栈跟踪

[OutOfMemoryException:'System.OutOfMemoryException' 类型的异常 被抛出。] System.Text.StringBuilder.ExpandByABlock(Int32 minBlockCharCount) +163 System.Text.StringBuilder.Append(Char* 值,Int32 值计数)+82
System.Text.StringBuilder.AppendHelper(字符串值)+31
System.Text.StringBuilder.Append(字符串值)+186
System.IO.StringWriter.Write(字符串值) +30
System.Web.WebPages.WebPageBase.Write(对象值) +87

【问题讨论】:

  • 什么是fileContent?将它发送到视图然后再原封不动地再次发送回去有什么意义?
  • 有很多因素导致OutOfMemoryException,例如StringBuilder 某个线程的溢出或内存分配不足。检查控制器代码以确保正确处理资源分配。
  • @StephenMuecke fileContent 是一个 JSON 字符串。我将该字符串转换为客户端 java 脚本中的 JSON 对象。此 JSON 对象将用于呈现页面。
  • data.csv 有多大?
  • 问题似乎是 Razor 无法分配足够的连续内存来创建 1.5 MB 数组(由于 UTF-16 可能是 3 MB,由于 StringBuilder 的内部扩展可能是 4 MB大批)。抛出这个异常的内存太少了。尝试使用字符串数组代替,看看是否能解决问题。

标签: c# json asp.net-mvc razor


【解决方案1】:

根据您的 CSV 文件大小,使用ReadLines 比使用ReadAllText 更好,如下所示:

var sb = new StringBuilder();
sb.Capacity = 16; // default, adjust it to less number as you wish

foreach (String line in System.IO.File.ReadLines(HttpContext.Server.MapPath("~/content/data.csv")))
{
    sb.Append(line);    
}

ViewData.Add("file", sb.ToString());

ReadLines 返回IEnumerable&lt;String&gt;,它逐行读取文件(一次一行),因此它适用于某些在存储为单个大字符串时可能会占用大量内存空间的文件。

但是,如果文件内容非常大,请考虑使用StreamReader 而不是StringBuilder,这需要连续的数组大小并取决于heap fragmentation

using (var sr = new System.IO.StreamReader(HttpContext.Server.MapPath("~/content/data.csv")))
{
     String line;
     while ((line = sr.ReadLine()) != null)
     {
         // combine source strings here
     }
}

请注意,每个字符需要 2 个字节,而字符串是字符数组,因此在内存中生成对象时应该存在影响性能和大小的连续数组。因此,在处理大量需要立即清理的对象后,使用 GC.Collect() 方法优化垃圾收集器功能可能会有所帮助。

相关参考资料:

interesting OutOfMemoryException with StringBuilder

MSDN: System.Text.StringBuilder

MSDN: System.IO.StreamReader

Eric Lippert: Out Of Memory” Does Not Refer to Physical Memory

【讨论】:

  • 读取不是问题,写入视图才是问题。
  • 感谢您提供宝贵的建议。文件成功读取到变量。 (进入 ViewData),一旦尝试使用该值渲染 DOM 元素就会发生错误。
  • 不确定它是否有效,但我的想法是在执行 ReadLines 之后将每行的 JSON 字符串拆分为几个隐藏字段,其中传递了字符串数组,例如@for (int i = 0; i &lt; file.Length; i++) { @Html.Hidden("fileContent" + i, file[i]); } 由于 HTML 呈现的隐藏字段值可能存在限制。
  • @TetsuyaYamamoto 它正在工作。应该是因为 Razor 不使用 long 值。但是我再次必须在 java 脚本中将这些字符串组合成一个,这也可能导致滞后。似乎这是 Razor 引擎的限制,不是吗?
  • 我认为这不是 Razor 引擎的限制,它可能来自 HTML 规范,该规范将隐藏字段中的值限制为一定数量的字符。确认所有内容后,我将编辑上面的答案。
猜你喜欢
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 2013-05-10
  • 1970-01-01
  • 2021-08-13
  • 1970-01-01
相关资源
最近更新 更多