Skip to content
Learni
View all tutorials
Développement Backend

Comment optimiser les performances mémoire en C# .NET 8 en 2026

18 minEXPERT

Introduction

La gestion de la mémoire reste un défi critique en 2026 pour les applications C# haute performance. Avec .NET 8, Microsoft a renforcé les outils comme Span et Memory permettant d'éviter les allocations inutiles. Ce tutoriel vous guide pas à pas vers une maîtrise experte de ces concepts. Vous apprendrez à réduire la pression sur le GC et à atteindre des performances proches du natif. Chaque technique est illustrée par du code complet et fonctionnel.

Prérequis

  • .NET 8 SDK installé
  • Connaissances avancées de C# (async/await, generics)
  • Visual Studio 2022 ou Rider
  • Notions de base sur le Garbage Collector

Créer le projet benchmark

terminal
dotnet new console -n MemoryPerf
cd MemoryPerf
dotnet add package BenchmarkDotNet

Initialisation d'un projet console avec BenchmarkDotNet pour mesurer précisément l'impact des optimisations mémoire.

Implémentation Span<T> basique

Program.cs
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class Program {
    public static void Main() => BenchmarkRunner.Run<MemoryBenchmarks>();
}

[MemoryDiagnoser]
public class MemoryBenchmarks {
    private byte[] data = new byte[10000];

    [Benchmark(Baseline = true)]
    public int SumWithArray() {
        int sum = 0;
        for (int i = 0; i < data.Length; i++) sum += data[i];
        return sum;
    }

    [Benchmark]
    public int SumWithSpan() {
        var span = new Span<byte>(data);
        int sum = 0;
        for (int i = 0; i < span.Length; i++) sum += span[i];
        return sum;
    }
}

Ce benchmark compare l'accès via tableau classique et Span. Span évite les allocations supplémentaires et permet des vues sans copie.

Utilisation de Memory<T> pour async

AsyncMemory.cs
using System;
using System.Threading.Tasks;

public class AsyncMemoryExample {
    public async Task<int> ProcessAsync(Memory<byte> buffer) {
        await Task.Delay(10);
        int sum = 0;
        foreach (var b in buffer.Span) sum += b;
        return sum;
    }

    public async Task Run() {
        byte[] data = new byte[5000];
        var result = await ProcessAsync(data);
        Console.WriteLine(result);
    }
}

Memory est utilisable dans les méthodes async contrairement à Span. Il permet de passer des buffers sans allocation tout en restant compatible avec l'asynchronisme.

Stackalloc et ref struct

StackAllocDemo.cs
using System;

public ref struct BufferWrapper {
    public Span<byte> Data { get; }
    public BufferWrapper(Span<byte> data) => Data = data;
}

public class Demo {
    public unsafe void Process() {
        Span<byte> stackBuffer = stackalloc byte[256];
        var wrapper = new BufferWrapper(stackBuffer);
        wrapper.Data[0] = 42;
        Console.WriteLine(wrapper.Data[0]);
    }
}

stackalloc permet d'allouer sur la pile. Combiné à ref struct, il garantit une allocation zéro sur le tas pour les buffers temporaires.

Optimisation avec ArrayPool

ArrayPoolDemo.cs
using System;
using System.Buffers;

public class PoolExample {
    public void RentAndReturn() {
        var pool = ArrayPool<byte>.Shared;
        byte[] buffer = pool.Rent(4096);
        try {
            buffer[0] = 1;
            // traitement
        } finally {
            pool.Return(buffer);
        }
    }
}

ArrayPool réduit les allocations en recyclant les tableaux. Toujours retourner le buffer dans un finally pour éviter les fuites.

Mesure finale avec Benchmark

FinalBench.cs
using BenchmarkDotNet.Attributes;

[MemoryDiagnoser]
public class FinalBench {
    [Benchmark]
    public void OptimizedPath() {
        var pool = ArrayPool<byte>.Shared;
        var buffer = pool.Rent(8192);
        try {
            var span = buffer.AsSpan(0, 8192);
            span.Fill(0xFF);
        } finally {
            pool.Return(buffer);
        }
    }
}

Benchmark final combinant ArrayPool et Span pour valider la réduction drastique des allocations et du temps GC.

Bonnes pratiques

  • Préférez toujours Span pour les traitements synchrones sur des buffers
  • Utilisez ArrayPool pour les buffers réutilisables de taille variable
  • Évitez de capturer des Span dans des closures ou des tâches async
  • Mesurez systématiquement avec BenchmarkDotNet avant optimisation
  • Documentez les hypothèses de taille maximale pour les stackalloc

Erreurs courantes à éviter

  • Utiliser Span dans des méthodes async (compilation error)
  • Oublier de retourner les buffers ArrayPool (fuite mémoire)
  • Stackalloc de tailles trop importantes (stack overflow)
  • Ignorer les conditions de course sur les buffers partagés

Pour aller plus loin

Approfondissez ces techniques avec nos formations expertes C# .NET. Découvrez également les nouveautés .NET 9 sur la gestion mémoire et les unsafe contexts.