- Notifications
You must be signed in to change notification settings - Fork548
Fix leak by freeing NSObjectData#24094
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 ourterms of service andprivacy 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?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Every NSObject allocates native memory for `NSObjectData` via a `NSObjectDataHandle` critical handle. When the handle dies, the memory wasn't reclaimed because `IsInvalid` method was implemented incorrectly. Fixing this bug, leads to crashes in `ReleaseManagedRef` which tries to access the native data that was already freed. Normally, this shouldn't happen because `NSObject` is a normal finalizable object and `NSObjectDataHandle` is a critical finalizable object. This means that the finalizer for `NSObject` would always run first, considering that the 2 objects die at the same time. This means that the native `NSObjectData` would be cleared only after the finalizer for `NSObject` has run and `ReleaseManagedRef` finished executing. However, this is not the case because the "finalization" code of `NSObject` is not run from the finalizer thread but it is enqueued to `NSObject_Disposer`. This means that now the critical finalizer could have actually released the native memory before the finalization of the `NSObject` is done.The simple fix for this is to create a GCHandle keeping the `NSObjectDataHandle` alive until all `NSObject` finalization is done, at the end of `ReleaseManagedRef`.
BrzVlad commentedOct 23, 2025
This seems to have started happening after#23300. I didn't look into that change and I don't have much understanding of this area. Fixes a leak I noticed in an app reported indotnet/runtime#119491. The app was leaking about 50mb per 3hours, so it looks like this could be problematic in certain scenarios. Tested locally by rebuilding |
| publicoverrideboolIsInvalid{ | ||
| get=>handle!=IntPtr.Zero; | ||
| get=>handle==IntPtr.Zero; |
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.
🤦♂️
dartasen commentedNov 17, 2025
Is there any progress towards that critical subject ? |
rolfbjarne commentedNov 18, 2025
@dartasen this is actively being worked on, we're trying different solutions to find the best one with the least impact to execution speed and memory usage. |
Every NSObject allocates native memory for
NSObjectDatavia aNSObjectDataHandlecritical handle. When the handle dies, the memory wasn't reclaimed becauseIsInvalidmethod was implemented incorrectly. Fixing this bug, leads to crashes inReleaseManagedRefwhich tries to access the native data that was already freed. Normally, this shouldn't happen becauseNSObjectis a normal finalizable object andNSObjectDataHandleis a critical finalizable object. This means that the finalizer forNSObjectwould always run first, considering that the 2 objects die at the same time. This means that the nativeNSObjectDatawould be cleared only after the finalizer forNSObjecthas run andReleaseManagedReffinished executing. However, this is not the case because the "finalization" code ofNSObjectis not run from the finalizer thread but it is enqueued toNSObject_Disposer. This means that now the critical finalizer could have actually released the native memory before the finalization of theNSObjectis done.The simple fix for this is to create a GCHandle keeping the
NSObjectDataHandlealive until allNSObjectfinalization is done, at the end ofReleaseManagedRef.