Announcing .NET 7 Preview 3

Jon Douglas

Today, we are glad to release .NET 7 Preview 3. The third preview of .NET 7 includes enhancements to observability, startup times, codegen, GC regions, native AOT compilation, and more. The bits are available for you to grab right now and start experimenting with new features like:

  • Native AOT
  • Default GC regions
  • ASP.NET Core startup time improvements

You can download .NET 7 Preview 3, for Windows, macOS, and Linux.

.NET 7 Preview 3 has been tested with Visual Studio 17.2 Preview 3. We recommend you use the preview channel builds if you want to try .NET 7 with Visual Studio family products. Visual Studio for Mac support for .NET 7 previews isn’t available yet but is coming soon. Now, let’s get into some of the latest updates in this release.

Faster, Lighter Apps with Native AOT

In the .NET 7 Preview 2 blog post, we announced that the Native AOT project has been moved out of experimental status and into mainline development in .NET 7 in the dotnet/runtime repo. We know that many of you have been eagerly awaiting updates from the team on what’s coming for Native AOT, and we have a couple of new updates for you for Preview 3.

If you want details about Native AOT, or to jump in and get started with it, the repo docs are the best place for that.

We also recognize that some of you might not be familiar with what Native AOT is, so we wanted to share a quick overview of it with you.

What is Native AOT?

Ahead-of-time (AOT) compilation refers to an umbrella of technologies which generate code at application build time, instead of run-time. AOT is not new to .NET. Today we ship ReadyToRun for client and server scenarios, and Mono AOT for mobile and WASM. Native AOT brings full native pre-compilation to .NET desktop client and server scenarios. Native AOT is not replacing these existing technologies, rather it’s offering a new set of capabilities that unlocks new form factors.

Existing AOT-compiled .NET assemblies contain platform-specific data structures and native code to frontload work typically done at runtime. Precompiling these artifacts saves time at startup (e.g. ReadyToRun), and enables access to no-JIT platforms (e.g. iOS). If precompiled artifacts are not present, .NET either falls back to JIT or interpretation (depending on the platform).

Native AOT is similar to .NET’s existing AOT technologies, but it produces only native artifacts. In fact, the Native AOT runtime does not know how to read the .NET assembly file formats – everything is platform-native. The executable file format parsing is fully handled by the underlying operating system.

The main advantage of Native AOT is in startup time, memory usage, accessing to restricted platforms (no JIT allowed), and smaller size on disk. Applications start running the moment the operating system pages in them into memory. The data structures are optimized for running AOT generated code, not for compiling new code at runtime. This is similar to how languages like Go, Swift, and Rust compile. Native AOT is best suited for environments where startup time matters the most. Targeting Native AOT has stricter requirements than general .NET Core/5+ applications and libraries. Native AOT forbids emitting new code at runtime (e.g. Reflection.Emit), and loading new .NET assemblies at runtime (eg. plug-in models).

Prepare your apps for Native AOT

For .NET 7 we are targeting console apps and native libraries as the primary scenario for Native AOT. Application developers and library authors can now take advantage of Native AOT by ensuring that their applications are trimmable. Since trimming is a requirement for Native AOT compilation, preparing your applications and libraries now for trimming will help them get ready for Native AOT as well. If you are an author of any .NET libraries, following the “Trimming libraries” instructions specifically will help you prepare your libraries for trimming and Native AOT.

One of the apps that we’re planning to ship in .NET 7 compiled with Native AOT is the crossgen tool. Crossgen is part of the .NET SDK. It’s the CoreCLR AOT compiler that produces ReadyToRun executables. Crossgen is written in C# and we currently ship it compiled with itself as a ReadyToRun app (it’s turtles all the way down!). We’re already seeing some very promising numbers in terms of compilation speed and size. Crossgen benefits heavily from Native AOT because it’s a short-lived process and the startup overhead dominates the overall execution time:

Scenario ReadyToRun NativeAOT
Compile CoreLib 4182 ms 3512 ms
Compile HelloWorld 185 ms 49 ms
Configuration Size
ReadyToRun 34.8 MB
NativeAOT 17.6 MB

Looking ahead, Native AOT compatibility will be improved over the next few versions of .NET, however there will always be reasons to prefer JIT for many scenarios. We will also add first-class support in the dotnet SDK for publishing projects with Native AOT.


.NET 7 continues to evolve support for the cloud native OpenTelemetry specification. Preview 3 adds support for specification updates #988 and #1708 that make the trace state mutable for samplers.

    //  ActivityListener Sampling callback
    listener.Sample = (ref ActivityCreationOptions<ActivityContext> activityOptions) =>
        activityOptions = activityOptions with { TraceState = "rojo=00f067aa0ba902b7" };
        return ActivitySamplingResult.AllDataAndRecorded;


The latest Managed Extensibility Framework gets a slight update to align with the previous version APIs. The new APIs allow adding a single object instance to the System.Composition.Hosting container. Similar to the functionality provided in the legacy interfaces System.ComponentModel.Composition.Hosting with the API ComposeExportedValue(CompositionContainer, T)

Proposal: Inject existing object into MEF2

namespace System.Composition.Hosting
    public class ContainerConfiguration
        public ContainerConfiguration WithExport<TExport>(TExport exportedInstance);
        public ContainerConfiguration WithExport<TExport>(TExport exportedInstance, string contractName = null, IDictionary<string, object> metadata = null);

        public ContainerConfiguration WithExport(Type contractType, object exportedInstance);
        public ContainerConfiguration WithExport(Type contractType, object exportedInstance, string contractName = null, IDictionary<string, object> metadata = null);

Startup time improvements with Write-Xor-Execute enabled

Performance continues to be a major focus for .NET 7. The dotnet/runtime#65738 PR reimplemented the precode and call counting stubs (tiered compilation helper stubs) to significantly reduce number of post-creation modifications of executable code in the runtime. This resulted in 10-15% startup time improvements.

As a bonus, this change also resulted in steady state performance improvements (upto 8%) in some microbenchmarks and some ASPNet Benchmarks even without Write-Xor-Execute enabled.

However, there are few regressions resulting from that change too (without Write-Xor-Execute enabled) that will be addressed in the upcoming preview releases. These were observed in the Orchard and Fortunes benchmarks on Intel processors only.


Thanks in a large part to community contributors, Preview 3 features several optimizations and bug fixes to code generation and just-in time (JIT) compilation. Here’s an overview of the changes that are available today.

Community PRs

These pull requests were all initiated by community contributors.

From @clamp03
From @SkiFoD
From @sandreenko
From @SingleAccretion
From @trympet
From @Wraith2

Dynamic PGO


Loop Optimizations

  • Loop Cloning improved the duration of single invocation by 21% for System.Collections.Tests.Perf_BitArray.BitArrayLeftShift(Size: 512): image

General Optimizations

GC Regions Enabled by default

With Preview 3, regions functionality which should help with memory utilization for high throughput applications has been enabled by default. The functionality is now enabled for all Platforms except MacOS and NativeAOT (which would be enabled in the future). More details are available in this issue:

We expect some working set increases for smaller applications due to how regions are initially allocated. If you notice any functional or performance differences please create an issue within the runtime repo.

Cryptography: Generating X.500 names more robustly

This change simplifies working with certificates by introducing a class that provides more clarity for parsing X.500 names.

Make it safer and easier to build an X500DistinguishedName

Classically, anyone wanting to build a X.500 name (such as for creating test certificates with the CertificateRequest class did so with string manipulation, either via a simple literal or with string formatting, e.g.

request = new CertificateRequest($"CN={subjectName},OU=Test,O=""Fabrikam, Inc.""", ...);

This is generally fine, except for when subjectName contains a comma, quote, or anything else that has an influence on the parser. To address that, we added the X500DistinguishedNameBuilder class. Because every method only operates on a single relative distinguished name (RDN), there’s no ambiguity in parsing. As a bonus, since the RDN identifiers are expanded, you no longer have to guess what “CN” stands for (“Common Name”).

X500DistinguishedNameBuilder nameBuilder = new();
nameBuilder.AddOrganizationName("Fabrikam, Inc.");

request = new CertificateRequest(nameBuilder.Build(), ...);

Targeting .NET 7

To target .NET 7, you need to use a .NET 7 Target Framework Moniker (TFM) in your project file. For example:


The full set of .NET 7 TFMs, including operating-specific ones follows.

  • net7.0
  • net7.0-android
  • net7.0-ios
  • net7.0-maccatalyst
  • net7.0-macos
  • net7.0-tvos
  • net7.0-windows

We expect that upgrading from .NET 6 to .NET 7 should be straightforward. Please report any breaking changes that you discover in the process of testing existing apps with .NET 7.


.NET 7 is a Current release, meaning it will receive free support and patches for 18 months from the release date. It’s important to note that the quality of all releases is the same. The only difference is the length of support. For more about .NET support policies, see the .NET and .NET Core official support policy.

Breaking changes

You can find the most recent list of breaking changes in .NET 7 by reading the Breaking changes in .NET 7 document. It lists breaking changes by area and release with links to detailed explanations.

To see what breaking changes are proposed but still under review, follow the Proposed .NET Breaking Changes GitHub issue.


Releases of .NET include products, libraries, runtime, and tooling, and represent a collaboration across multiple teams inside and outside Microsoft. You can learn more about these areas by reading the product roadmaps:


We appreciate and thank you for your all your support and contributions to .NET. Please give .NET 7 Preview 3 a try and tell us what you think!