【问题标题】:Parsing Large Json to XML (System Out Of Memory Exception)将大型 Json 解析为 XML(系统内存不足异常)
【发布时间】:2015-06-18 07:54:40
【问题描述】:

JSON 文件(非常大)和脚本如下。

脚本

Dim JsonContents As String = IO.File.ReadAllText(filePath)

Dim doc As XmlDocument = CType(JsonConvert.DeserializeXmlNode("{""root"":" + JsonContents + "}", "root"), XmlDocument)

Dim Document As New XDocument
Dim query As String

Document = XDocument.Parse(doc.OuterXml)

query = "importJSON"

Dim CMD As New SqlCommand(query)
CMD.Parameters.Add("@type", SqlDbType.Int).Value = importType
CMD.Parameters.Add("@xmlObject", SqlDbType.Xml).Value = Document.ToString
......

JSON 文件

[{“ID”:“001”,“NAME”:“Den.Y”,“GENDER”:“M”,年龄:“18”}]


我想将其转换为 XML,然后传递给 SQL 服务器。但我发现 JSON 太大,然后在

处抛出 "System Out Of Memory Exception"
 Dim doc As XmlDocument =
 CType(JsonConvert.DeserializeXmlNode("{""root"":" + JsonContents +
 "}", "root"), XmlDocument)

我不知道如何修改脚本以便在不耗尽内存的情况下转换大型 JSON 文件,但小型 JSON 在此脚本上可以正常工作。

我在网上搜索,有人建议使用XMLReader,但我不知道如何处理。

[解决方案]

感谢大家的努力。最后,我将大的 JSON 文件剪掉,以防止脚本内存不足。现在它适用于大型 JSON。

【问题讨论】:

  • Converting JSON to XML 的可能重复项
  • JsonConvert.DeserializeXmlNode 返回一个XmlDocument 时,为什么你有CType?我希望删除它不会有帮助,但值得一试。另外,您是否可以选择运行为 x64 而不是 x86 编译的程序?
  • @ElektroStudios:我尝试删除不必要的语句,问题依旧存在。
  • @AndrewMorton:我删除了CType,问题依然存在。 'System.OutOfMemoryException' 在这个声明中被抛出: Dim JsonXMLDoc As XmlDocument = JsonConvert.DeserializeXmlNode("{root:" + JsonContents + "}", "root")
  • ***** 如果我的 JSON 文件为 55MB,则必须抛出“System.OutOfMemoryException”。如果是 33MB,“System.OutOfMemoryException”是幸运的。如果小于 5MB,则一直运行良好

标签: xml json vb.net


【解决方案1】:

首先,了解 CLR 如何处理内存很重要。 CLR 使用分代垃圾收集器,其中内存在每次收集后都会被移动到更高阶的代。此外,还有一个特殊的代,称为“大对象堆”(LOH),用于特定大小和更大的对象。您的 JSON 字符串几乎肯定会在这里结束。重要的是要知道 LOH 很少被收集。更糟糕的是,它几乎从不压缩。这意味着即使在一个对象已被收集并从内存中删除之后,为您的进程中的对象保留的虚拟地址空间仍在使用中......请记住,您的进程只有 2GB 的地址默认为空格。

顺便说一句,我们可以在您的代码中看到可能导致问题的内容。 如果(并且“如果”稍后会很重要)您正在直接进行字符串连接,这段摘录最终会创建三个复制您的字符串的对象:

"{""root"":" + JsonContents + "}"

"{""root"":" + JsonContents 部分将有一个副本,而尾随 + "}" 将有第二个新副本。值得庆幸的是,编译器通常会将其重写为更高效的内容,而您只会得到一个额外的副本。当我在这里时,我们不要忘记将这些字节从旧字符串移动到新字符串的所有 CPU 工作。

不过,一两份额外的副本通常不是什么大问题。我通常看到人们在处理使用加倍算法的文档时遇到麻烦,在该算法中,它读取数据流并分配一个新缓冲区,每次缓冲区填满时,该缓冲区的大小是旧缓冲区的两倍。 .Net 中的大多数集合类型都以这种方式工作。我不熟悉JSONConvert 类型的内部结构,但DeserializeXmlNode() 方法可能以这种方式工作。如果是这种情况,您需要找到另一种方法来创建您的 xml 文档。

不过,我们不必看那么远,就能找到一遍又一遍地创建新字符串对象的过程。看看documentation for File.ReadAllText()

此方法打开一个文件,读取文件的每一行,然后将每一行添加为字符串的一个元素。

哦哦。这听起来像是一遍又一遍地将行连接到一个字符串。如果这确实是您的错误的原因,我希望 File.ReadAllText() 调用是该过程中断的地方。但是,有可能是由我在开头显示的字符串连接创建的额外副本,加上用于DeserializeXmlNode() 调用和CType() 转换结果的任何内存——请记住:如果 CType() 实际上正在执行这些工作将是两个独立的(大)物体——是压断骆驼的稻草。

您知道文件有多大,因此我们可以通过从 StreamReader 读取并将每一行写入使用 constructor overload the pre-allocates space for buffer 创建的 StringBuilder 对象来做得更好。这样,所有中间步骤都足够小,可以避免大型对象堆。但我怀疑这还不够,您需要找到 JSONConvert 类型的替代方法来创建您的 XmlDocument 对象。

当然,您的 JSON 文件可能就是这么大。在这种情况下,您可能需要将其拆分为对数据库的多个调用,并一次从文件中读取一个部分。 .Net 中的单个内存对象不允许超过 2GB,即使在 64 位系统上也是如此(进程可以变大,但单个对象不能)。

【讨论】:

  • 欣赏详细解说!我打破陈述并逐步进行。我发现File.ReadAllText( ) 工作正常并成功阅读了所有文本,但我发现System.OutOfMemoryExceptionJsonConvert.DeserializeXmlNode( ) 抛出。由于我的 JSON 文件最大只有 55MB,我不知道为什么它会达到 2GB 虚拟内存限制,导致它抛出 System.OutOfMemoryException。还有一件事,由于错误来自JsonConvert.DeserializeXmlNode( ),我想知道在VB.NET 中是否有将大型JSON 转换为XML 的建议?
  • 仅仅因为 File.ReadAllText() 在没有抛出错误的情况下完成,并不意味着它没有在这个过程中咀嚼你的大部分虚拟地址空间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 1970-01-01
相关资源
最近更新 更多