Skip to content

Unbounded memory leak from DependencyInjectionEventSource #115974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 4, 2025
Original file line number Diff line number Diff line change
@@ -31,6 +31,11 @@ private DependencyInjectionEventSource() : base(EventSourceSettings.EtwSelfDescr
{
}

// There is a risk that each ServiceProviderBuilt call only finds one entry to remove, and the next call will clean the list again and spend O(n) time on that.
// So instead of tying the cleaning to the resizing, it might be better to have a separate counter.
// For example: the current capacity is 1024, but the last time the list was cleaned, 1000 valid entries were left, so now we'll let the list grow to 2000 entries before cleaning it again.
private int? _survivingProvidersCount;

// NOTE
// - The 'Start' and 'Stop' suffixes on the following event names have special meaning in EventSource. They
// enable creating 'activities'.
@@ -144,6 +149,14 @@ public void ServiceProviderBuilt(ServiceProvider provider)
{
lock (_providers)
{
int providersCount = _providers.Count;
if (providersCount > 0 &&
(_survivingProvidersCount is int spc ? (uint)providersCount >= 2 * (uint)spc : providersCount == _providers.Capacity))
{
_providers.RemoveAll(static p => !p.TryGetTarget(out _));
_survivingProvidersCount = _providers.Count;
}

_providers.Add(new WeakReference<ServiceProvider>(provider));
}

@@ -162,6 +175,11 @@ public void ServiceProviderDisposed(ServiceProvider provider)
if (!reference.TryGetTarget(out ServiceProvider? target) || target == provider)
{
_providers.RemoveAt(i);

if (target is not null)
{
break;
}
}
}
}
Loading