Course contentsShow
← C#
Lesson 1776 of 1,90816. Performance and DiagnosticsFree lesson

Reducing Serialization Overhead with System.Text.Json

Learners will profile and optimize binary or text serialization routines for throughput.

In this lesson, you will learn how to optimize System.Text.Json throughput by minimizing allocations and bypassing reflection-based metadata overhead.

The Cost of Reflection

By default, JsonSerializer uses reflection to inspect the properties of your objects at runtime. While convenient, this incurs a "tax" on every serialization cycle. In a high-throughput system, this reflection overhead—coupled with the creation of temporary metadata caches—can lead to increased CPU usage and pressure on the Garbage Collector (GC).

Eliminating Overhead with Source Generators

The most effective way to eliminate this overhead is by using the JsonSerializerContext source generator. By generating the serialization logic at compile-time, you move the heavy lifting from runtime to the build phase. This allows the JIT compiler to produce more efficient machine code and removes the need for runtime type inspection.

Consider this optimized approach:

// Define the context for source generation
[JsonSerializable(typeof(UserRequest))]
internal partial class RequestJsonContext : JsonSerializerContext { }

// Usage: Pass the generated metadata to the serializer
public void Process(UserRequest request, Stream output)
{
    // The serializer now uses the compiled metadata instead of reflection
    JsonSerializer.Serialize(output, request, RequestJsonContext.Default.UserRequest);
}

Avoiding Buffer Allocations

Beyond reflection, excessive allocation of byte[] arrays during serialization often triggers frequent GC cycles. To optimize throughput, you should target IBufferWriter<byte> interfaces or Span<byte> whenever possible.

By leveraging Utf8JsonWriter in conjunction with a pooled ArrayBufferWriter<byte>, you can reuse memory across multiple requests. This minimizes the GC stall times we discussed in earlier lessons, as the serializer writes directly into a rented memory segment rather than allocating a new object on the managed heap. Combined with source generation, these two techniques allow you to keep your hot paths lean, reducing both CPU instruction counts and memory pressure.

Key Takeaway: Replace reflection-based serialization with C# source generators and utilize IBufferWriter<byte> to achieve zero-allocation serialization paths in your high-performance services.