I have a C# .NET system that takes a JSON
data feed and converts it to an object using the Newtonsoft.Json.JsonConvert.DeserializeObject
converter.
This process works perfect as long as the JSON string is below a certain size (a few Mb) but as soon as the returned data is large (almost 100Mb) I get the error OutOfMemoryException
This code works great for small data:
// WebClient ------------------------------------------------------------------
var _client = new System.Net.WebClient();
var _content = _client.DownloadString(_url);
but blows up on the last line (DownloadString
)
I tried changing to this which also works for small data but it still blew up on the ReadToEnd
line when the data grew in size.
using (var _response = (System.Net.HttpWebResponse)_request.GetResponse())
{
using (System.IO.Stream _dataStream = _response.GetResponseStream())
{
using (System.IO.StreamReader _streamReader = new System.IO.StreamReader(_dataStream))
{
string _responseFromServer = _streamReader.ReadToEnd();
}
}
}
Finally I tried this which worked:
StringBuilder _stringBuilder = new StringBuilder();
using (var _response = (System.Net.HttpWebResponse)_request.GetResponse())
{
using (System.IO.Stream _dataStream = _response.GetResponseStream())
{
using (System.IO.StreamReader _streamReader = new System.IO.StreamReader(_dataStream))
{
while (!streamReader.EndOfStream)
{
char[] _buffer = new char[4096];
_streamReader.ReadBlock(_buffer, 0, _buffer.Length);
var _bufferString = new String(_buffer);
_stringBuilder.Append(_bufferString);
}
}
}
}
But it blew up with an OutOfMemoryException
error when it got to the next line here:
var _results = Newtonsoft.Json.JsonConvert.DeserializeObject<List<MyObject>>(_stringBuilder.ToString());
It didn't like the ToString()
method.
It also crashed with a simple line like
string _convertedString = _stringBuilder.ToString();
The full error is:
An exception of type 'System.OutOfMemoryException' occurred in mscorlib.dll but was not handled in user code
The machine is running 64bit windows with 16Gb of memory.
So, what are my options?
All I want is an IQueryable<MyObject>
from a (very large) JSON
string.
Well, according to the topic of the question, best way to avoid out of memory exception would be not to create objects that fill in that memory. Then you can calculate the length of your queue based on estimate of one object memory capacity. Another way would be to check for memory size in each worker thread.
When data structures or data sets that reside in memory become so large that the common language runtime is unable to allocate enough contiguous memory for them, an OutOfMemoryException exception results.
An OutOfMemoryException exception has two major causes: You are attempting to expand a StringBuilder object beyond the length defined by its StringBuilder. MaxCapacity property. The common language runtime cannot allocate enough contiguous memory to successfully perform an operation.
Working with large data sets requires a huge amount of memory and if CLR does not have enough contiguous space available for it then it results in OutOfMemoryException. As strings are immutable, the operations performed on string create a new string in the memory.
catch(OutOfMemoryException exception) {//statements to handle the exception} The syntax to throw an OutOfMemoryException in C# is as follows: throw new OutOfMemoryException(); In the above statement ‘throw’ is the keyword which is used to throw exceptions in C#.
The output from the example shows that, because the example stores the entire array in memory before it calculates the mean, an OutOfMemoryException is thrown. using System; using System.Collections.Generic; public class Example { public static void Main() { Double [] values = GetData (); // Compute mean.
OutOfMemoryException uses the HRESULT COR_E_OUTOFMEMORY, which has the value 0x8007000E. For a list of initial property values for an instance of OutOfMemoryException, see the OutOfMemoryException constructors. The value of the inherited Data property is always null.
Your code essentially emulates what StreamReader.ReadToEnd does, taking at least 4 times the memory needed to read a large response (the memory of the string response itself, the StringBuilder's internal buffer, the size of all the intermediate temporary strings and the final string).
You can avoid this by deserializing from the stream directly with a JsonTextReader. Copying from the documentation sample:
using (var json= new JsonTextReader(streamReader))
{
JsonSerializer serializer = new JsonSerializer();
return (List<MyObject>)serializer.Deserialize(json, typeof(List<MyObject>));
}
O
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With