Speicherverwaltung und Garbage Collection (GC) in ASP.net CoreMemory management and garbage collection (GC) in ASP.NET Core

Von Sébastien Ros und Rick AndersonBy Sébastien Ros and Rick Anderson

Die Speicherverwaltung ist auch in verwalteten Frameworks wie .net Komplex.Memory management is complex, even in a managed framework like .NET. Das analysieren und verstehen von Speicherproblemen kann eine Herausforderung darstellen.Analyzing and understanding memory issues can be challenging. Dieser Artikel:This article:

  • Wurde von vielen Speicherlecks und GC -Fehlern motiviert.Was motivated by many memory leak and GC not working issues. Die meisten dieser Probleme sind darauf zurückzuführen, dass Sie nicht verstehen, wie die Arbeitsspeicher Nutzung in .net Core funktioniert, oder nicht verstehen, wie Sie gemessen wird.Most of these issues were caused by not understanding how memory consumption works in .NET Core, or not understanding how it's measured.
  • Veranschaulicht die problematische Speicher Verwendung und schlägt alternative Ansätze vor.Demonstrates problematic memory use, and suggests alternative approaches.

Funktionsweise von Garbage Collection (GC) in .net CoreHow garbage collection (GC) works in .NET Core

Der GC ordnet Heap Segmente zu, wobei jedes Segment ein zusammenhängender Speicherbereich ist.The GC allocates heap segments where each segment is a contiguous range of memory. Objekte, die im Heap abgelegt werden, werden in einer von drei Generationen kategorisiert: 0, 1 oder 2.Objects placed in the heap are categorized into one of 3 generations: 0, 1, or 2. Die Generierung bestimmt die Häufigkeit, mit der die GC versucht, Arbeitsspeicher auf verwalteten Objekten freizugeben, auf die nicht mehr von der APP verwiesen wird.The generation determines the frequency the GC attempts to release memory on managed objects that are no longer referenced by the app. Niedrigere nummerierte Generationen werden häufiger als GC gezählt.Lower numbered generations are GC'd more frequently.

Objekte werden basierend auf Ihrer Lebensdauer von einer Generation zu einer anderen verschoben.Objects are moved from one generation to another based on their lifetime. Wenn Objekte länger leben, werden Sie in eine höhere Generation verschoben.As objects live longer, they are moved into a higher generation. Wie bereits erwähnt, sind höhere Generationen seltener.As mentioned previously, higher generations are GC'd less often. Kurzlebige Objekte bleiben immer in Generation 0.Short term lived objects always remain in generation 0. Beispielsweise sind Objekte, auf die während der Lebensdauer einer Webanforderung verwiesen wird, kurzlebig.For example, objects that are referenced during the life of a web request are short lived. Singletons auf Anwendungsebene werden in der Regel zu Generation 2 migriert.Application level singletons generally migrate to generation 2.

Wenn eine ASP.net Core-App gestartet wird, wird der GC:When an ASP.NET Core app starts, the GC:

  • Reserviert Speicher für die anfänglichen Heap Segmente.Reserves some memory for the initial heap segments.
  • Führt einen Commit für einen kleinen Teil des Speichers aus, wenn die Laufzeit geladen wird.Commits a small portion of memory when the runtime is loaded.

Die vorhergehenden Speicher Belegungen werden aus Leistungsgründen ausgeführt.The preceding memory allocations are done for performance reasons. Der Leistungsvorteil ergibt sich aus Heap Segmenten im zusammenhängenden Arbeitsspeicher.The performance benefit comes from heap segments in contiguous memory.

Ruft GC auf. SammCall GC.Collect

GC wird aufgerufen . Explizit erfassen :Calling GC.Collect explicitly:

  • Sollte nicht von Produktions ASP.net Core-apps ausgeführt werden.Should not be done by production ASP.NET Core apps.
  • Ist nützlich bei der Untersuchung von Speicher Verlusten.Is useful when investigating memory leaks.
  • Bei der Untersuchung überprüft, ob der GC alle verbleibenden Objekte aus dem Arbeitsspeicher entfernt hat, damit der Arbeitsspeicher gemessen werden kann.When investigating, verifies the GC has removed all dangling objects from memory so memory can be measured.

