Delen via


Geheugentoewijzingen verminderen met behulp van nieuwe C#-functies

Belangrijk

De technieken die in deze sectie worden beschreven, verbeteren de prestaties wanneer ze worden toegepast op dynamische paden in uw code. Dynamische paden zijn de secties van uw codebasis die vaak en herhaaldelijk worden uitgevoerd in normale bewerkingen. Het toepassen van deze technieken op code die niet vaak wordt uitgevoerd, heeft minimale gevolgen. Voordat u wijzigingen aanbrengt om de prestaties te verbeteren, is het essentieel om een basislijn te meten. Analyseer vervolgens die basislijn om te bepalen waar knelpunten in het geheugen optreden. U vindt meer informatie over veel platformoverschrijdende hulpprogramma's om de prestaties van uw toepassing te meten in de sectie diagnostische gegevens en instrumentatie. In de zelfstudie kunt u oefenen met een profileringssessie om het geheugengebruik te meten in de Visual Studio-documentatie.

Zodra u het geheugengebruik hebt gemeten en hebt vastgesteld dat u toewijzingen kunt verminderen, gebruikt u de technieken in deze sectie om toewijzingen te verminderen. Na elke opeenvolgende wijziging meet u het geheugengebruik opnieuw. Zorg ervoor dat elke wijziging een positieve invloed heeft op het geheugengebruik in uw toepassing.

Prestatiewerkzaamheden in .NET betekenen vaak dat toewijzingen uit uw code worden verwijderd. Elk geheugenblok dat u toewijst, moet uiteindelijk worden vrijgemaakt. Minder toewijzingen verminderen de tijd die wordt besteed aan het verzamelen van afval. Het maakt een voorspelbare uitvoeringstijd mogelijk door garbage collections uit specifieke codepaden te verwijderen.

Een veelvoorkomende tactiek voor het verminderen van toewijzingen is het wijzigen van kritieke gegevensstructuren van class typen in struct typen. Deze wijziging heeft invloed op de semantiek van het gebruik van deze typen. Parameters en resultaten worden nu door waarde doorgegeven in plaats van per verwijzing. De kosten voor het kopiëren van een waarde zijn te verwaarlozen als de typen klein zijn, drie woorden of minder, waarbij wordt uitgegaan van een woord dat de natuurlijke grootte heeft van een geheel getal. Het is meetbaar en kan echte invloed hebben op de prestaties voor grotere typen. Om het effect van kopiëren te bestrijden, kunnen ontwikkelaars deze typen ref doorgeven om de beoogde semantiek terug te krijgen.

De C# ref -functies bieden u de mogelijkheid om de gewenste semantiek voor struct typen uit te drukken zonder de algehele bruikbaarheid te beïnvloeden. Vóór deze verbeteringen moesten ontwikkelaars zich richten op unsafe constructies met aanwijzers en onbewerkt geheugen om dezelfde invloed op de prestaties te bereiken. De compiler genereert verifieerbare veilige code voor de nieuwe ref gerelateerde functies. Verifieerbare veilige code betekent dat de compiler mogelijke bufferoverschrijdingen detecteert of toegang heeft tot niet-toegewezen of vrijgemaakt geheugen. De compiler detecteert en voorkomt enkele fouten.

Doorgeven en teruggeven via verwijzing

Variabelen in C# slaan waarden op. In struct typen is de waarde de inhoud van een instantie van het type. In class typen is de waarde een verwijzing naar een blok geheugen waarin een exemplaar van het type wordt opgeslagen. Als u de ref wijzigingsfunctie toevoegt, betekent dit dat de variabele de verwijzing naar de waarde opslaat. In struct typen wijst de referentie naar de opslag die de waarde bevat. In class typen wijst de referentie naar de opslag die de referentie naar het geheugenblok bevat.

In C# worden parameters aan methoden doorgegeven door waarde en retourwaarden worden geretourneerd op waarde. De waarde van het argument wordt doorgegeven aan de methode. De waarde van het retourargument is de retourwaarde.

De ref, in, ref readonly of out modifier geeft aan dat het argument door verwijzing wordt doorgegeven. Er wordt een verwijzing naar de opslaglocatie doorgegeven aan de methode. Toevoegen ref aan de methodehandtekening betekent dat de retourwaarde wordt geretourneerd door verwijzing. Een verwijzing naar de opslaglocatie is de retourwaarde.

