Skip to content

Fix crossgen2 nobubble memory leak#123627

Merged
jkotas merged 5 commits intodotnet:mainfrom
t-mustafin:crossgen2_pipeline_nobubble_memleak
Feb 3, 2026
Merged

Fix crossgen2 nobubble memory leak#123627
jkotas merged 5 commits intodotnet:mainfrom
t-mustafin:crossgen2_pipeline_nobubble_memleak

Conversation

@t-mustafin
Copy link
Contributor

Crossgen2 --out-near-input and --single-file-compilation options without inputbubble option leads to memory leak. This patch reduces memory consumption. Measurement on armel shows reduce peak Rss consumption from 825 Mb to 312 Mb for launch:

./corerun ./crossgen2/crossgen2.dll -r:`pwd`/*.dll --jitpath ./libclrjit.so --targetarch armel -O --out-near-input --single-file-compilation *.dll

cc @clamp03 @gbalykov

--out-near-input and --single-file-compilation options without inputbubble option leads to memory leak.

Co-authored-by: Dong-Heon Jung <dheon.jung@samsung.com>
@clamp03 clamp03 requested a review from jkotas January 27, 2026 00:42
@jkotas
Copy link
Member

jkotas commented Jan 27, 2026

It is not clear why the code is clearing this particular table and none of the other ones. This feels very ad-hoc and likely leaving a number of issues behind.

What is the state that you want to be preserved between different compilations?

@t-mustafin
Copy link
Contributor Author

What is the state that you want to be preserved between different compilations?

Between different compilations we want to preserve opened modules:
https://github.com/dotnet/runtime/pull/51154/changes#diff-b927ba8051b104407ed1c58c2325e5755d3c12ca6d1d396b795b5bb79b365b15R251

@jkotas
Copy link
Member

jkotas commented Jan 27, 2026

Would it better to let the whole Compilation object go after each compilation and just selectively carry the type system context (that has the open modules) from one compilation to the next?

@t-mustafin
Copy link
Contributor Author

t-mustafin commented Jan 30, 2026

Would it better to let the whole Compilation object go after each compilation and just selectively carry the type system context (that has the open modules) from one compilation to the next?

It was intention of #51154: Compilation object exist inside of RunSingleCompilation during one dll compilation, and after one dll compilation Compilation object should be gc-collected. Same should be for new ReadyToRunCompilerContext: it is created inside of loop iteration for one dll and should be gc-collected after new ReadyToRunCompilerContext for next dll.

As far as I see, here is loop reference like ConcurrentBag<EcmaModule> -> EcmaModule -> EcmaAssembly -> ReadyToRunCompilerContext -> ... -> ConcurrentBag<EcmaModule>:

> gcroot b1dad7e0                                                                                                                               
Caching GC roots, this may take a while.
Subsequent runs of this command will be faster.

HandleTable:
    00000000b5f31654 (strong handle)
          -> b131185c System.Object[] 
          -> 96a29484 System.Threading.ThreadLocal<System.Collections.Concurrent.ConcurrentBag<Internal.TypeSystem.Ecma.EcmaModule>+WorkStealingQueue>+LinkedSlotVolatile[] 
          -> b1e48fa8 System.Threading.ThreadLocal<System.Collections.Concurrent.ConcurrentBag<Internal.TypeSystem.Ecma.EcmaModule>+WorkStealingQueue>+LinkedSlot 
          -> b1e48fc0 System.Collections.Concurrent.ConcurrentBag<Internal.TypeSystem.Ecma.EcmaModule>+WorkStealingQueue 
          -> b1f286a8 Internal.TypeSystem.Ecma.EcmaModule[] 
          -> b1d60e38 Internal.TypeSystem.Ecma.EcmaAssembly 
          -> b1be0d4c ILCompiler.ReadyToRunCompilerContext 
          -> b1bf49c4 ILCompiler.ReadyToRunMetadataFieldLayoutAlgorithm 
          -> b1da75d0 ILCompiler.ReadyToRunSingleAssemblyCompilationModuleGroup 
          -> b1da7f0c ILCompiler.DependencyAnalysis.ReadyToRun.ModuleTokenResolver 
          -> b1dae730 System.Func<Internal.TypeSystem.Ecma.IEcmaModule, System.Int32> 
          -> b1dad798 ILCompiler.DependencyAnalysis.ReadyToRun.ManifestMetadataTableNode 
          -> b1dad7e0 System.Collections.Concurrent.ConcurrentBag<Internal.TypeSystem.Ecma.EcmaModule> 

Found 1 unique roots.

Along with #23103 it may lead to memory leak. One of solutions discussed in #23103 is clear ConcurrentBag container in Dispose method.

@jkotas jkotas merged commit 99797e2 into dotnet:main Feb 3, 2026
98 of 101 checks passed
@t-mustafin t-mustafin deleted the crossgen2_pipeline_nobubble_memleak branch February 3, 2026 22:02
lewing pushed a commit to lewing/runtime that referenced this pull request Feb 9, 2026
Crossgen2 --out-near-input and --single-file-compilation options without
inputbubble option leads to memory leak. This patch reduces memory
consumption. Measurement on armel shows reduce peak Rss consumption from
825 Mb to 312 Mb for launch:
```
./corerun ./crossgen2/crossgen2.dll -r:`pwd`/*.dll --jitpath ./libclrjit.so --targetarch armel -O --out-near-input --single-file-compilation *.dll
```

cc @clamp03 @gbalykov

---------

Co-authored-by: Dong-Heon Jung <dheon.jung@samsung.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-crossgen2-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants