.NET 11 preview 3: novidades no runtime, JIT e WebAssembly

O .NET 11 Preview 3 chegou em 14 de abril de 2026 com atualizações relevantes no runtime. A versão consolida o trabalho iniciado nos previews anteriores e traz mudanças que afetam quem escreve código assíncrono, quem depende do JIT e quem publica aplicações WebAssembly.

Neste artigo, cubro os principais avanços do runtime com exemplos e contexto. O foco está nos aspectos técnicos mais relevantes para equipes que trabalham com C# e ASP.NET Core.

O que é o .NET 11 e qual o seu ciclo de lançamento

O .NET 11 é a versão STS (Standard Term Support) prevista para novembro de 2026. Ela sucede o .NET 10, lançado em novembro de 2025 como versão LTS (Long Term Support).

Desde o .NET 9, versões STS recebem 24 meses de suporte, enquanto versões LTS recebem três anos. Portanto, para aplicações que exigem ciclos de suporte mais longos, o .NET 10 continua sendo a escolha mais conservadora.

Ainda assim, acompanhar cada preview ajuda a antecipar mudanças. Por isso, vale entender o que o .NET 11 Preview 3 entrega no runtime antes da versão final.

Runtime async no .NET 11 Preview 3

A principal novidade do runtime nesta versão é a remoção do requisito [RequiresPreviewFeatures] das APIs de runtime async. Segundo as notas de versão oficiais do runtime, um projeto net11.0 não precisa mais de EnablePreviewFeatures apenas para ativar o recurso.

Até o Preview 2, quem quisesse experimentar o runtime async precisava marcar o projeto inteiro como preview. Com o .NET 11 Preview 3, basta a feature switch.

<!-- Antes do Preview 3 -->
<PropertyGroup>
  <Features>runtime-async=on</Features>
  <EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>

<!-- A partir do Preview 3 -->
<PropertyGroup>
  <Features>runtime-async=on</Features>
</PropertyGroup>

A feature switch runtime-async=on continua necessária para ativar o comportamento. A diferença é que o projeto não precisa mais ser marcado explicitamente como preview.

O que é o runtime async

O runtime async é uma das maiores apostas técnicas do .NET 11. Trata-se de uma infraestrutura em nível de runtime para métodos assíncronos.

Hoje, o compilador C# transforma cada método async em uma state machine em tempo de compilação. O runtime async muda essa abordagem e delega o gerenciamento de continuações diretamente ao runtime.

No modelo tradicional, o compilador gera um tipo que implementa IAsyncStateMachine e usa um builder, como o AsyncTaskMethodBuilder, para coordenar a execução. Quando o método realmente suspende em um await, esse estado precisa ir para o heap.

Além disso, cada método assíncrono acrescenta frames de infraestrutura à pilha. Por isso, o código gerado fica maior e o diagnóstico fica mais difícil do que no código síncrono equivalente.

Com o runtime async, o compilador emite métodos marcados para o runtime, que cuida da suspensão e da retomada. Na prática, o mesmo async e await que você já escreve passa a ser executado por um mecanismo nativo da plataforma.

O objetivo é reduzir o overhead em caminhos assíncronos intensos e melhorar ferramentas como stack traces e profilers. Por exemplo, a documentação oficial mostra uma pilha de chamadas que cai de 13 para 5 frames quando o recurso está ativo.

// Com <Features>runtime-async=on</Features> no .csproj
await ProcessOrderAsync();

static async Task ProcessOrderAsync()
{
    await Task.Yield();
    await SaveOrderAsync();
}

static async Task SaveOrderAsync()
{
    await Task.Yield();
    // A pilha mostra apenas os métodos reais, sem a infraestrutura da state machine
    Console.WriteLine(new System.Diagnostics.StackTrace(fNeedFileInfo: true));
}

Em outras palavras, profilers, logs de diagnóstico e a janela de call stack do depurador passam a refletir a cadeia real de chamadas.

Suporte a NativeAOT e ReadyToRun

