Skip to content

Deduplicate "all managed object wrappers" mapping - #133260

Merged
jkoritzinsky merged 1 commit into
mainfrom
jkoritzinsky-patch-1
Sep 5, 2026
Merged

jkoritzinsky merged 1 commit into
mainfrom
jkoritzinsky-patch-1

Conversation

@jkoritzinsky

Copy link
Copy Markdown
Member

Prevent duplicate entries in the "all managed object wrappers" table.

Fixes #129191

For the case in #129191, there is always only one ComWrappers instance, so this collection will be length 1.

I chose to do a simple fix like this for the following reasons:

  1. This collection is read from the DAC and cDAC directly to support the !DumpObj command. Any significant changes to the structure (such as moving to a HashSet<ManagedObjectWrapperHolder>) would require very large changes to enable enumerating the structure from DAC or cDAC code, creating a higher risk change for backporting.
  2. In practice, objects are passed to unmanaged code via 1 ComWrappers instance in the vast majority of cases outside of our own tests. In the unlikely case it's passed via more than 1, it would be via 2, the CsWinRT ComWrappers instance and the StrategyBasedComWrappers default instance.
  3. I considered adding a parallel HashSet for deduplication. However, that felt like a very large memory cost for the common scenario (.NET object passed to native via only one ComWrappers instance) for a minor savings in a very unlikely scenario (many ComWrappers for the same .NET object).

Prevent duplicate entries in the "all managed object wrappers" table.

Fixes #129191

For the case in #129191, there is always only one ComWrappers instance, so this collection will be length 1.

I chose to do a simple fix like this for the following reasons:

1. This collection is read from the DAC and cDAC directly to support the `!DumpObj` command. Any significant changes to the structure (such as moving to a `HashSet<ManagedObjectWrapperHolder>`) would require very large changes to enable enumerating the structure from DAC or cDAC code, creating a higher risk change for backporting.
2. In practice, objects are passed to unmanaged code via 1 ComWrappers instance in the vast majority of cases outside of our own tests. In the unlikely case it's passed via more than 1, it would be via 2, the CsWinRT ComWrappers instance and the StrategyBasedComWrappers default instance.
3. I considered adding a parallel HashSet for deduplication. However, that felt like a very large memory cost for the common scenario (.NET object passed to native via only one ComWrappers instance) for a minor savings in a very unlikely scenario (many ComWrappers for the same .NET object).
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The change is small, localized to diagnostics registration, and avoids unbounded list growth while keeping the existing DAC/cDAC-observable structure intact.

Review tier: Lite
Findings: None

What changed in this PR

Prevents duplicate ManagedObjectWrapperHolder entries from being recorded in the global “all managed object wrappers” ConditionalWeakTable, which is used by diagnostics tooling to enumerate managed object wrappers.

Changes:

  • Adds a guarded insert (Contains check) when registering a managed object wrapper for diagnostics, preventing repeated additions of the same ManagedObjectWrapperHolder to the per-object list.
File Description
src/​libraries/​System.Private.CoreLib/​src/​System/​Runtime/​InteropServices/​ComWrappers.cs Deduplicates per-object wrapper registration in the diagnostics tracking table to avoid list growth from repeated registrations.

@jtschuster jtschuster left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jkoritzinsky

Copy link
Copy Markdown
Member Author

/backport to release/10.0

@jkoritzinsky

Copy link
Copy Markdown
Member Author

/backport to release/11.0

@dotnet dotnet deleted a comment from github-actions Bot Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release/10.0 (link to workflow run)

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0 (link to workflow run)

@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Sep 9, 2026
JulieLeeMSFT pushed a commit that referenced this pull request Sep 9, 2026
…3292)

Backport of #133260 to release/10.0

/cc @jkoritzinsky

## Customer Impact

- [X] Customer reported
- [ ] Found internally

Ever increasing managed memory usage (memory leak) each time a managed
object is passed to COM via ComWrappers, even when reusing the same COM
instance. The only workaround is to turn off the "managed debugging"
helper APIs.

## Regression

- [X] Yes
- [ ] No

Regressed in #113907

## Testing

Local validation via SOS (the only direct exposure of the adjusted API)

## Risk

Low, only affects an API exposed to SOS (no other consumers use it). As
mentioned in the PR on main, the expected number of elements in the
collection when deduplicated is 1 or 2 at most, so not using a
hash-table is fine for the scenario (in particular the user-reported
issue will have 1 item in the collection after the fix).

**IMPORTANT**: If this backport is for a servicing release, please
verify that:

- For .NET 8 and .NET 9: The PR target branch is `release/X.0-staging`,
not `release/X.0`.
- For .NET 10+: The PR target branch is `release/X.0` (no `-staging`
suffix).

## Package authoring no longer needed in .NET 9

**IMPORTANT**: Starting with .NET 9, you no longer need to edit a NuGet
package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older
versions.

Co-authored-by: Jeremy Koritzinsky <jekoritz@microsoft.com>
akoeplinger pushed a commit that referenced this pull request Sep 11, 2026
…3291)

Backport of #133260 to release/11.0

/cc @jkoritzinsky

## Customer Impact

- [X] Customer reported
- [ ] Found internally

Ever increasing managed memory usage (memory leak) each time a managed
object is passed to COM via ComWrappers, even when reusing the same COM
instance. The only workaround is to turn off the "managed debugging"
helper APIs.

## Regression

- [X] Yes
- [ ] No

Regressed in #113907

## Testing

Local validation via SOS (the only direct exposure of the adjusted API)

## Risk

Low, only affects an API exposed to SOS (no other consumers use it). As
mentioned in the PR on main, the expected number of elements in the
collection when deduplicated is 1 or 2 at most, so not using a
hash-table is fine for the scenario (in particular the user-reported
issue will have 1 item in the collection after the fix).

**IMPORTANT**: If this backport is for a servicing release, please
verify that:

- For .NET 8 and .NET 9: The PR target branch is `release/X.0-staging`,
not `release/X.0`.
- For .NET 10+: The PR target branch is `release/X.0` (no `-staging`
suffix).

## Package authoring no longer needed in .NET 9

**IMPORTANT**: Starting with .NET 9, you no longer need to edit a NuGet
package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older
versions.

Co-authored-by: Jeremy Koritzinsky <jekoritz@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Regression] COMWrapper Leak when targeting .NET 10 on a WinUI3 project

4 participants