Analysieren der Speicherauslastung einer APPAnalyzing the memory usage of an app

Dedizierte Tools können bei der Analyse der Speicherauslastung helfen:Dedicated tools can help analyzing memory usage:

  • Zählen von Objekt verweisenCounting object references
  • Messen der Auswirkung der GC auf die CPU-Auslastung durch den GCMeasuring how much impact the GC has on CPU usage
  • Messen des für jede Generation verwendeten SpeicherplatzesMeasuring memory space used for each generation

Verwenden Sie die folgenden Tools, um die Speicherauslastung zu analysieren:Use the following tools to analyze memory usage:

Erkennen von SpeicherproblemenDetecting memory issues

Der Task-Manager kann verwendet werden, um eine Vorstellung davon zu erhalten, wie viel Arbeitsspeicher eine ASP.net-App verwendet.Task Manager can be used to get an idea of how much memory an ASP.NET app is using. Der Arbeitsspeicher Wert des Task-Managers:The Task Manager memory value:

  • Stellt die Menge an Arbeitsspeicher dar, die vom ASP.NET-Prozess verwendet wird.Represents the amount of memory that is used by the ASP.NET process.
  • Enthält die Living-Objekte der APP und andere Arbeitsspeicherconsumer, z. b. systemeigene Speicherauslastung.Includes the app's living objects and other memory consumers such as native memory usage.

Wenn der Arbeitsspeicher Wert des Task-Managers unbegrenzt zunimmt und nie vereinfacht wird, weist die APP einen Speicher Verlust auf.If the Task Manager memory value increases indefinitely and never flattens out, the app has a memory leak. In den folgenden Abschnitten werden verschiedene Speicher Verwendungs Muster veranschaulicht und erläutert.The following sections demonstrate and explain several memory usage patterns.

Beispiel für die Anzeige der Arbeitsspeicher AuslastungSample display memory usage app

Die Memoryleak-Beispiel-App ist auf GitHub verfügbar.The MemoryLeak sample app is available on GitHub. Die memoryleck-App:The MemoryLeak app:

  • Enthält einen Diagnose Controller, der Echt Zeit Speicher-und GC-Daten für die APP sammelt.Includes a diagnostic controller that gathers real-time memory and GC data for the app.
  • Verfügt über eine Index Seite, auf der die Speicher-und GC-Daten angezeigt werden.Has an Index page that displays the memory and GC data. Die Index Seite wird jede Sekunde aktualisiert.The Index page is refreshed every second.
  • Enthält einen API-Controller, der verschiedene Speicher Auslastungs Muster bereitstellt.Contains an API controller that provides various memory load patterns.
  • Ist kein unterstütztes Tool, kann jedoch verwendet werden, um Speicher Auslastungs Muster von ASP.net Core-apps anzuzeigen.Is not a supported tool, however, it can be used to display memory usage patterns of ASP.NET Core apps.

Führen Sie memoryleck aus.Run MemoryLeak. Der zugewiesene Arbeitsspeicher steigt langsam, bis eine GC auftritt.Allocated memory slowly increases until a GC occurs. Der Arbeitsspeicher nimmt zu, da das Tool benutzerdefiniertes Objekt zum Erfassen von Daten zuordnetMemory increases because the tool allocates custom object to capture data. Die folgende Abbildung zeigt die Index Seite memoryleck, wenn eine Generation 0-GC auftritt.The following image shows the MemoryLeak Index page when a Gen 0 GC occurs. Das Diagramm zeigt 0 RPS (Anforderungen pro Sekunde), da keine API-Endpunkte vom API-Controller aufgerufen wurden.The chart shows 0 RPS (Requests per second) because no API endpoints from the API controller have been called.

Vorheriges Diagramm

Im Diagramm werden zwei Werte für die Speicherauslastung angezeigt:The chart displays two values for the memory usage:

  • Reserviert: die Menge an Arbeitsspeicher, die von verwalteten Objekten belegt wird.Allocated: the amount of memory occupied by managed objects
  • Workingset: der Satz von Seiten im virtuellen Adressraum des Prozesses, der sich zurzeit im physischen Speicher befinden.Working set: The set of pages in the virtual address space of the process that are currently resident in physical memory. Das angezeigte Workingset ist derselbe Wert, den Task-Manager anzeigt.The working set shown is the same value Task Manager displays.