U kunt verw-toewijzing ook gebruiken om een variabele te laten verwijzen naar een andere variabele. Een typische toewijzing kopieert de waarde van de rechterkant naar de variabele aan de linkerkant van de toewijzing. Een verw-toewijzing kopieert de geheugenlocatie van de variabele aan de rechterkant naar de variabele aan de linkerkant. De ref oorspronkelijke variabele verwijst nu naar:

int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment

Console.WriteLine(location); // output: 42

sameLocation = 19; // assignment

Console.WriteLine(anInteger); // output: 19

Wanneer u een variabele toewijst , wijzigt u de waarde ervan. Wanneer u een variabele toewijst , wijzigt u waarnaar deze verwijst.

U kunt rechtstreeks werken met de opslag voor waarden met behulp van ref variabelen, passeren door reference en ref-toewijzing. Bereikregels die door de compiler worden afgedwongen, zorgen voor veiligheid bij het rechtstreeks werken met opslag.

De ref readonly en in modifiers geven zowel aan dat het argument met een referentie moet worden doorgegeven, en niet opnieuw toegewezen kan worden binnen de methode. Het verschil is dat ref readonly aangeeft dat de methode de parameter als variabele gebruikt. De methode kan de parameter vastleggen, of de parameter kan worden geretourneerd via een alleen-lezen referentie. In die gevallen moet u de ref readonly modifier gebruiken. Anders biedt de in wijzigingsfunctie meer flexibiliteit. U hoeft de in wijzigingsfunctie niet toe te voegen aan een argument voor een in parameter, zodat u bestaande API-handtekeningen veilig kunt bijwerken met behulp van de in wijzigingsfunctie. De compiler geeft een waarschuwing als u of de ref of de in modifier niet toevoegt aan een argument voor een ref readonly parameter.

Veilige context voor referentie

C# bevat regels voor ref expressies om ervoor te zorgen dat een ref expressie niet kan worden geopend waar de opslag waarnaar wordt verwezen, niet langer geldig is. Bekijk het volgende voorbeeld:

public ref int CantEscape()
{
    int index = 42;
    return ref index; // Error: index's ref safe context is the body of CantEscape
}

De compiler rapporteert een fout omdat u vanuit een methode geen verwijzing naar een lokale variabele kunt retourneren. De aanroeper heeft geen toegang tot de opslag waarnaar wordt verwezen. De context voor ref safe definieert het bereik waarin een ref expressie veilig is voor toegang of wijziging. De volgende tabel bevat de veilige contexten voor ref voor variabele typen. ref velden kunnen niet worden gedefinieerd in een class of niet-referentie struct, waardoor deze rijen zich niet in de tabel bevinden.

Verklaring ref safe context
niet-verwijzingslokaal het blok waarin de variabele 'local' wordt gedeclareerd
niet-referentieparameter huidige methode
ref, parameter ref readonlyin aanroepmethode
out parameter huidige methode
class veld aanroepmethode
niet-verwijzingsveld struct huidige methode
ref veld van ref struct aanroepmethode

Een variabele kan worden ref geretourneerd als de ref veilige context de aanroepmethode is. Als de veilige context van de ref de huidige methode of een blok is, ref is return niet toegestaan. In het volgende codefragment ziet u twee voorbeelden. Een lidveld kan worden geopend vanuit het bereik dat een methode aanroept, dus de veilige context van een klasse- of structveld is de aanroepmethode. De ref-veilige context voor een parameter met de ref of in modifiers is de hele methode. Beide kunnen worden ref geretourneerd vanuit een lidmethode:

private int anIndex;

public ref int RetrieveIndexRef()
{
    return ref anIndex;
}

public ref int RefMin(ref int left, ref int right)
{
    if (left < right)
        return ref left;
    else
        return ref right;
}

Opmerking

Wanneer de ref readonly of in modifier wordt toegepast op een parameter, kan die parameter worden geretourneerd door ref readonly, niet ref.

De compiler zorgt ervoor dat een verwijzing niet kan ontsnappen aan de veilige context van de ref. U kunt ref parameters, ref return, en ref lokale variabelen veilig gebruiken omdat de compiler detecteert of u per ongeluk code hebt geschreven waarin een ref uitdrukking kan worden geopend wanneer de opslag niet geldig is.

Veilige context en referentiestructuren

ref struct voor typen zijn meer regels vereist om ervoor te zorgen dat ze veilig kunnen worden gebruikt. Een ref struct type kan velden bevatten ref . Hiervoor is een veilige context vereist. Voor de meeste typen is de veilige context de aanroepmethode. Met andere woorden, een waarde die niet een ref struct waarde is, kan altijd worden geretourneerd vanuit een methode.

