Fix memory leak by clearing shared array pool references in LightweightObservableBase #19167
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What does the pull request do?
The
LightweightObservableBase<T>
holdsIObserver<T>
references in thePublishNext
method for a brief moment. However, it does not clear the shared array after returning it to the pool. This can cause a memory leak that persists for about 30 seconds, even when the GC runs multiple collection cycles.This pull request clears the references to observers in the shared array after calling the
OnNext
method. This way, we can prevent the memory leak.Looking at the code in
LightweightObservableBase
, we can see that when the subscriber count exceeds 3, a shared array is rented from the pool. Therefore, to reproduce the memory leak, we need more than 3 subscribers to the same observable.What is the current behavior?
See the image below. This is a memory analysis from dotMemory.
After the
Window
is closed, pressing theForce GC
button on dotMemory several times shows that theWindow
is still retained in memory. After about 30 seconds, theWindow
will finally be collected by the GC.What is the updated/expected behavior with this PR?
After the
Window
is closed, it should be collected by the GC during the next garbage collection cycle. The memory analysis should no longer show theWindow
being retained in memory.How was the solution implemented (if it's not obvious)?
This is a simple fix. We clear the references to observers in the shared array after calling the
OnNext
method. An alternative solution would be to passtrue
toArrayPool<T>.Shared.Return
, but that approach may be slightly slower than the solution implemented in this pull request.Checklist
Breaking changes
None. There are no breaking changes in this pull request.
Obsoletions / Deprecations
Fixed issues