Vorübergehende ObjekteTransient objects

Die folgende API erstellt eine 10-KB-Zeichen folgen Instanz und gibt Sie an den Client zurück.The following API creates a 10-KB String instance and returns it to the client. Bei jeder Anforderung wird ein neues-Objekt im Arbeitsspeicher zugeordnet und in die Antwort geschrieben.On each request, a new object is allocated in memory and written to the response. Zeichen folgen werden als UTF-16-Zeichen in .net gespeichert, sodass jedes Zeichen 2 Bytes im Arbeitsspeicher benötigt.Strings are stored as UTF-16 characters in .NET so each character takes 2 bytes in memory.

[HttpGet("bigstring")]
public ActionResult<string> GetBigString()
{
    return new String('x', 10 * 1024);
}

Das folgende Diagramm wird mit einer relativ kleinen Auslastung in generiert, um anzuzeigen, wie die Speicher Belegungen durch die GC beeinträchtigt werden.The following graph is generated with a relatively small load in to show how memory allocations are impacted by the GC.

Vorheriges Diagramm

Das vorangehende Diagramm zeigt Folgendes:The preceding chart shows:

  • 4K RPS (Anforderungen pro Sekunde).4K RPS (Requests per second).
  • Garbage Collections der Generation 0 erfolgen ungefähr alle zwei Sekunden.Generation 0 GC collections occur about every two seconds.
  • Die Workingsets sind konstant bei ungefähr 500 MB.The working set is constant at approximately 500 MB.
  • CPU ist 12%.CPU is 12%.
  • Die Arbeitsspeicher Nutzung und-Freigabe (über GC) ist stabil.The memory consumption and release (through GC) is stable.

Das folgende Diagramm wird mit dem maximalen Durchsatz erstellt, der vom Computer verarbeitet werden kann.The following chart is taken at the max throughput that can be handled by the machine.

Vorheriges Diagramm

Das vorangehende Diagramm zeigt Folgendes:The preceding chart shows:

  • 22K RPS22K RPS
  • Garbage Collections der Generation 0 sind mehrmals pro Sekunde aufgetreten.Generation 0 GC collections occur several times per second.
  • Sammlungen der Generation 1 werden ausgelöst, da die APP bedeutend mehr Arbeitsspeicher pro Sekunde zugewiesen hat.Generation 1 collections are triggered because the app allocated significantly more memory per second.
  • Die Workingsets sind konstant bei ungefähr 500 MB.The working set is constant at approximately 500 MB.
  • Die CPU-Auslastung beträgt 33%.CPU is 33%.
  • Die Arbeitsspeicher Nutzung und-Freigabe (über GC) ist stabil.The memory consumption and release (through GC) is stable.
  • CPU (33%) ist nicht über ausgelastet, daher kann der Garbage Collection mit einer hohen Anzahl von Zuordnungen mithalten.The CPU (33%) is not over-utilized, therefore the garbage collection can keep up with a high number of allocations.

Arbeitsstation GC im Vergleich zu Server GCWorkstation GC vs. Server GC

Der .NET Garbage Collector verfügt über zwei verschiedene Modi:The .NET Garbage Collector has two different modes:

  • Arbeitsstation GC: für den Desktop optimiert.Workstation GC: Optimized for the desktop.
  • Server-GCServer GC. Der standardmäßige GC für ASP.net Core-apps.The default GC for ASP.NET Core apps. Für den Server optimiert.Optimized for the server.

Der GC-Modus kann explizit in der Projektdatei oder in der runtimeconfig.js Datei der veröffentlichten App festgelegt werden.The GC mode can be set explicitly in the project file or in the runtimeconfig.json file of the published app. Das folgende Markup zeigt die Einstellung ServerGarbageCollection in der Projektdatei:The following markup shows setting ServerGarbageCollection in the project file:

<PropertyGroup>
  <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>

Zum Ändern der ServerGarbageCollection Projektdatei muss die APP neu erstellt werden.Changing ServerGarbageCollection in the project file requires the app to be rebuilt.

