-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory grows with each request, PinnedBlockMemoryPool never release memory #55490
Comments
Can you still repro with this change? - var memory = System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64;
+ long memory;
+ using (var process = System.Diagnostics.Process.GetCurrentProcess())
+ {
+ memory = process.PrivateMemorySize64;
+ } |
I've running it in docker on Processor count: 2
Available memory: 6217 MB
68 MB
2102 MB
3136 MB
3138 MB
3123 MB
3104 MB
3127 MB Code: using System.Diagnostics;
using System.Runtime.CompilerServices;
var data = new byte[1_000_000_000];
var app = WebApplication
.CreateSlimBuilder(args)
.Build();
Console.WriteLine($"Processor count: {Environment.ProcessorCount}");
Console.WriteLine($"Available memory: {GetAvailableMemory()}");
var downloadApi = app.MapGroup("/download");
downloadApi.MapGet("/", () =>
{
GC.Collect();
Console.WriteLine(GetCurrentMemory());
return Results.File(data, "application/pdf", "test.pdf");
});
await app.RunAsync();
return;
static string GetAvailableMemory()
{
var memoryInfo = GC.GetGCMemoryInfo();
return $"{memoryInfo.TotalAvailableMemoryBytes / 1_000_000} MB";
}
[MethodImpl(MethodImplOptions.NoInlining)]
static string GetCurrentMemory()
{
using var process = Process.GetCurrentProcess();
return $"{process.WorkingSet64 / 1_000_000} MB";
} So, I think:
|
Yes, I still can reproduce it. There is probably some leak because of missing Dispose, but not gigabytes. |
If you replace it with |
There's a number of issues open in dotnet/runtime related to increased memory consumption with containers in .NET 8:
Do any of those correlate with what you're seeing in terms of the runtime environment you use and the work your application typically does? |
So, memory is growing up dramatically because of this:
You see? Starting from some response length (megabytes and more) Kestrel uses memory pool to store this response. The problem is that PinnedBlockMemoryPool never release memory - it can only grows up. As far as I know there is no way to disable this behavior, and Microsoft has been promising for 3 years to fix this 🙁 |
I've made code sample to demonstrate the problem and way to reduce memory consumption: https://github.com/depler/DotnetMemoryTest It uses Harmony (https://github.com/pardeike/Harmony) to override original logic of Test steps are following:
Memory consumption without patch: Memory consumption with patch: So this problem is definitely related with incorrect implementation of |
While this is a problem, the easy way to avoid this is to write to the response in chunks instead of allocating large buffers per request. Though the pinned block memory pool has issues, allocating large buffers on the large object heap per request will result in similar issues. |
I'm seeing this issue when sending small byte array protobuffers. Its a very straight forward grab blob from sqlite and send it to controller.
Controller
|
In a Kubernetes environment, this behavior also disrupts the functionality of the Horizontal Pod Autoscaler. When the load increases, the memory used by the pods rises, prompting Kubernetes to spin up new containers. These new containers also consume more memory. However, when the load decreases, the memory usage of the additional pods does not decrease accordingly, leaving the system in a state with, for example, 20 pods, even if there hasn’t been a single request in the past hour. |
We're looking into addressing this in .NET 10, dont' expect magic though, the examples in this thread will still perform poorly. The solution is often to stream data and avoiding large, reallocated buffers especially on a per request basis. |
Thanks for the fast response @davidfowl! We use gRPC in our ASP.NET Core applications. I think the Protobuf serialization is non-streaming (unlike System.Text.Json's JSON serialization. This is mentioned in #27394). Our scenario includes an Anti-Corruption Layer application which takes gRPC requests from another app (ours), makes the necessary request to a SOAP service (3rd party), receives the SOAP response, fill the gRPC response message, and return it to the caller. Converting our gRPC services into server-streaming from unary may be a solution for our use case. However, it may introduce unnecessary complexity to both our application and the caller application. A simple depiction of the scenario is below. gRPC client app ------------> gRPC server app (ACL, this one has the memory issues) ------------> SOAP web service (3rd party) |
Streaming might not mean a converting to gRPC streams, it might mean breaking up the big payload into smaller chunks. If you are sending large data payloads over GRPC then it might make sense to use http directly instead (multipart file uploads or streams directly). It all depends on the scenario though. gRPC is fairly optimized with how it writes to the underlying response, though the lack of async in the protobuf serializers makes it not ideal for large payloads. @mustafacagataytulun where are the memory issues in your pipeline? |
I believe it but the OP returned a 1 GB buffer from each HTTP request, something that I wouldn't recommend anyone do. It might be possible in your scenario to write better code to avoid the problem. |
Is there an existing issue for this?
Describe the bug
Here is code sample:
I am calling this method via
curl
as following 10 times:curl --insecure https://localhost:5000/api/Tests/Download > NUL
And here is output of the sample:
So memory grows over and over, despite the fact that
Data
is static variable andGC.Collect()
called each time. Please someone explain what is going on? And how to reduce memory consumption?Expected Behavior
Much less memory consumption
Steps To Reproduce
No response
Exceptions (if any)
No response
.NET Version
8.0.204
Anything else?
No response
The text was updated successfully, but these errors were encountered: