Repository navigation
Investigate additional NativeAOT static initialization and rooting size opportunities #132595
Description
Summary
Follow-up to #132550 and #132576. A review of all 15 NativeAOT Sizeoscope reports linked from #132576 (comment) found additional cases where static initialization, canonical generic sharing, reflection metadata, or shared representations retain unnecessary code and data.
Priority order: standard/base applications first, representative real-world applications second, and explicitly minimal configurations as supporting evidence. The strongest near-term candidates are Unix ConsolePal, ASP.NET HostString, typed-header generic sharing, and hosting telemetry method normalization. The largest longer-term opportunity is NativeAOT AppContext initialization.
Important
Sizes below are mapped edge-dominated closures, not predicted executable savings. Each change needs a direct before/after NativeAOT publish measurement.
Candidate overview
| Priority | Candidate | Reports / mapped closure | Classification |
|---|---|---|---|
| 1 | Unix ConsolePal terminal metadata |
10.4–10.7 KB across Linux Hello/reflection; 5.2–7.4 KB server apps | Direct holder change |
| 2 | ASP.NET HostString caches |
15.6–17.7 KB across all server reports | Direct holder change |
| 3 | HeaderDictionaryTypeExtensions.GetList<T> |
23.8–24.3 KB Web API; ~5 KB Todos API | Generic specialization prototype |
| 4 | HostingTelemetryHelpers.KnownHttpMethods |
~13.5 KB minimal Kestrel; 4.8 KB Web API | Direct representation change |
| 5 | Linux NetworkChange state |
18.2 KB Todos API | State separation prototype |
| 6 | PathUtils table construction |
4.3–5.4 KB across server reports | Direct initialization rewrite |
| 7 | System.Net.Http.KnownHeaders |
~20.9 KB Todos API | Architectural representation change |
| 8 | UriHelper search tables |
11.5–12.3 KB Avalonia | Holder/representation prototype |
| 9 | HMAC algorithm dispatch | 4.9–5.5 KB SHA3-512 closure on Windows | Cryptography prototype |
| 10 | TimeZoneInfo UTC construction |
16.8 KB Avalonia; 4.4–4.9 KB server apps | Runtime design investigation |
| 11 | NativeAOT AppContext data store |
38.1 KB Windows Hello; 15.0 KB Linux Hello | Architectural runtime change |
| 12 | Logging delegate initialization | Repeated 1.5–5.8 KB closures | Cross-cutting architectural work |
Direct candidates
- Defer Unix
ConsolePalterminal metadata. MoveLazy<TerminalFormatStrings>into a nested holder. PlainConsole.WriteLinecurrently rootsTermInfo.DatabaseFactory.ReadActiveDatabasewithout using terminal formatting. - Defer ASP.NET
HostStringimplementation caches. Moves_safeHostStringCharsands_idnMappinginto non-reflected nested holders, independently where useful. The current constructor is rooted directly through reflection of the privateSearchValues<char>field. - Replace
HostingTelemetryHelpers.KnownHttpMethods. ReuseHttpMethodscanonical comparisons or an equivalent allocation-free helper instead of constructing aFrozenDictionary. - Rewrite
PathUtilstable initialization without LINQ. Both validation tables are used in the server workloads, so holder separation may be neutral; directly construct the small platform-specific sets and measure. - Separate independent
Taskstatic groups. GiveTaskFactory(~120 B), the completed-task singleton (~362 B), and the execution-context callback (~80–126 B) independent holders.
Prototypes requiring measurement
- Specialize
HeaderDictionaryTypeExtensions.GetList<T>for NativeAOT. CanonicalSystem.__Canonsharing causesRequestHeaders.Acceptto retain the Set-Cookie parser. Use closed typed parsing entry points or an inlinable type dispatcher. - Separate Linux
NetworkChangeaddress and availability state. Address-change subscription currently roots availability-only dictionaries, event arguments, callbacks, timer machinery, and handlers. - Separate
UriHelperwhitespace and search tables.Uri.ResolveHelperneeds the whitespace array but triggers fiveSearchValues<char>initializations. Retained escaping methods may still root a subset. - Avoid routing HMAC long-key normalization through every SHA implementation.
HMACSHA256 -> HMACCommon.ChangeKeyImpl -> SHA3_512.HashDataroots SHA3 support initialization. Investigate provider-based hashing using the selected algorithm identifier. - Investigate
TimeZoneInfo.CreateUtcTimeZone. Many retained methods reference UTC state, so simplifying UTC construction/localization is more promising than only moving the field. - Evaluate compact
HttpCharactersvalidation tables. Seven sharedSearchValuesproduce 3.7–4.3 KB closures. Full Kestrel uses most operations, so holders alone are unlikely to help; compact ASCII tests or RVA bitsets require performance validation.
Architectural investigations
- Avoid eager NativeAOT
AppContextmaterialization. A holder is insufficient becauseAppContext.GetDatais retained. Investigate on-demand native runtime-knob lookup instead of eagerly decoding all keys and values intoDictionary<string, object?>. - Separate
KnownHeadersname recognition from object materialization. One reflected header getter initializes everyKnownHeaderand parser.TryGetKnownHeaderreferences every field, so per-field holders alone cannot remove the closure. - Reduce eager logging delegate construction. Kestrel, routing, Data Protection, gRPC, and health checks contain many independent
LoggerMessage.Defineclosures. Evaluate source-generated logging or per-event implementations.
Ecosystem follow-ups
- Avalonia MicroCom registration: the Windows module initializer eagerly registers independent vtables, with individual closures of roughly 3–15 KB. Investigate per-interface registration holders or registration from actual consumers.
- Avalonia reflection metadata: compiler-generated static-constructor lambdas and broad property-registration graphs are retained. Investigate excluding unnecessary implementation methods and narrowing reflected control/property roots.
Coverage
| Category | Reports reviewed | Result |
|---|---|---|
| Standard/base | Hello and reflection, Windows and Linux | ConsolePal, AppContext, and small Task opportunities |
| Representative | Avalonia, Web API, and Todos API, Windows and Linux | Most actionable and architectural candidates above |
| Minimal | Hello and Kestrel on Windows/Linux; WinRT component on Windows | Reinforces the same roots; no higher-priority unique candidate |
Not currently actionable as holder-only changes
The NativeAOT reflection type-unifier tables are already independently isolated. RuntimeAssemblyInfo, runtime cast/interface caches, processor and array-pool state, calendar/leap-second state, generated JSON/protobuf metadata, and both BCryptAlgorithmCache paths are genuinely used in the examined workloads. Encoding singletons and comparer wrappers are retained by shared runtime selection logic. TextWriter.Null is constrained by its public static field shape.
The reports also contain the Number roots already addressed by #132550 and the CompareInfo/EncodingTable roots addressed by #132576.
Note
This issue was drafted with GitHub Copilot.
Activity
dotnet-policy-service commentedon Aug 21, 2026
Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.
tannergooding commentedon Aug 21, 2026
CC. @MichalStrehovsky, @jkotas
Did some automated analysis over the diffs as a follow up to the other work. Some of this is simpler than others, but most of it looks to be reasonably actionable. There's certainly a few clear "large islands" that are the most impactful and so we can likely whittle away at this for .NET 12
A few are too small to be meaningful and some may be unactionable as well, we can mark those off or decide to defer as appropriate.
MichalPetryka commentedon Aug 21, 2026
Kinda related if we want some runtime handling for this: #85014 (comment)
jeffhandley commentedon Aug 22, 2026
@tannergooding I triaged this into the 12.0.0 milestone. Let me know if you were considering this as a quality gate for 11.0.0.
tannergooding commentedon Aug 22, 2026
These are generally all pre-existing, so I don't see a significant need to backport or track it for 11.0.
If any of them are very easy and big enough wins, I think they can be bar checked for backport individually.
Allow trimming of TerminalFormatStrings (#132597)