Hinweis: Server Garbage Collection ist auf Computern mit einem einzelnen Kern nicht verfügbar.Note: Server garbage collection is not available on machines with a single core. Weitere Informationen finden Sie unter IsServerGC.For more information, see IsServerGC.

In der folgenden Abbildung ist das Arbeitsspeicher Profil unter einem 5K RPS mithilfe der Arbeitsstations-GC dargestellt.The following image shows the memory profile under a 5K RPS using the Workstation GC.

Vorheriges Diagramm

Die Unterschiede zwischen diesem Diagramm und der Server Version sind von Bedeutung:The differences between this chart and the server version are significant:

  • Das Workingset sinkt von 500 MB auf 70 MB.The working set drops from 500 MB to 70 MB.
  • Der GC führt die Garbage Collection der Generation 0 mehrmals pro Sekunde statt alle zwei Sekunden aus.The GC does generation 0 collections multiple times per second instead of every two seconds.
  • GC fällt von 300 MB auf 10 MB.GC drops from 300 MB to 10 MB.

In einer typischen Webserver Umgebung ist die CPU-Auslastung wichtiger als der Arbeitsspeicher, daher ist die Server-GC besser geeignet.On a typical web server environment, CPU usage is more important than memory, therefore the Server GC is better. Wenn die Arbeitsspeicher Auslastung hoch ist und die CPU-Auslastung relativ gering ist, kann die GC der Arbeitsstation leistungsfähiger werden.If memory utilization is high and CPU usage is relatively low, the Workstation GC might be more performant. Beispielsweise eine hohe Dichte, in der mehrere Web-Apps gehostet werden, bei denen Arbeitsspeicher knappFor example, high density hosting several web apps where memory is scarce.

GC mittels Docker und kleiner ContainersGC using Docker and small containers

Wenn mehrere Container-apps auf einem Computer ausgeführt werden, ist die Arbeitsstations-GC möglicherweise höher als die Server-GC.When multiple containerized apps are running on one machine, Workstation GC might be more preformant than Server GC. Weitere Informationen finden Sie unter Running with Server GC in a Small Container und Running with Server GC in a Small Container Scenario Part 1 – Hard Limit for the GC Heap.For more information, see Running with Server GC in a Small Container and Running with Server GC in a Small Container Scenario Part 1 – Hard Limit for the GC Heap.

Persistente Objekt VerweisePersistent object references

Der GC kann keine Objekte freigeben, auf die verwiesen wird.The GC cannot free objects that are referenced. Objekte, auf die verwiesen wird, die jedoch nicht mehr benötigt werden, führen zu einem Speicherplatz.Objects that are referenced but no longer needed result in a memory leak. Wenn die APP Objekte häufig anordnet und Sie nicht mehr freigibt, wenn Sie nicht mehr benötigt werden, erhöht sich die Speicherauslastung im Laufe der Zeit.If the app frequently allocates objects and fails to free them after they are no longer needed, memory usage will increase over time.

Die folgende API erstellt eine 10-KB-Zeichen folgen Instanz und gibt Sie an den Client zurück.The following API creates a 10-KB String instance and returns it to the client. Der Unterschied zum vorherigen Beispiel besteht darin, dass auf diese Instanz von einem statischen Member verwiesen wird. Dies bedeutet, dass Sie nie für die Auflistung verfügbar ist.The difference with the previous example is that this instance is referenced by a static member, which means it's never available for collection.

private static ConcurrentBag<string> _staticStrings = new ConcurrentBag<string>();

[HttpGet("staticstring")]
public ActionResult<string> GetStaticString()
{
    var bigString = new String('x', 10 * 1024);
    _staticStrings.Add(bigString);
    return bigString;
}

Der vorangehende Code:The preceding code:

  • Ist ein Beispiel für einen typischen Speicherplatz.Is an example of a typical memory leak.
  • Bei häufigen aufrufen bewirkt, dass der APP-Speicher zunimmt, bis der Prozess mit einer Ausnahme abstürzt OutOfMemory .With frequent calls, causes app memory to increases until the process crashes with an OutOfMemory exception.

Vorheriges Diagramm

