aspnetcore/blazor/host-and-deploy/server/memory-management.md
This article explains how to manage memory use in deployed ASP.NET Core server-side Blazor apps.
On the server, a new circuit is created for each user session. Each user session corresponds to rendering a single document in the browser. For example, multiple tabs create multiple sessions.
Blazor maintains a constant connection to the browser, called a circuit, that initiated the session. Connections can be lost at any time for any of several reasons, such as when the user loses network connectivity or abruptly closes the browser. When a connection is lost, Blazor has a recovery mechanism that places a limited number of circuits in a "disconnected" pool, giving clients a limited amount of time to reconnect and re-establish the session (default: 3 minutes).
After that time, Blazor releases the circuit and discards the session. From that point on, the circuit is eligible for garbage collection (GC) and is claimed when a collection for the circuit's GC generation is triggered. One important aspect to understand is that circuits have a long lifetime, which means that most of the objects rooted by the circuit eventually reach Gen 2. As a result, you might not see those objects released until a Gen 2 collection happens.
Prerequisites:
We compute the memory used by blazor as follows:
(Active Circuits × Per-circuit Memory) + (Disconnected Circuits × Per-circuit Memory)
The amount of memory a circuit uses and the maximum potential active circuits that an app can maintain is largely dependent on how the app is written. The maximum number of possible active circuits is roughly described by:
Maximum Available Memory / Per-circuit Memory = Maximum Potential Active Circuits
For a memory leak to occur in Blazor, the following must be true:
In other cases, there's no memory leak. If the circuit is active (connected or disconnected), the circuit is still in use.
If a collection for the circuit's GC generation doesn't run, the memory isn't released because the garbage collector doesn't need to free the memory at that time.
If a collection for a GC generation runs and frees the circuit, you must validate the memory against the GC stats, not the process, as .NET might decide to keep the virtual memory active.
If the memory isn't freed, you must find a circuit that isn't either active or disconnected and that's rooted by another object in the framework. In any other case, the inability to free memory is an app issue in developer code.
Adopt any of the following strategies to reduce an app's memory usage:
GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true)) free the memory?When building a Blazor app that runs on the client and targets mobile device browsers, especially Safari on iOS, decreasing the maximum memory for the app with the MSBuild property EmccMaximumHeapSize may be required. For more information, see xref:blazor/host-and-deploy/webassembly/index#decrease-maximum-heap-size-for-some-mobile-device-browsers.
dotnet-counters. For more information see Investigate performance counters (dotnet-counters).dotnet-counters because you'll see the GCs happen and the amount of used memory go down to 0 (zero), but you won't see the working set counter decrease, which is the sign that .NET is holding on to the memory to reuse it. For more information on project file (.csproj) settings to control this behavior, see Runtime configuration options for garbage collection.