O .NET 11 Preview 3 também inclui suporte ao runtime async para NativeAOT e ReadyToRun. Assim, o recurso deixa de ficar restrito ao código compilado pelo JIT e alcança cenários de compilação antecipada.

O ReadyToRun pré-compila o IL para código nativo e reduz o tempo de inicialização, mas mantém o JIT disponível. Já o NativeAOT gera um executável totalmente nativo, sem JIT em tempo de execução.

Além disso, houve trabalho para reutilizar objetos de continuação de forma mais agressiva. O runtime também evita salvar variáveis locais que não foram alteradas, o que reduz a pressão de alocação em código com alto volume de operações assíncronas.

Para quem publica APIs com NativeAOT, essa combinação é especialmente interessante. Afinal, menos alocações significam menos trabalho para o garbage collector em serviços com muitas requisições simultâneas.

Otimizações de JIT no .NET 11 Preview 3

O compilador JIT (Just-In-Time) do .NET recebe melhorias incrementais a cada preview, e esta versão não é exceção. Três áreas foram aprimoradas: switches, bounds checks e conversões numéricas.

Expressões switch com múltiplos alvos

Padrões comuns de switch com vários valores constantes agora viram verificações sem desvios desnecessários.

public static bool IsSmall(int x) => x is 0 or 1 or 2 or 3 or 4;

public static string Classify(int statusCode) => statusCode switch
{
    // Vários alvos constantes podem ser dobrados em checagens mais simples
    200 or 201 or 204 => "success",
    400 or 404 or 422 => "client-error",
    _ => "other"
};

Antes, esse padrão podia gerar comparações sequenciais. Com a melhoria, o JIT dobra a lógica em uma verificação mais direta, o que reduz o número de saltos condicionais no código gerado.

Para ter uma ideia, um conjunto contíguo de constantes como 0 a 4 equivale a uma única comparação sem sinal, algo como (uint)x <= 4. Esse tipo de transformação evita desvios difíceis de prever pela CPU, que custam caro quando o processador erra a previsão.

Bounds checks em índice reverso

O acesso a arrays com ^, o índice a partir do fim, já contava com a remoção de bounds checks redundantes. Agora, o .NET 11 Preview 3 amplia essa capacidade para mais padrões do código real.

public static int SumTail(int[] values)
{
    // O JIT consegue eliminar verificações de limite redundantes aqui
    return values[^1] + values[^2];
}

Vale lembrar o que é um bounds check. Sempre que você acessa values[i], o runtime confere se o índice está dentro do array e, se não estiver, lança IndexOutOfRangeException.

Quando o JIT consegue provar que o índice é válido, ele remove essa verificação. Assim, cada bounds check removido é uma comparação a menos dentro de laços quentes, e código que manipula buffers, parsers e algoritmos numéricos tende a ganhar.

Conversão de uint para float e double

Conversões de uint para float ou double ficaram mais rápidas em hardware x86 sem suporte a AVX-512.

public static double ToDouble(uint value)
{
    // Conversão mais rápida em CPUs x86 anteriores ao AVX-512
    double result = value;
    return result;
}

O motivo é técnico. Processadores com AVX-512 têm instruções que convertem inteiros sem sinal diretamente para ponto flutuante, enquanto o conjunto SSE2 só oferece a conversão de inteiros com sinal.

Por isso, em CPUs sem AVX-512, o JIT precisa gerar uma sequência alternativa para tratar o uint. O .NET 11 Preview 3 melhora justamente essa sequência, o que importa em servidores mais antigos e em instâncias de nuvem de gerações anteriores.

Por que essas melhorias importam

É importante entender que essas melhorias se acumulam. O .NET evolui o JIT de forma contínua, e cada preview entrega ganhos que beneficiam código existente sem nenhuma mudança no código-fonte.

A filosofia é clara: você não deveria reescrever código para ganhar performance. Em vez disso, o JIT identifica padrões comuns e gera código de máquina cada vez mais eficiente.

WebAssembly no .NET 11 Preview 3

