I have a 32-bit .NET Core 3.1 app (important, because we don't crash with a 64-bit app; at least not with this much data).
It deserializes data that comes in from another process that I read via a named pipe:
TextReader reader = TextReader.Synchronized(new StreamReader(namedPipeServer));
string data = reader.ReadLine(); // messages are separated by newlines
string deserializedMessage = JsonConvert.DeserializeObject<string>(data); // string for simplicity; it's actually a more complex object
When the data is roughly 100MB (so set data
to new string('a', 100 * 1024 * 1024)
), Newtonsoft crashes with an OutOfMemory Exception:
System.OutOfMemoryException
HResult=0x8007000E
Message=Exception of type 'System.OutOfMemoryException' was thrown.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Utilities.BufferUtils.RentBuffer(IArrayPool`1 bufferPool, Int32 minSize)
at Newtonsoft.Json.JsonTextReader.PrepareBufferForReadData(Boolean append, Int32 charsRequired)
at Newtonsoft.Json.JsonTextReader.ReadData(Boolean append, Int32 charsRequired)
at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)
at Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)
at Newtonsoft.Json.JsonTextReader.ReadAsString()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)
at OutOfMemory.Program.Main(String[] args)
I found this related question that suggested to use streams, however I get the same crash doing that:
using JsonReader reader = new JsonTextReader(new StringReader(data));
JsonSerializer serializer = new JsonSerializer();
string deserializedMessage = serializer.Deserialize<string>(reader);
System.OutOfMemoryException
HResult=0x8007000E
Message=Exception of type 'System.OutOfMemoryException' was thrown.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Utilities.BufferUtils.RentBuffer(IArrayPool`1 bufferPool, Int32 minSize)
at Newtonsoft.Json.JsonTextReader.PrepareBufferForReadData(Boolean append, Int32 charsRequired)
at Newtonsoft.Json.JsonTextReader.ReadData(Boolean append, Int32 charsRequired)
at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)
at Newtonsoft.Json.JsonTextReader.ReadStringValue(ReadType readType)
at Newtonsoft.Json.JsonTextReader.ReadAsString()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
at OutOfMemory.Program.Main(String[] args)
I have considered not reading the full line of data, but instead using a stream from the get-go, like so:
using JsonReader reader = new JsonTextReader(textReader);
JsonSerializer serializer = new JsonSerializer();
serializer.Deserialize<string>(reader);
But that just hangs, I think because according to the docs, JsonSerializer.Deserialize
will read the Stream to completion, but I might have multiple messages coming through on the Stream.
Is there another solution to this, or am I just doing something wrong with the Streams?
Example Main method:
int mB = 150;
var serializedMessage = JsonConvert.SerializeObject(new string('a', mB * 1024 * 1024));
using JsonReader reader = new JsonTextReader(new StringReader(serializedMessage));
JsonSerializer serializer = new JsonSerializer();
string result = serializer.Deserialize<string>(reader);
//result = JsonConvert.DeserializeObject<string>(serializedMessage);
Console.WriteLine(result.Length);
Console.ReadKey();