Skip to content

RingBuffer<T>.Clear() keeps cleared reference-type elements alive (memory leak) #72

Description

@matt-edmondson

What's wrong

RingBuffer<T>.Clear() (Containers/RingBuffer.cs ~304) only resets the indices and the count:

public void Clear()
{
	BackIndex = 0;
	FrontIndex = 0;
	Count = 0;
}

The backing array is left untouched. Every reference-type element that was logically removed stays reachable until a later PushBack overwrites its slot. If the buffer is never refilled, that never happens. Every other array-backed container in the library (ContiguousCollection, ContiguousSet, ContiguousMap) clears its references in Clear, and SpscRingBuffer clears each slot when it dequeues.

Repro (reproduced)

var rb = new RingBuffer<object>(4);
WeakReference wr = Fill(rb); // [NoInlining]: var o = new object(); rb.PushBack(o); return new WeakReference(o);
rb.Clear();
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Console.WriteLine(wr.IsAlive); // Observed: True. The same test with ContiguousCollection<object> prints False.

Why it matters

Ring buffers usually hold recent large objects, such as frames, audio blocks or log entries. After Clear(), a buffer of N slots can keep up to N of them in memory for as long as the buffer itself lives.

Suggested fix

In Clear(), clear the array before resetting the indices when T contains references:

if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) Array.Clear(Buffer, 0, Buffer.Length);

On netstandard targets, use !typeof(T).IsValueType, as the other containers do.

Acceptance: a WeakReference test shows that items are collectable after Clear().

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions