Course contentsShow
← C#
Lesson 1501 of 1,90813. Real-time Communication with SignalRFree lesson

Memory Efficient Object Serialization

Use buffer pools to reduce memory overhead when serializing large real-time payloads.

In this lesson, you will learn how to leverage ArrayPool<byte> to minimize garbage collection pressure when serializing large payloads for real-time SignalR communication.

The Problem: Allocation Bloat

When serializing large objects to byte[] arrays for SignalR delivery, we typically use methods like JsonSerializer.SerializeToUtf8Bytes(). If you send dozens of large payloads per second, you are allocating new byte[] arrays constantly. This creates "short-lived" objects that force the Garbage Collector (GC) to run frequently, leading to performance stutters and increased latency in your real-time Hub.

The Solution: Buffer Pooling

Instead of allocating a new array every time, we borrow a buffer from a shared pool (System.Buffers.ArrayPool<byte>.Shared). This allows the runtime to reuse memory across different serialization operations.

Here is how you implement this pattern in a SignalR Hub:

using System.Buffers;
using System.Text.Json;

public async Task SendLargePayload(LargeData payload)
{
    // Rent a buffer large enough for our payload
    byte[] buffer = ArrayPool<byte>.Shared.Rent(4096); 
    
    try
    {
        // Use a MemoryStream to wrap our rented buffer
        using var ms = new MemoryStream(buffer);
        JsonSerializer.Serialize(ms, payload);
        
        // SignalR can send the slice of the buffer actually used
        await Clients.All.SendAsync("ReceiveData", ms.ToArray());
    }
    finally
    {
        // Return the buffer to the pool so it can be reused
        ArrayPool<byte>.Shared.Return(buffer);
    }
}

Why This Matters

By using ArrayPool, you bypass the "Allocation -> Collection" cycle for your serialization buffers. When you call Return, the memory isn't destroyed; it is marked as available for the next Rent request. In high-concurrency scenarios, this significantly stabilizes the memory footprint of your SignalR server, ensuring that the GC doesn't interrupt your real-time message flow to clean up fragmented heap memory.

Key Takeaway: Use ArrayPool<byte> to reuse memory buffers during serialization to avoid frequent heap allocations and reduce Garbage Collector overhead in high-throughput SignalR hubs.