Skip to content

Commit e2bb568

Browse files
author
Ahmed
committed
Fix memory leak in LazyLoaderFactory using WeakReference (#33444)
1 parent aeda6f5 commit e2bb568

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

src/EFCore/Infrastructure/Internal/LazyLoaderFactory.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ public class LazyLoaderFactory : ILazyLoaderFactory
1313
{
1414
private readonly ICurrentDbContext _currentContext;
1515
private readonly IDiagnosticsLogger<DbLoggerCategory.Infrastructure> _logger;
16-
private readonly List<ILazyLoader> _loaders = [];
16+
17+
// Use WeakReference to allow ILazyLoader instances to be GC'ed during enumeration,
18+
// preventing them from being rooted by the factory.
19+
//
20+
// List<WeakReference> is chosen over ConditionalWeakTable to avoid a 30-50%
21+
// performance regression
22+
//
23+
// While the list does not self-compact, it is explicitly cleared during
24+
// ResetState/Dispose to prevent memory accumulation in pooled contexts.
25+
private readonly List<WeakReference<ILazyLoader?>> _loaders = [];
1726

1827
/// <summary>
1928
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -38,7 +47,7 @@ public LazyLoaderFactory(
3847
public virtual ILazyLoader Create()
3948
{
4049
var loader = new LazyLoader(_currentContext, _logger);
41-
_loaders.Add(loader);
50+
_loaders.Add(new WeakReference<ILazyLoader?>(loader));
4251
return loader;
4352
}
4453

@@ -50,9 +59,9 @@ public virtual ILazyLoader Create()
5059
/// </summary>
5160
public void Dispose()
5261
{
53-
foreach (var loader in _loaders)
62+
foreach (var weakReference in _loaders)
5463
{
55-
loader.Dispose();
64+
if(weakReference.TryGetTarget(out var loader)) loader.Dispose();
5665
}
5766

5867
_loaders.Clear();

0 commit comments

Comments
 (0)