Skip to content

Huge GC performance degradation when many RCW are created #126837

@vladimir-cheverdyuk-altium

Description

Description

Our applications uses a lot of COM-like interfaces to communicate with C++ code. And I found that the GC became much slower when many of them created. To me it looks like even Gen 0 is affected.

I've attached small project that demonstrate the problem. Demo.zip

It creates 1.5M RCW's and then simulates the Newtonsoft Deserialize function by creating strings and arrays. With 1.5M RCW's it takes more than 100 seconds on my PC. If you comment RCW creating (the first for-loop), then it takes from 2 to 2.5 seconds. Just in case creating RCW's takes only 1 second.

If you add following code:

public class ClientApiAgile : IClientApiAgile { }

and replace the body of the first for-loop to this:

rcws.Add(new ClientApiAgile());

it also takes 2.5 seconds, so number of objects is not a problem.

STR:

  1. Open the C++ project from Cpp directory, switch to the Release configuration and compile project. I used VS 2022 to compile it. It has very basic COM-like interface.
  2. Open the C# project from the DotNet\App directory, switch to the Release configuration and run it without debugger.

Configuration

.NET 8, .NET 10.
Windows 11, x64

Regression?

I don't think so.

Data

  • It takes 100 seconds to execute the application with 1.5M of RCW's
  • When the first for loop is commented, it takes 2.5 seconds to execute the simulation.
  • When the first loop creates 1.5M of the regular .NET objects, it takes 2.5 seconds to execute the simulation.

Analysis

It looks like the GC calls GCToEEInterface::AfterGcScanRoots, that function calls AppDomain::DetachRCWs and that function calls DetachWrappersWorker where the most of the time is spent.

I understand that this code must run but perhaps it can execute less often. Or perhaps that code can execute faster.

We are porting some native code to C# but some bits and pieces are still on the native C++ side. We could have up to 5M of RCW's and I afraid that our customers will have micro-freezes when use our application.

From what I see it looks like there is no workaround. Probably the new COM that uses source generator can help, but we are using thousands interfaces and converting everything to the new COM will require a lot of work. Plus I think it also uses RCW's internally.

Any help or workaround will be greatly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions