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!


Leave a comment

  • Max Mustermueller

    My thoughts about NativeAOT are:

    1. It’s incredibly disappointing that it’s not supported by WPF / Winforms (dunno about MAUI). That makes it only a console / library feature. That means I cannot use it in any of my larger applications but for those it only matters. I don’t need startup improvement for tiny console projects.

    2. The output size of a simple hello world is ridiculous (5mb). The same written in C++ or Pascal ends up with ~100kb. Unfortunately it has been rejected to improve that.

    So thanks for that but the way its (not) implemented it’s not really useful, for me at least.

    • Michal StrehovskyMicrosoft employee

      We plan to look into expanding the scenarios where NativeAOT is a good fit over time. It’s a function of time and resources. It is already possible to use WinForms with NativeAOT, but it requires some polish and won’t be advertised as a supported scenario because of that. I suggest following a .NET community member who helped to make a lot of progress on this over the years: Andrey Kurdyumov – Here’s one of his articles where he made tweaks to various open source WinForms apps to make them compatible with NativeAOT in the form it is right now: It can give you an idea where are we now.

      What output size are you looking for? We’re currently tuning NativeAOT to have comparable perf to the JIT based runtimes and that includes the garbage collector. The garbage collector alone has a ~100 kB precomputed lookup table in it so that it can sort really fast at runtime. This lookup table alone is the size of a Hello World in C++. That said, we do have switches to change various defaults from “compatible with JIT-based runtimes” to “optimized for size” that can currently get you to ~1 MB range:

      • Charles Roddie

        The state of dotnet AOT platform support on MAUI is yes for iOS, Mac catalyst, android (via monovm aot), and no for Windows (regression from Xamarin since UWP has AOT but WinUI hasn’t).

        The regression on Windows is more a matter of politics than time and resources. The work is being done by Andrey but the team is stalling (, They are treating reflection support as a reason to avoid generics (!), putting a tiny issue ahead of AOT support presumably because it’s not on the official roadmap.

        Regarding size, 1MB is very good overhead for dotnet UI applications. Reflection free is good hygiene, but removing garbage collection from dotnet isn’t acceptable unless enormous changes are made to dotnet (reviving project snowflake).

      • Max Mustermueller

        What about WPF? It is quite known, also because of the several repo polls, that people are unsatisfied with the WPF development / repo owners so far. The recently updated roadmap doesn’t show any NativeAOT support and every time we ask for that we get no answers.

        About the output size, like I said. There is a difference of 4.9mb between C++ and C# on a console project just printing “Hello World”. I don’t know exactly what’s the biggest part of it. Maybe someone with tools and knowledge can figure it out. The mentioned garbage collector with ~100kb table can’t be the problem.

        • Jason Baginski

          Just a mention because I have one sitting in front of me. A small console app I wrote to do some minor sort/consolidation on some config files on our mail server is 16,384 bytes with .NET Framework 4.8. 163,776 in .NET 6.0 and 165,317 in NET 6.0 with ReadyToRun. Sure, it’s quite a bit larger, but it’s 147kb, not 4.9mb.

        • Hughes, Danial

          Hi Max, I hope you’re not expecting a response. I have posted MANY times on both here and the VS blog, and i NEVER get a response to WPF questions. We have a huge WPF codebase and the lack of support for the technology from Microsoft is appalling. Just look at the WPF Roadmap on github that this article links to and its clear there’s nothing happening.
          We are currently evaluating non MS technology for future projects as it’s clear Micrsoft cannot be relied upon to support their own technology.

  • JinShil

    I suppose this is not the right place to post this, but I’m not sure where the right place is.

    What we really need from .NET right now is a way to deploy our .NET apps to a remote Linux computer (not WSL) and debug all with one click of the “Debug” button in Visual Studio. This works fine for Linux C++ apps in Visual Studio, but for .NET apps, it requires us to manually deploy, manually start the application, and then manually attach the debugger as described at

    I don’t know if this is something to be implemented by the .NET project templates or Visual Studio, so sorry if this is not the right place to post this. If you know the right place to advocate for such a feature, please let me know.

    • Max Mustermueller

      The problem is that the current WPF team has only taken over WPF and is also responsible for WinUI as far as I know which is their main job. So WPF has extremely low priority. You can easily see this on the issue response time and how long even simple PR’s remain unreviewed for months. It is extremely unlikely that WPF gets any new features. Since it was open sourced it hasn’t even got an folder picker yet which is one of the most requested and easiest features to implement (see several attempts via PR which all hasn’t got reviewed). The Winforms team however is the same, that’s why they are ahead of most and issue response time is fast, pull requests are merged constantly.

      We can only hope that someone at Microsoft notice that and hires a few more developers working exclusively on WPF, for Microsoft money shouldn’t be a problem.

      • Paulo Pinto

        Looking at WinUI lack of progress, and exponential set of github issues across all repos related to WinRT and WinUI tooling, they aren’t also doing an exception job on that side either.

        And those .NET developers that have to deal with C++ stuff, and were robbed of C++/CX, also get to enjoy C++/WinRT with a development experience akin to the good old days of ATL development. Catching up with C++/CX tooling seems to be something, that if it happens at all, is only being considered for end 2023 or early 2024, as per community calls feedback (the 16th March one).

        It seems almost everyone has left the desktop ship and is busy on Azure or Web stuff, hence why so much talk about using Blazor with WebWidgets.


        You mean they were lying to us when they said they were hiring for WPF? They have the money and they notice. They know the business world is using WPF and they know that literally nobody cares about the whole garbage WinUI/WinRT stack. Do not accept their excuses, rather find out what nefarious project really has their attention like Pluton and TPM.

        What I gather is that TPTB don’t like that you can create free-spirited Win32 apps with WPF that actually get work done. They want you junking up your project with signed certificates so one day they can conveniently expire those certificate authorities for saying you don’t support Ukraine and leave you holding the bag.

  • Dawid Haładus

    Will using AOT make the code not readily available through reverse engineering?
    Unfortunately, the current applications are easy to browse with e.g. dnSpy

  • B C

    We are having issues getting net7.0-android to be recognized. We are currently migrating from .net6 and are now getting this error with the android version in:

    error NU1012: Platform version is not present for one or more target frameworks, even though they have specified a platform: net7.0-android
    error NETSDK1139: The target platform identifier android was not recognized.

    Is android not supported in this release?

    EDIT: we are using TargetFrameworks instead of TargetFramework: net7.0-windows;net7.0-android
    We have to use them specifically because one part of the code uses WinForms as a display canvas for vulkan and winforms cannot be used with “net7.0” it has to be “net7.0-windows”.

      • B C

        Appreciate the reply, but we did that but because of limitations on packaging, two of the libraries had to be merged into one csproj. We have a small library that uses almost no other references and it wont compile either and it doesnt use winforms at all.

        That said, We reverted the test machine back to .net6 and it would not compile any longer either. We removed the .net 7 sdk from add/remove programs and boom, we could compile .net 6 on the machine again. I have no doubt there are massive issues with the .net 7 sdk or they simply did not include android.

      • B C

        Was this in response to my post? If so we don’t use Maui. The only UI we use is for windows and it’s WinForms only to present vulkan. We really want .net 7 for AOT, we had AOT running in the early .net 6 releases but at some point a newer .net 6 release broke it. I can run back and get the last version of .net 6 it last worked before it broke and then later a block for WinForms was added.

        In order to get native aot running again, we had hoped this was the start of the answer but .net 7 breaks targeting multiple frameworks which I presume a few developers use including us.

  • LiFei

    Most people pay attention to the startup speed and size of desktop applications. Console programs are generally used by developers themselves, while dotnet is committed to optimizing the console and ignoring desktop applications. What’s the use of faster and smaller console startup