-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
BlazorServer doesn't release circuits/component instances after 25mins+forced GC2 #44062
Comments
This seems to happen only with razor components that have events in them. |
@chrdlx I've investigated, and haven't seen any evidence that there's a memory leak or that circuit/component instances fail to get released. The way memory management works inside .NET is more subtle than your repro allows for. For example, you only build up 10 circuits. If it was a leak, you would be able to build up an unlimited number until the process crashes with an out-of-memory error. When I tried following your steps, I do see that at 10 circuits they remain in memory, whereas if I keep refreshing (say, to 100 circuits) then they do get evicted. As an example of the sort of thing that complicates the analysis, one of the things that was rooting the circuit instances when there were only 10 of them was this So in general it doesn't work to do an analysis of the form "do a small amount of things, see that objects are still in memory". A leak is only present if you can build up an unlimited amount of memory pressure until the process eventually fails with an OutOfMemoryException. It's a tricky area and easy to misread what's going on. If you are convinced there is a leak, could you please provide more information about how to consume an unlimited amount of memory? Thanks! |
Yes @SteveSandersonMS , thanks!! I understand what you say, that's why I didn't use the word "leak" specifically, I report it as a bug since that's the only option to report things as far as I can see, I apologize if this wasn't the correct issue category. So, there may not be a problem with this simple repro, the real issue are the implications of this behavior. The implications are that all the class fields your component has will persist in RAM until the internal workings of the framework (like when json serializer) decide it's time to release, and on top of that when GC decides to finally release from memory those references. So if you don't have much data in your component class (like this repro) it's fine, but if you have several hundreds of MBs (like my app), it becomes a problem. This also maybe not problematic in a server situation where you may let .NET Core take all the RAM possible, but not in a workstation scenario like mine. In my case, it's an Blazor server app that runs locally on some client machines (because the app needs access to local files and Windows OS processes), so letting it grow to consume all available RAM until there's no more left to start releasing memory is not acceptable in my scenario. So what did I end up doing then? I set all the component's class fields to |
Also, people may find your explanation useful Steve, because if you read this from @javiercn here
This is what I did and what Javier says it will happen, but it didn't. The reason is your explanation Steve.
Memory may be or may not be released based on further layers of framework logic (json seralizer cache/others/etc) |
Is this behaviour which has arisen in 7.0 because of new STJ caching JsonSerializerOptions? It does seem a bit unexpected that serializer options would become a root for potentially large/numerous application data structures. Whoever carefully used WeakReference<> on that cache to prevent it blocking GC must feel particularly galled that someone's already found a way to trap huge amounts of data via the cache key... :-) |
Thanks for the additional context. I've filed dotnet/runtime#76548. |
You're welcome! Blazor is great! |
Is there an existing issue for this?
Describe the bug
Blazor component instances and circuit objects/instances don't seem to be released from memory after 25+ minutes and forced 2nd generation garbage collection using the basic out of the box Blazor Server template. I tried to follow the recommendations from @javiercn in this post #30210 (comment) to test this.
Expected Behavior
The amount of Counter instances & Circuits should go to zero after 10/20/30 minutes and a forced garbage collection
( GC.Collect(2, GCCollectionMode.Forced);).
Steps To Reproduce
private void IncrementCount() { GC.Collect(2, GCCollectionMode.Forced); }
as recommended by @javiercn here #30210 (comment) so we can force a GC collect after 20 minutes.
and
So you can see there're 11 instances (1 initial request + 10 refreshes) of the component as expected in memory, and the related circuits.
Wait 10 minutes.
Take a new snapshot.
Component instances & circuits still there after 10 minutes.
Wait another 10 minutes (just in case)
Take a new snapshot
We can see a GC has occured.
So nothing has changed, 11 instances are still there after almost an hour, and a forced GC collection.
Just in case I forced GC a couple more times.
Still nothing is released.
Exceptions (if any)
No response
.NET Version
7.0.100-rc.1.22431.12
Anything else?
What I've tried to see if this gets fixed:
Changing the GC mode to Workstation.
Debug & Release modes.
Closed the browser completely (but left the server running).
Nothing seems to make .NET to release Counter component instances & circuits after half an hour.
The text was updated successfully, but these errors were encountered: