从磁盘

时间:2016-10-10 09:38:52

标签: c# json json.net

我有一个1.2 GB的json文件,在反序列化时应该给我一个包含15 mil对象的列表。

我正在尝试对其进行反序列化的机器是具有16核和32 GB Ram的Windows 2012服务器(64位)。

应用程序已构建为x64目标。

尽管如此,当我尝试阅读json doc并将其转换为对象列表时,我将失去内存异常。 当我看到任务管理器时,我发现只使用了5GB内存。

我尝试的代码如下。

一个。

 string plays_json = File.ReadAllText("D:\\Hun\\enplays.json");

                plays = JsonConvert.DeserializeObject<List<playdata>>(plays_json);

 string plays_json = "";
        using (var reader = new StreamReader("D:\\Hun\\enplays.json"))
        {
            plays_json = reader.ReadToEnd();
            plays = JsonConvert.DeserializeObject<List<playdata>>(plays_json);
        }

下进行。

 using (StreamReader sr = File.OpenText("D:\\Hun\\enplays.json"))
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(sr.ReadToEnd());
            plays_json = sb.ToString();
            plays = JsonConvert.DeserializeObject<List<playdata>>(plays_json);
        }

真诚感谢所有帮助

2 个答案:

答案 0 :(得分:7)

问题在于,您正在将整个大文件读入内存,然后尝试将其全部反序列化为一个巨大的列表。您应该使用StreamReader来逐步处理文件。您的问题中的示例(b)并未剪切,即使您在那里使用StreamReader,因为您仍在通过ReadToEnd()读取整个文件。你应该做这样的事情:

using (StreamReader sr = new StreamReader("D:\\Hun\\enplays.json"))
using (JsonTextReader reader = new JsonTextReader(sr))
{
    var serializer = new JsonSerializer();

    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            // Deserialize each object from the stream individually and process it
            var playdata = serializer.Deserialize<playdata>(reader);

            ProcessPlayData(playdata);
        }
    }
}

ProcessPlayData方法应处理单个playdata对象,然后理想地将结果写入文件或数据库而不是内存列表(否则您可能会再次发现自己处于相同的情况)。如果必须将处理每个项目的结果存储到内存列表中,那么您可能需要考虑使用链接列表或类似结构,该结构不会尝试在一个连续块中分配内存,并且不需要重新分配和复制何时需要扩展。

答案 1 :(得分:2)

在我看来,您的内存不足异常可能是由于以下原因之一。

对象plays的大小超过2GB,默认情况下,.NET中CLR对象的最大大小为2GB(即使在x64上)See here

现在,您的对象不必是2GB。大对象堆(LOH)中的碎片可能导致小于2GB的对象也会引发内存不足异常。 (任何超过80kb的对象都将驻留在大对象堆中)

另一种情况是操作系统无法为大型对象分配连续的虚拟内存块,但我不认为这是因为你提到你有32GB内存。

除非没有其他选项,否则我不会去启用gcAllowVeryLargeObjects。我看到在打开该设置后,我的一个大数据处理Apis的内存消耗从3GB上升到8GB。 (尽管大部分内容仅保留)我认为这是因为您允许您的应用程序向操作系统询问存储大型对象所需的内存量。如果您在同一台服务器上托管其他应用程序,这可能会特别成问题。对托管对象可以占用多少内存有一个上限。

另外需要注意的是,默认情况下GC不会压缩LOH。因此,这意味着工作集的大小将保持很大,除非进行完整的垃圾收集。 (您可以从.NET 4.5.1开始调用GC来压缩LOH)See here

我强烈建议使用像dotMemory这样的内存分析器,在做出任何决定之前先了解一下发生了什么。

如果您的目标是x64,并且这是一个Web应用程序,那么请确保IIS也设置为使用64位版本。请点击此处查看local IIS expressIIS on Server

如果我是你,我会尝试将这项任务分成小批量。

一次加载整个文件的目的是什么?您是否尝试使用加载的数据或任何CPU密集型任务执行某些IO操作?

这是一个有用的link on GC fundamentals