Informeel is de veilige context voor een ref struct de scope waar alle ref velden toegankelijk zijn. Met andere woorden, het is het kruispunt van de referentieve veilige context van al zijn ref velden. De volgende methode retourneert een ReadOnlySpan<char> naar een lidveld, waardoor de veilige context de methode is.

private string longMessage = "This is a long message";

public ReadOnlySpan<char> Safe()
{
    var span = longMessage.AsSpan();
    return span;
}

De volgende code verzendt daarentegen een fout omdat het ref field lid van de Span<int> code verwijst naar de toegewezen stackmatrix met gehele getallen. Er is geen ontsnappen aan de methode

public Span<int> M()
{
    int length = 3;
    Span<int> numbers = stackalloc int[length];
    for (var i = 0; i < length; i++)
    {
        numbers[i] = i;
    }
    return numbers; // Error! numbers can't escape this method.
}

Geheugentypen samenvoegen

De introductie van System.Span<T> en System.Memory<T> biedt een geïntegreerd model voor het werken met geheugen. System.ReadOnlySpan<T> en System.ReadOnlyMemory<T> bieden leesversies voor toegang tot geheugen. Ze bieden allemaal een abstractie over een blok geheugen dat een matrix met vergelijkbare elementen opslaat. Het verschil is dat Span<T> en ReadOnlySpan<T> zijn ref struct typen terwijl Memory<T> en ReadOnlyMemory<T> zijn struct typen. Span-elementen bevatten een ref field. Daarom kunnen exemplaren van een span de veilige context niet verlaten. De veilige context van een ref struct is de ref veilige context van zijn ref field. De implementatie van Memory<T> en ReadOnlyMemory<T> verwijder deze beperking. U gebruikt deze typen om rechtstreeks toegang te krijgen tot geheugenbuffers.

Prestaties verbeteren met refveiligheid

Als u deze functies gebruikt om de prestaties te verbeteren, worden de volgende taken uitgevoerd:

  • Vermijd toewijzingen: wanneer u een type wijzigt van een class in een struct, wijzigt u de manier waarop het wordt opgeslagen. Lokale variabelen worden opgeslagen op de stack. Leden worden inline opgeslagen wanneer het containerobject wordt toegewezen. Deze wijziging betekent minder toewijzingen en dat vermindert het werk dat de garbagecollector doet. Het kan ook de geheugendruk verminderen, zodat de vuilnisopruimer minder vaak hoeft te draaien.
  • Behoud de verwijzingsemantiek: als u een type wijzigt van een class in een struct variabele, wijzigt u de semantiek van het doorgeven van een variabele aan een methode. Code die de status van de parameters heeft gewijzigd, moet worden gewijzigd. Nu de parameter een structis, wijzigt de methode een kopie van het oorspronkelijke object. U kunt de oorspronkelijke semantiek herstellen door die parameter door te geven als parameter ref . Na deze wijziging wordt de oorspronkelijke struct methode opnieuw gewijzigd.
  • Vermijd het kopiëren van gegevens: het kopiëren van grotere struct typen kan van invloed zijn op de prestaties in sommige codepaden. U kunt ook de ref modifier toevoegen om grotere gegevensstructuren door referentie aan methoden door te geven in plaats van op waarde.
  • Wijzigingen beperken: wanneer een struct type wordt doorgegeven door verwijzing, kan de aangeroepen methode de status van de struct wijzigen. U kunt de ref wijzigingsfunctie vervangen door de ref readonly of in modifiers om aan te geven dat het argument niet kan worden gewijzigd. Geef de voorkeur aan ref readonly wanneer de methode de parameter vastlegt of deze retourneert als een 'readonly' referentie. U kunt ook readonly struct typen of struct typen maken met readonly leden om meer controle te bieden over welke leden van een struct kunnen worden gewijzigd.
  • Geheugen rechtstreeks bewerken: sommige algoritmen zijn het efficiëntst bij het behandelen van gegevensstructuren als een blok geheugen dat een reeks elementen bevat. De Span typen Memory bieden veilige toegang tot blokken geheugen.

Voor geen van deze technieken is code vereist unsafe . U kunt op verstandige wijze prestatiekenmerken verkrijgen van veilige code die voorheen alleen mogelijk was door onveilige technieken te gebruiken. U kunt de technieken zelf proberen in de zelfstudie over het verminderen van geheugentoewijzingen.