-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add missing clearing of pooled arrays #63710
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR addresses memory leaks by adding proper clearing of pooled arrays before returning them to array pools. When arrays containing references are returned to pools without clearing, the referenced objects cannot be garbage collected, potentially causing memory leaks.
Key changes:
- Adds conditional clearing logic that only clears arrays when they contain reference types or references
- Uses
RuntimeHelpers.IsReferenceOrContainsReferences<T>()
on .NET and falls back totypeof(T).IsPrimitive
check for .NET Framework - Optimizes clearing by only clearing the used portion of arrays when the count is known
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
src/SignalR/common/Shared/JsonUtils.cs | Adds clearing logic with .NET Framework compatibility for generic array pool wrapper |
src/Shared/ValueStringBuilder/ValueListBuilder.cs | Clears used portion of arrays in Dispose and Grow methods |
src/Mvc/Mvc.NewtonsoftJson/src/JsonArrayPool.cs | Adds clearing for JSON array pool implementation |
src/Middleware/OutputCaching/src/RecyclableArrayBufferWriter.cs | Clears arrays in Dispose and resize operations |
src/Middleware/OutputCaching/src/OutputCacheEntryFormatter.cs | Adds explicit clearing for string array when tags exist |
src/Components/Endpoints/src/FormMapping/PrefixResolver.cs | Clears FormKey array before returning to pool |
src/Components/Endpoints/src/FormMapping/Converters/CollectionAdapters/ArrayPoolBufferAdapter.cs | Clears arrays in buffer operations and result conversion |
src/Components/Components/src/NavigationManager.cs | Clears function delegate array after use |
ArrayPool has a return method with an option for clearing arrays. Why not use it instead of clearing before returning? https://learn.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1.return?view=net-9.0 |
The array pool method would clear the whole array, regardless of how many items are actually non-zero thus potentially more work than needed. With the approach here just the elements are cleared, that are used. But instead doing this on every use, maybe create a helper function for this like the one linked above? |
Yes, that's why. You generally don't want or need to clear the whole array. 🙂 |
Added the |
2f05452
to
3cd9a03
Compare
Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime. |
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) | ||
{ | ||
ArrayPool<T>.Shared.Return(tmp, count); | ||
} | ||
else | ||
{ | ||
ArrayPool<T>.Shared.Return(tmp); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is repeated many times.
What do you think of adding an extra helper method to ArrayPoolExtensions
.
ArrayPool<T>.Shared.ReturnAndClearReferences(tmp, count);
In the helper method have a comment that references are cleared from the pool so they can be GCed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that totally makes sense 👍
@BrennanConroy Could you take a look? Double check this is ok. |
/// <summary> | ||
/// Clears the specified range and returns the array to the pool. | ||
/// </summary> | ||
public static void Return<T>(this ArrayPool<T> pool, T[] array, int lengthToClear) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this overload? Shouldn't we just always use the ReturnAndClearReferences
one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's for cases were it's known that T
is a reference type.
Do you think ReturnAndClearReferences
should be used everywhere? I assume RuntimeHelpers.IsReferenceOrContainsReferences<T>()
check is optimized away by JIT so they end up being the same anyway.
Add missing clearing of pooled arrays
I found several places where arrays rented from pools are not cleared before returning. Any objects in the pools' arrays cannot be collected by the GC. This might keep objects in the returned arrays longer or, in the worst case, forever alive.
T
is a reference type or contains references.RuntimeHelpers.IsReferenceOrContainsReferences<T>()
is not available there, sotypeof(T).IsPrimitive
is checked instead.