Am I creating a leak here?

When you call JsonDocument.Parse it uses pooled arrays to avoid garbage collection pauses in high throughput.

When you Dispose that document it puts the arrays back in the pool, if you lose the reference and it gets garbage collected... it's the same as if it hadn't been disposable at all (slightly worse, since some other aspect of the runtime may suddenly be where things pause and the GC kicks in because the pool has fewer arrays now).

JsonElement has a Clone method (but isn't ICloneable) which returns a copy of the (relevant) data using a JsonDocument instance that doesn't use pooled arrays.

JsonSerializer, when returning JsonElement values, always calls Clone() and then disposes the original JsonDocument. So when you use JsonSerializer and get a JsonElement (directly, or through overflow properties) it's the same as if JsonDocument was built without an ArrayPool optimization... so everything's fine.


From a brief investigation of sources (https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs) it seems that JsonDocument Dispose returns "rented" bytes to shared array pool and does some general cleanup. Some instances of JsonDocument are marked as not disposable and in this case Dispose will not do anything. You can check this flag for your instance using reflection - if your instance doesn't have internal IsDisposable flag set to true there is no need to worry, because Dispose will not do anything anyway.

I think in normal scenario, JsonDocument parser should clean after itself and there should be no rented bytes left or internal data after parser is done.

It's always safe to not rely on specific implementation though as it may change and store only references to elements needed. You should probably remap JSON elements to your model, I think that's the whole purpose of JSON deserialization

Quick test:

        var parentField = result.Bar.GetType().GetMember("_parent", MemberTypes.Field, BindingFlags.Instance | BindingFlags.NonPublic)[0] as FieldInfo;
        var parentDocument = parentField.GetValue(result.Bar);

        var isDisposableProperty = parentDocument.GetType().GetProperty("IsDisposable", BindingFlags.Instance | BindingFlags.NonPublic) as PropertyInfo;
        Console.WriteLine(isDisposableProperty.GetValue(parentDocument)); // false

Proves that the instance of JsonDocument held by JsonElement is not disposable.