A migração do Blazor WebAssembly do runtime Mono para o CoreCLR continua neste preview com adições concretas. Se você acompanha o tema, recomendo também o artigo sobre o futuro do Blazor no .NET 11.

Carregamento de payloads WebCIL

WebCIL é um formato de empacotamento de assemblies .NET adaptado para distribuição via HTTP no navegador. O .NET 11 Preview 3 adiciona o carregamento desse formato no CoreCLR para WebAssembly.

O formato existe por um motivo prático. Alguns firewalls e antivírus bloqueiam o download de arquivos .dll, então o WebCIL empacota o IL em um formato que passa por esses filtros sem problemas.

No Mono, o WebCIL já é usado há algumas versões.

A novidade agora é o suporte no CoreCLR, o que faz parte do trabalho de longo prazo para unificar a execução do .NET entre servidores e navegadores. Assim, a meta é que uma aplicação Blazor WebAssembly aproveite as mesmas otimizações de JIT e GC disponíveis no servidor.

Símbolos e stack traces melhores

Depurar aplicações WebAssembly sempre foi desafiador. Nesta versão, a qualidade dos símbolos de depuração e a precisão das stack traces no navegador melhoraram.

Isso ataca uma das queixas mais comuns da comunidade. Afinal, stack traces de código assíncrono no navegador costumavam perder o sentido a partir do primeiro await.

Marshal direto de float[] e Span

O .NET 11 Preview 3 também passa float[], Span<float> e ArraySegment<float> de forma mais direta pela fronteira com o JavaScript.

using System.Runtime.InteropServices.JavaScript;

public static partial class ChartInterop
{
    // Envia um array de floats para uma função JavaScript importada
    [JSImport("renderSeries", "chart.js")]
    public static partial void RenderSeries(float[] values);
}

Essa melhoria é relevante para computação numérica e machine learning no navegador. Nesses cenários, transferir arrays de ponto flutuante entre .NET e JavaScript costumava ser um gargalo.

Historicamente, o interop baseado em JSImport e JSExport priorizava tipos como byte, int e double em arrays e spans. Dessa forma, quem trabalhava com float precisava converter os dados antes de cruzar a fronteira, pagando cópias e alocações extras.

Breaking change do .NET 11 Preview 3 no NativeAOT

O .NET 11 Preview 3 introduz uma mudança incompatível que afeta projetos NativeAOT no Unix. Bibliotecas nativas geradas pelo NativeAOT agora seguem a convenção padrão e recebem o prefixo lib.

Ou seja, um projeto chamado MyLib, que antes gerava MyLib.so, agora gera libMyLib.so.

# Antes do Preview 3
ls bin/Release/net11.0/linux-x64/publish/MyLib.so

# A partir do Preview 3
ls bin/Release/net11.0/linux-x64/publish/libMyLib.so

Por isso, equipes que publicam bibliotecas NativeAOT em sistemas Unix precisam atualizar scripts de CI/CD, Dockerfiles e qualquer referência ao nome do arquivo gerado.

A boa notícia é que aplicações .NET que carregam a biblioteca com DllImport("MyLib") costumam continuar funcionando. Isso acontece porque a sondagem de bibliotecas nativas do .NET já tenta variações com o prefixo lib e a extensão .so no Linux.

Por outro lado, consumidores escritos em C, Python ou Go que abrem o arquivo pelo nome exato vão quebrar. Nesses casos, revise o caminho usado em dlopen ou equivalente.

Correções no .NET 11 Preview 3

Além dos recursos novos, a versão corrige problemas que afetavam a estabilidade e a correção do código gerado.

No JIT, foram corrigidos problemas em save e restore do runtime async, em if-conversion e na clonagem de return blocks. Bugs dessa classe podiam gerar comportamento incorreto em código otimizado.

Para contextualizar, if-conversion é a otimização em que o JIT troca um desvio condicional por uma instrução de seleção, como o cmov no x64. Quando essa transformação erra, o programa calcula um valor diferente do esperado, sem nenhuma exceção que denuncie o problema.

