Les objets Memory<T> et MemoryPool<T> permettent de manipuler les buffers de données de façon très efficace. Encore trop peu connus et utilisés, faisons le point !
🎯 Objectif
Comprendre en quoi Memory<T> et MemoryPool<T> complètent Span<T>, et comment ils permettent de gérer efficacement la mémoire en .NET dans les cas suivants :
- buffers réutilisables ou longs à vivre
- manipulation asynchrone de données (I/O, réseau…)
- allocations contrôlées ou à faible empreinte mémoire
📍 Rappel : pourquoi Span<T> ne suffit pas toujours ?
Span<T> permet de manipuler une portion contiguë de mémoire de manière sûre et sans allocation. Il est ultra performant, mais :
- il ne peut vivre que sur la stack
- il ne peut pas être capturé dans une closure ou stocké dans une variable asynchrone
Dès qu’on sort du scope immédiat, on a besoin d’autre chose : Memory<T>.
🔍 Memory<T> : la version heap-safe de Span<T>
Définition
Memory<T> représente un bloc de mémoire comme Span<T>, mais stockable, transmissible et compatible async.
Exemple simple
Memory<byte> buffer = new byte[1024];
DoSomethingAsync(buffer);
async Task DoSomethingAsync(Memory<byte> data)
{
await Task.Delay(10); // Autorisé : Memory<T> peut être capturé ici
var span = data.Span;
span[0] = 42;
}
🔧 Memory<T>.Span permet d’obtenir un Span<T> temporaire pour la manipulation directe.
🏊♂️ MemoryPool<T> : réutiliser les buffers sans allouer à chaque fois
Chaque fois que vous écrivez :
var buffer = new byte[4096];
… vous allouez sur le tas. Si vous le faites souvent (par ex. traitement réseau ou fichiers), cela génère beaucoup de garbage.
MemoryPool<T> fournit une stratégie de réutilisation de blocs de mémoire.
Exemple d’utilisation de MemoryPool<T>
var pool = MemoryPool<byte>.Shared;
using var owner = pool.Rent(4096);
Memory<byte> buffer = owner.Memory;
await DoSomethingAsync(buffer); // Transmet un buffer loué
// Pas besoin de libérer : l'objet est retourné au pool à la fin du `using`
Exemple concret : lecture d’un flux réseau ou fichier
async Task ReadStreamAsync(Stream source)
{
var pool = MemoryPool<byte>.Shared;
using var bufferOwner = pool.Rent(4096);
var buffer = bufferOwner.Memory;
int bytesRead = await source.ReadAsync(buffer);
Console.WriteLine($"Lu {bytesRead} octets");
}
✅ Aucun garbage produit, buffer réutilisable, et compatible async.
⚖️ Comparatif rapide
|
Type
|
Vivant sur
|
Capturable async ?
|
Allocation
|
Idéal pour
|
|
Span<T>
|
Stack
|
❌ Non
|
Non
|
Traitement immédiat
|
|
Memory<T>
|
Heap
|
✅ Oui
|
Oui
|
Données longues à vivre
|
|
MemoryPool<T>
|
Heap (réutilisé)
|
✅ Oui
|
Faible
|
I/O performants, buffers récurrents
|
🛠️ Astuces et bonnes pratiques
- Utilisez Span<T> dans les fonctions internes non asynchrones.
- Passez à Memory<T> si vos données doivent traverser un await, une closure, ou être conservées.
- Pour des buffers récurrents, adoptez systématiquement MemoryPool<T>.
🔁 Pour aller plus loin
- IMemoryOwner<T> vous permet de rendre vos API plus flexibles (possibilité de libérer le buffer ou non)
- ReadOnlyMemory<T> est la version immutable de Memory<T> (comme ReadOnlySpan<T>)
- Les nouvelles API .NET (par ex. Stream.ReadAsync) acceptent Memory<T> en standard depuis .NET Core 2.1+
✅ Conclusion
Memory<T> et MemoryPool<T> sont les piliers du traitement efficace de données en .NET moderne. Ils permettent d’écrire du code rapide, sécurisé, compatible async, sans sacrifier la performance mémoire.
Comme Span<T> ces outils d'accès à la mémoire nous éloignent un peu du côté rassurant d'un langage "managé" avec garbage collector pour nous entraîner sur le territoire marécageux de C et C++, des pointeurs, des buffers et autres techniques qu'on croyait d'un autre temps... Mais en réalité ces structures de données sont toujours vivantes et utiles ! C# réussit (grâce aux évolutions de ..NET Core) à rendre leur utilisation naturelle en les intégrant à sa logique propre. Je ne vous dis pas qu'il faut truffer vos Apps désormais de ces techniques d'accès à la mémoire, mais je vous dis, oui elles sont utiles et vivantes et aujourd'hui elles deviennent utilisables dans le contexte d'un langage managé, c'est une arme à garder sous le coude pour certains cas où cela s'avère un avantage indiscutable.
Stay Tuned !