In der vorangehenden Abbildung:In the preceding image:

  • Auslastungs Tests der /api/staticstring Endpunkt bewirkt eine lineare Zunahme des Speichers.Load testing the /api/staticstring endpoint causes a linear increase in memory.
  • Der GC versucht, Arbeitsspeicher freizugeben, wenn die Arbeitsspeicher Auslastung zunimmt, indem eine Sammlung der Generation 2 aufgerufen wird.The GC tries to free memory as the memory pressure grows, by calling a generation 2 collection.
  • Der GC kann den kompromittierten Speicher nicht freigeben.The GC cannot free the leaked memory. Der zugewiesene und Workingset erhöhen sich mit der Zeit.Allocated and working set increase with time.

Einige Szenarien, wie z. b. Caching, erfordern, dass Objekt Verweise aufrechterhalten werden, bis die Arbeitsspeicher Auslastung die Freigabe aufgehoben.Some scenarios, such as caching, require object references to be held until memory pressure forces them to be released. Die- WeakReference Klasse kann für diese Art von Cache Code verwendet werden.The WeakReference class can be used for this type of caching code. Ein- WeakReference Objekt wird unter Arbeitsspeicher Druck erfasst.A WeakReference object is collected under memory pressures. Die Standard Implementierung von IMemoryCache verwendet WeakReference .The default implementation of IMemoryCache uses WeakReference.

Nativer SpeicherNative memory

Einige .net Core-Objekte basieren auf System eigenem Arbeitsspeicher.Some .NET Core objects rely on native memory. Der Native Arbeitsspeicher kann nicht vom GC gesammelt werden.Native memory can not be collected by the GC. Das .NET-Objekt, das den nativen Speicher verwendet, muss es mit System eigenem Code freigebenThe .NET object using native memory must free it using native code.

.NET stellt die- IDisposable Schnittstelle bereit, damit Entwickler systemeigenen Speicher freigeben können..NET provides the IDisposable interface to let developers release native memory. Auch wenn Dispose nicht aufgerufen wird, wird von ordnungsgemäß implementierten Klassen aufgerufen, Dispose Wenn der Finalizer ausgeführt wird.Even if Dispose is not called, correctly implemented classes call Dispose when the finalizer runs.

Betrachten Sie folgenden Code:Consider the following code:

[HttpGet("fileprovider")]
public void GetFileProvider()
{
    var fp = new PhysicalFileProvider(TempPath);
    fp.Watch("*.*");
}

Physicalfileprovider ist eine verwaltete Klasse, daher wird jede Instanz am Ende der Anforderung gesammelt.PhysicalFileProvider is a managed class, so any instance will be collected at the end of the request.

In der folgenden Abbildung wird das Speicher Profil angezeigt, während die API fortlaufend aufgerufen wird fileprovider .The following image shows the memory profile while invoking the fileprovider API continuously.

Vorheriges Diagramm

Das vorangehende Diagramm zeigt ein offensichtliches Problem mit der Implementierung dieser Klasse, da die Speicherauslastung weiterhin zunimmt.The preceding chart shows an obvious issue with the implementation of this class, as it keeps increasing memory usage. Dies ist ein bekanntes Problem, das in diesem Problemnachverfolgt wird.This is a known problem that is being tracked in this issue.

Der gleiche Fehler kann im Benutzercode auftreten, indem eine der folgenden Aktionen durchgeführt wird:The same leak could be happen in user code, by one of the following:

  • Die Klasse wird nicht ordnungsgemäß freigegeben.Not releasing the class correctly.
  • Das Aufrufen der- Dispose Methode der abhängigen Objekte, die verworfen werden sollen, wird vergessen.Forgetting to invoke the Disposemethod of the dependent objects that should be disposed.

Heap für große ObjekteLarge objects heap

