'OutOfMemoryException while making a Http GET request
Its a straight forward scenario: make the request and convert the json result into equivalent POCO class. Initially I was converting the response to string and then deserializing it :
private async Task<T> CallApiAsync<T>(string url)
{
var httpResponseMessage = await _httpClient.GetAsync(url);
if (httpResponseMessage.IsSuccessStatusCode)
{
var responseBody = await httpResponseMessage.Content.ReadAsStringAsync(); // this one here
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseBody);
}
var errors = await httpResponseMessage.Content.ReadAsStringAsync();
throw DomainError(httpResponseMessage.StatusCode, url, errors);
}
For which I used to get this intermittent error:
"Message": "Exception of type 'System.OutOfMemoryException' was thrown.",
"Source": "System.Private.CoreLib",
"StackTraceString": " at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
in /_/src/System.Private.CoreLib/shared/System/String.cs:line 505\n
at System.Text.UTF8Encoding.GetString(Byte[] bytes, Int32 index, Int32 count)
in /_/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs:line 663\n
at System.Net.Http.HttpContent.ReadBufferAsString(ArraySegment`1 buffer, HttpContentHeaders headers)
in /_/src/System.Net.Http/src/System/Net/Http/HttpContent.cs:line 223\n
at System.Net.Http.HttpContent.ReadBufferedContentAsString() in /_/src/System.Net.Http/src/System/Net/Http/HttpContent.cs:line 182\n
at System.Net.Http.HttpContent.WaitAndReturnAsync[TState,TResult](Task waitTask, TState state, Func`2 returnFunc)
in /_/src/System.Net.Http/src/System/Net/Http/HttpContent.cs:line 717\n
at Infrastructure.Clients.SecurityMasterFileService.CallApiAsync[T](String url)
in /app/Infrastructure/Clients/SecurityMasterFileService.cs:line 68\n
So I switched to reading from stream since there was no point converting to string and hogging up the memory:
private async Task<T> CallApiAsync<T>(string url)
{
using var httpResponseMessage = await _httpClient.GetAsync(url); //added "using"
if (httpResponseMessage.IsSuccessStatusCode)
{
using var responseBody = await httpResponseMessage.Content.ReadAsStreamAsync(); //changed this line
return await System.Text.Json.JsonSerializer.DeserializeAsync<T>(responseBody); //also changed from Newtonsoft to System.Text since the former did not have async overload
}
var errors = await httpResponseMessage.Content.ReadAsStringAsync();
throw DomainError(httpResponseMessage.StatusCode, url, errors);
}
Now I am getting this intermittent error:
"Message": "Exception of type 'System.OutOfMemoryException' was thrown.",
"Source": "System.Private.CoreLib",
"StackTraceString": " at System.IO.MemoryStream..ctor(Int32 capacity)
in /_/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs:line 47\n
at System.Net.Http.HttpContent.CreateMemoryStream(Int64 maxBufferSize, Exception& error)
in /_/src/System.Net.Http/src/System/Net/Http/HttpContent.cs:line 522\n
at System.Net.Http.HttpContent.LoadIntoBufferAsync(Int64 maxBufferSize, CancellationToken cancellationToken)
in /_/src/System.Net.Http/src/System/Net/Http/HttpContent.cs:line 430\n
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
in /_/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:line 546\n
at Infrastructure.Clients.SecurityMasterFileService.CallApiAsync[T](String url) in /app/Infrastructure/Clients/SecurityMasterFileService.cs:line 68\n
So now I am wondering what further needs to be fixed in the new implementation. Can someone point me out in the correct direction. Thank you.
Edit: Adding the json payload : https://jsonblob.com/4d2eac0f-f42a-11eb-9b1d-736dff69cd5c
Note: From the json payload I require only the first 2 objects, so I have a POCO class for the first two:
public class Root
{
public RootA RootA { get; set; }
public RootB RootB { get; set; }
}
public class RootA
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public int Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
public int Field6 { get; set; }
public int Field7 { get; set; }
public string Field8 { get; set; }
public string Field9 { get; set; }
public int Field10 { get; set; }
public int Field11 { get; set; }
public int Field12 { get; set; }
public int Field13 { get; set; }
public DateTime Field14 { get; set; }
public string Field15 { get; set; }
public DateTime Field16 { get; set; }
public string Field17 { get; set; }
public string Field18 { get; set; }
public string Field19 { get; set; }
public string Field20 { get; set; }
public string Field21 { get; set; }
public string Field22 { get; set; }
public string Field23 { get; set; }
public string Field24 { get; set; }
public string Field25 { get; set; }
public string Field26 { get; set; }
public DateTime Field27 { get; set; }
}
public class RootB
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public DateTime Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
public string Field6 { get; set; }
public string Field7 { get; set; }
public string Field8 { get; set; }
public int Field9 { get; set; }
public int Field10 { get; set; }
public int Field11 { get; set; }
public int Field12 { get; set; }
public int Field13 { get; set; }
public string Field14 { get; set; }
public int Field15 { get; set; }
public int Field16 { get; set; }
public int Field17 { get; set; }
public int Field18 { get; set; }
public int Field19 { get; set; }
public int Field20 { get; set; }
public int Field21 { get; set; }
public string Field22 { get; set; }
public int Field23 { get; set; }
public string Field24 { get; set; }
public int Field25 { get; set; }
public int Field26 { get; set; }
public string Field27 { get; set; }
public int Field28 { get; set; }
public int Field29 { get; set; }
public string Field30 { get; set; }
public int Field31 { get; set; }
public string Field32 { get; set; }
public string Field33 { get; set; }
public string Field34 { get; set; }
public string Field35 { get; set; }
public string Field36 { get; set; }
public string Field37 { get; set; }
public int Field38 { get; set; }
public string Field39 { get; set; }
public int Field40 { get; set; }
public string Field41 { get; set; }
public DateTime Field42 { get; set; }
public DateTime Field43 { get; set; }
public DateTime Field44 { get; set; }
public string Field45 { get; set; }
public DateTime Field46 { get; set; }
public DateTime Field47 { get; set; }
public string Field48 { get; set; }
public int Field49 { get; set; }
public string Field50 { get; set; }
public string Field51 { get; set; }
public DateTime Field52 { get; set; }
public string Field53 { get; set; }
public string Field54 { get; set; }
public string Field55 { get; set; }
public int Field56 { get; set; }
public string Field57 { get; set; }
public string Field58 { get; set; }
public string Field59 { get; set; }
public string Field60 { get; set; }
public string Field61 { get; set; }
public DateTime Field62 { get; set; }
public string Field63 { get; set; }
public int Field64 { get; set; }
public string Field65 { get; set; }
public string Field66 { get; set; }
}
Solution 1:[1]
I got a feedback from one of the senior developer. He asked me to switch from ReadAsStreamAsync to ReadAsByteArrayAsync. Now I don't get the error anymore and its stable in production also
private async Task<T> CallApiAsync<T>(string url)
{
using var httpResponseMessage = await _httpClient.GetAsync(url);
if (httpResponseMessage.IsSuccessStatusCode)
{
var responseBody = await httpResponseMessage.Content.ReadAsByteArrayAsync(); //this fixed the issue
return JsonSerializer.Deserialize<T>(responseBody);
}
var errors = await httpResponseMessage.Content.ReadAsStringAsync();
throw GetDomainError(httpResponseMessage.StatusCode, url, errors);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | afrose |