No runtime async, a equipe corrigiu um vazamento causado por race condition nos resumption stubs. Também resolveu um problema de nível de lock que podia causar deadlock sob contenção, algo crítico em aplicações com alto paralelismo.

Onde o .NET 11 Preview 3 se encaixa

As melhorias do .NET 11 Preview 3 fazem parte de um quadro maior de investimentos para a versão.

O runtime async representa a maior mudança no modelo de execução assíncrona desde a chegada de async e await no C# 5. A meta não é só performance, mas tornar o diagnóstico de código assíncrono comparável ao de código síncrono.

A migração do WebAssembly para o CoreCLR, por sua vez, é uma aposta de longo prazo. Quando concluída, aplicações Blazor WebAssembly terão acesso ao mesmo pipeline de otimização do servidor.

Já o JIT segue como investimento contínuo e incremental. Cada preview entrega ganhos que se aplicam ao código existente sem nenhuma ação do desenvolvedor.

Para ver o ponto de partida dessa jornada, leia as novidades do runtime no .NET 11 Preview 1. E, para o que veio depois, confira o runtime do .NET 11 Preview 5.

Como testar o .NET 11 Preview 3

Para experimentar as novidades, instale o SDK do .NET 11 Preview 3, disponível na página oficial de download. Em seguida, ative o runtime async em um projeto net11.0.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <!-- Ativa o runtime async sem precisar de EnablePreviewFeatures -->
    <Features>runtime-async=on</Features>
  </PropertyGroup>
</Project>

Recomendo testar em ambientes isolados antes de pensar em produção. Afinal, o .NET 11 ainda está em preview e pode trazer mudanças incompatíveis nas próximas versões.

Uma boa prática é medir antes e depois com o BenchmarkDotNet. Dessa forma, você descobre se o runtime async e as otimizações de JIT fazem diferença nos seus cenários reais.

Conclusão

O .NET 11 Preview 3 entrega avanços concretos no runtime para cenários muito usados: código assíncrono, switches, acesso a arrays e aplicações WebAssembly.

A remoção da barreira de preview no runtime async é um sinal de maturidade do recurso. Ao mesmo tempo, o suporte a NativeAOT e ReadyToRun amplia o alcance para cenários de performance crítica.

As otimizações de JIT seguem o padrão já estabelecido: ganhos transparentes, sem mudanças no código-fonte. Enquanto isso, o trabalho em WebAssembly avança de forma consistente rumo a uma execução unificada entre servidor e navegador.

Para quem acompanha o ecossistema, o .NET 11 Preview 3 confirma a direção técnica da versão. Além disso, mostra que os investimentos anunciados nos previews anteriores estão sendo entregues de forma incremental e verificável.

FAQ

O runtime async está pronto para produção no .NET 11 Preview 3?

Não. O recurso ainda depende da feature switch runtime-async=on e está em fase de preview, então não é recomendado para produção antes do lançamento oficial.

Preciso alterar meu código para aproveitar as otimizações de JIT?

Não. As otimizações de JIT são transparentes, e o código existente se beneficia automaticamente ao rodar no novo runtime.

A migração do WebAssembly para CoreCLR afeta aplicações Blazor existentes?

Por enquanto, o impacto é mínimo, porque a migração ainda está em andamento. As melhorias em WebCIL e depuração são os benefícios mais imediatos.

O que muda com o prefixo lib no NativeAOT?

Bibliotecas NativeAOT no Unix passam a seguir a convenção libNome.so. Scripts e Dockerfiles que referenciam Nome.so precisam ser atualizados.

Qual a diferença entre o .NET 11 STS e o .NET 10 LTS?

O .NET 10 LTS tem três anos de suporte, enquanto o .NET 11 STS terá 24 meses. Para produção com ciclos longos de atualização, o .NET 10 é a escolha mais conservadora.

O runtime async funciona com NativeAOT?

Sim. O Preview 3 inclui suporte ao runtime async para NativeAOT e ReadyToRun.

Quando o .NET 11 será lançado oficialmente?

O lançamento está previsto para novembro de 2026, seguindo o calendário anual do .NET.