Häufige Speicher Belegungen/freie Zyklen können den Arbeitsspeicher fragmentieren, insbesondere bei der Zuordnung von großen Arbeitsspeicher Blöcken.Frequent memory allocation/free cycles can fragment memory, especially when allocating large chunks of memory. Objekte werden in zusammenhängenden Speicherblöcken zugeordnet.Objects are allocated in contiguous blocks of memory. Um die Fragmentierung zu minimieren, wird das Defragmentieren, wenn der GC Speicher freigibt, defragmentiert.To mitigate fragmentation, when the GC frees memory, it trys to defragment it. Dieser Vorgang wird als " Komprimierung" bezeichnet.This process is called compaction. Die Komprimierung umfasst das Verschieben von Objekten.Compaction involves moving objects. Das Verschieben von großen Objekten führt zu einer Leistungs Einbuße.Moving large objects imposes a performance penalty. Aus diesem Grund erstellt der GC eine spezielle Speicher Zone für große Objekte ( Large Object Heap , Loh).For this reason the GC creates a special memory zone for large objects, called the large object heap (LOH). Objekte, die größer als 85.000 Byte sind (etwa 83 KB):Objects that are greater than 85,000 bytes (approximately 83 KB) are:

  • Auf dem Loh platziert.Placed on the LOH.
  • Nicht komprimiert.Not compacted.
  • Wird während der Generation 2-GCS gesammelt.Collected during generation 2 GCs.

Wenn der Loh voll ist, löst der GC eine Sammlung der Generation 2 aus.When the LOH is full, the GC will trigger a generation 2 collection. Sammlungen der Generation 2:Generation 2 collections:

  • Sind grundsätzlich langsam.Are inherently slow.
  • Zusätzlich entstehen die Kosten für das Auslösen einer Sammlung für alle anderen Generationen.Additionally incur the cost of triggering a collection on all other generations.

Der folgende Code komprimiert den Loh sofort:The following code compacts the LOH immediately:

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

LargeObjectHeapCompactionModeInformationen zur Komprimierung des Loh finden Sie unter.See LargeObjectHeapCompactionMode for information on compacting the LOH.

In Containern, die .net Core 3,0 und höher verwenden, wird der Loh automatisch komprimiert.In containers using .NET Core 3.0 and later, the LOH is automatically compacted.

Die folgende API veranschaulicht dieses Verhalten:The following API that illustrates this behavior:

[HttpGet("loh/{size=85000}")]
public int GetLOH1(int size)
{
   return new byte[size].Length;
}

Das folgende Diagramm zeigt das Speicher Profil des aufrufenden /api/loh/84975 Endpunkts unter maximale Auslastung:The following chart shows the memory profile of calling the /api/loh/84975 endpoint, under maximum load:

Vorheriges Diagramm

Das folgende Diagramm zeigt das Speicher Profil für den Aufruf des /api/loh/84976 Endpunkts, wobei nur noch ein Bytezugeordnet wird:The following chart shows the memory profile of calling the /api/loh/84976 endpoint, allocating just one more byte:

Vorheriges Diagramm

Hinweis: die byte[] Struktur hat Overhead-bytes.Note: The byte[] structure has overhead bytes. Aus diesem Grund wird das 85.000-Limit von 84.976 bytes ausgelöst.That's why 84,976 bytes triggers the 85,000 limit.

Vergleichen der beiden vorangehenden Diagramme:Comparing the two preceding charts:

  • Das Workingset ist für beide Szenarien ähnlich, etwa 450 MB.The working set is similar for both scenarios, about 450 MB.
  • Die unter Loh-Anforderungen (84.975 Bytes) zeigen überwiegend Auflistungen der Generation 0.The under LOH requests (84,975 bytes) shows mostly generation 0 collections.
  • Die over Loh-Anforderungen generieren Konstante Generation 2-Auflistungen.The over LOH requests generate constant generation 2 collections. Sammlungen der Generation 2 sind aufwendig.Generation 2 collections are expensive. Es ist mehr CPU erforderlich, und der Durchsatz sinkt fast 50%.More CPU is required and throughput drops almost 50%.

Temporäre große Objekte sind besonders problematisch, da Sie Gen2-GCS verursachen.Temporary large objects are particularly problematic because they cause gen2 GCs.

Um die maximale Leistung zu erzielen, sollte die Verwendung von großen Objekten minimiert werden.For maximum performance, large object use should be minimized. Teilen Sie nach Möglichkeit große Objekte auf.If possible, split up large objects. Beispielsweise wird durch die Middleware zum zwischen Speichern von Antworten in ASP.net Core die Cache Einträge in Blöcke aufgeteilt, die kleiner als 85.000 Bytes sind.For example, Response Caching middleware in ASP.NET Core split the cache entries into blocks less than 85,000 bytes.

Die folgenden Links zeigen die ASP.net Core Methode zum Beibehalten von Objekten unter dem Loh-Grenzwert:The following links show the ASP.NET Core approach to keeping objects under the LOH limit:

Weitere Informationen finden Sie unter:For more information, see:

HttpClientHttpClient

Die falsche Verwendung von HttpClient kann zu einem Ressourcen Fehler führen.Incorrectly using HttpClient can result in a resource leak. System Ressourcen, z. b. Datenbankverbindungen, Sockets, Datei Handles usw.:System resources, such as database connections, sockets, file handles, etc.:

  • Sind knapper als der Arbeitsspeicher.Are more scarce than memory.
  • Sind schwieriger, wenn es zu einem kompromittierten als ArbeitsspeicherAre more problematic when leaked than memory.

Erfahrene .NET-Entwickler wissen, dass für Objekte aufgerufen werden muss Dispose , die implementieren IDisposable .Experienced .NET developers know to call Dispose on objects that implement IDisposable. Das Verwerfen von Objekten, die implementieren, IDisposable führt in der Regel zu einem kompromittierten oder kompromittierten Systemressourcen.Not disposing objects that implement IDisposable typically results in leaked memory or leaked system resources.

HttpClientimplementiert IDisposable , sollte jedoch bei jedem Aufruf nicht verworfen werden.HttpClient implements IDisposable, but should not be disposed on every invocation. Stattdessen HttpClient sollte wieder verwendet werden.Rather, HttpClient should be reused.

Der folgende Endpunkt erstellt eine neue-Instanz und gibt diese HttpClient bei jeder Anforderung aus:The following endpoint creates and disposes a new HttpClient instance on every request:

[HttpGet("httpclient1")]
public async Task<int> GetHttpClient1(string url)
{
    using (var httpClient = new HttpClient())
    {
        var result = await httpClient.GetAsync(url);
        return (int)result.StatusCode;
    }
}

Unter Last werden die folgenden Fehlermeldungen protokolliert:Under load, the following error messages are logged:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HLG70PBE1CR1", Request id "0HLG70PBE1CR1:00000031":
      An unhandled exception was thrown by the application.
System.Net.Http.HttpRequestException: Only one usage of each socket address
    (protocol/network address/port) is normally permitted --->
    System.Net.Sockets.SocketException: Only one usage of each socket address
    (protocol/network address/port) is normally permitted
   at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port,
    CancellationToken cancellationToken)

Obwohl die HttpClient Instanzen verworfen werden, benötigt die tatsächliche Netzwerkverbindung eine gewisse Zeit, bis Sie vom Betriebssystem freigegeben wird.Even though the HttpClient instances are disposed, the actual network connection takes some time to be released by the operating system. Durch das fortlaufende Erstellen neuer Verbindungen erfolgt die Auslastung der Ports .By continuously creating new connections, ports exhaustion occurs. Jede Client Verbindung erfordert einen eigenen ClientPort.Each client connection requires its own client port.

Eine Möglichkeit, die Port Auslastung zu verhindern, ist die Wiederverwendung derselben HttpClient Instanz:One way to prevent port exhaustion is to reuse the same HttpClient instance:

private static readonly HttpClient _httpClient = new HttpClient();

[HttpGet("httpclient2")]
public async Task<int> GetHttpClient2(string url)
{
    var result = await _httpClient.GetAsync(url);
    return (int)result.StatusCode;
}

Die- HttpClient Instanz wird freigegeben, wenn die APP angehalten wird.The HttpClient instance is released when the app stops. Dieses Beispiel zeigt, dass nicht jede verwerfbare Ressource nach jeder Verwendung verworfen werden sollte.This example shows that not every disposable resource should be disposed after each use.

Im folgenden finden Sie eine bessere Möglichkeit zur Handhabung der Lebensdauer einer HttpClient Instanz:See the following for a better way to handle the lifetime of an HttpClient instance:

Objekt PoolingObject pooling

Im vorherigen Beispiel wurde gezeigt, wie die HttpClient -Instanz statisch gemacht und von allen Anforderungen wieder verwendet werden kann.The previous example showed how the HttpClient instance can be made static and reused by all requests. Durch die Wiederverwendung wird verhindert, dass Ressourcen aussteht.Reuse prevents running out of resources.

Objekt Pooling:Object pooling:

  • Verwendet das Verwendungs Muster.Uses the reuse pattern.
  • Ist für Objekte konzipiert, die teuer zu erstellen sind.Is designed for objects that are expensive to create.

Ein Pool ist eine Sammlung von vorinitialisierten Objekten, die über mehrere Threads reserviert und freigegeben werden können.A pool is a collection of pre-initialized objects that can be reserved and released across threads. Pools können Zuordnungs Regeln definieren, z. b. Grenzwerte, vordefinierte Größen oder Wachstumsrate.Pools can define allocation rules such as limits, predefined sizes, or growth rate.

Das nuget-Paket Microsoft. Extensions. Objectpool enthält Klassen, die die Verwaltung solcher Pools erleichtern.The NuGet package Microsoft.Extensions.ObjectPool contains classes that help to manage such pools.

Der folgende API-Endpunkt instanziiert einen byte Puffer, der bei jeder Anforderung mit Zufallszahlen aufgefüllt wird:The following API endpoint instantiates a byte buffer that is filled with random numbers on each request:

        [HttpGet("array/{size}")]
        public byte[] GetArray(int size)
        {
            var random = new Random();
            var array = new byte[size];
            random.NextBytes(array);

            return array;
        }

Im folgenden Diagramm wird der Aufruf der vorangehenden API mit mittlerer Auslastung angezeigt:The following chart display calling the preceding API with moderate load:

Vorheriges Diagramm

Im vorangehenden Diagramm erfolgen die Sammlungen der Generation 0 ungefähr einmal pro Sekunde.In the preceding chart, generation 0 collections happen approximately once per second.

Der vorangehende Code kann optimiert werden, indem der byte Puffer mithilfe von arraypool <T> gebündelt wird.The preceding code can be optimized by pooling the byte buffer by using ArrayPool<T>. Eine statische-Instanz wird in allen Anforderungen wieder verwendet.A static instance is reused across requests.

Unterscheidet sich bei diesem Ansatz, dass ein in einem Pool zusammengefasste Objekt von der API zurückgegeben wird.What's different with this approach is that a pooled object is returned from the API. Dies bedeutet Folgendes:That means:

  • Das-Objekt befindet sich außerhalb des-Steuer Elements, sobald Sie von der-Methode zurückkehren.The object is out of your control as soon as you return from the method.
  • Das Objekt kann nicht freigegeben werden.You can't release the object.

So richten Sie die Entsorgung des-Objekts ein:To set up disposal of the object:

RegisterForDisposeübernimmt das Aufrufen von Dispose für das Zielobjekt, sodass es nur freigegeben wird, wenn die HTTP-Anforderung beendet ist.RegisterForDispose will take care of calling Disposeon the target object so that it's only released when the HTTP request is complete.

private static ArrayPool<byte> _arrayPool = ArrayPool<byte>.Create();

private class PooledArray : IDisposable
{
    public byte[] Array { get; private set; }

    public PooledArray(int size)
    {
        Array = _arrayPool.Rent(size);
    }

    public void Dispose()
    {
        _arrayPool.Return(Array);
    }
}

[HttpGet("pooledarray/{size}")]
public byte[] GetPooledArray(int size)
{
    var pooledArray = new PooledArray(size);

    var random = new Random();
    random.NextBytes(pooledArray.Array);

    HttpContext.Response.RegisterForDispose(pooledArray);

    return pooledArray.Array;
}

Das Anwenden derselben Last wie die nicht in einem Pool zusammengefasste Version führt zu folgendem Diagramm:Applying the same load as the non-pooled version results in the following chart:

Vorheriges Diagramm

Der Hauptunterschied besteht darin, dass Bytes zugeordnet werden, was zu einer wesentlich geringeren Auflistung der Generation 0 gehört.The main difference is allocated bytes, and as a consequence much fewer generation 0 collections.

Zusätzliche RessourcenAdditional resources