Hospedar e implantar aplicativos Blazor do lado do servidor

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Esse artigo explica como hospedar e implantar aplicativos Blazor do lado do servidor usando o ASP.NET Core.

Valores de configuração do host

Os aplicativos Blazor do lado do servidor podem aceitar valores de configuração de Host Genérico.

Implantação

Ao usar um modelo de hospedagem no lado do servidor, Blazor é executado no servidor a partir de um aplicativo do ASP.NET Core. As atualizações da interface do usuário, a manipulação de eventos e as chamadas de JavaScript são realizadas por uma conexão SignalR.

É necessário um servidor Web capaz de hospedar um aplicativo ASP.NET Core. O Visual Studio inclui um modelo de projeto de aplicativo do lado do servidor. Para obter mais informações sobre modelos de projeto Blazor, consulte Estrutura do projeto Blazor do ASP.NET Core.

Dimensionamento

Ao considerar a escalabilidade de um único servidor (escalar verticalmente), a memória disponível para um aplicativo provavelmente é o primeiro recurso que o aplicativo esgota à medida que as demandas do usuário aumentam. A memória disponível no servidor afeta:

  • Número de circuitos ativos aos quais um servidor pode dar suporte.
  • Latência de interface do usuário no cliente.

Para obter diretrizes sobre como compilar aplicativos Blazor seguros e escalonáveis do lado do servidor, consulte os recursos a seguir:

Cada circuito usa aproximadamente 250 KB de memória para um aplicativo mínimo de estilo Olá, Mundo. O tamanho de um circuito depende do código do aplicativo e dos requisitos de manutenção de estado associados a cada componente. Recomendamos que você meça as demandas de recursos durante o desenvolvimento para seu aplicativo e infraestrutura, mas a linha de base a seguir pode ser um ponto de partida no planejamento de seu destino de implantação: se você espera que seu aplicativo dê suporte a 5.000 usuários simultâneos, considere o orçamento de pelo menos 1,3 GB de memória do servidor para o aplicativo (ou ~273 KB por usuário).

Configuração de SignalR

As condições de hospedagem e dimensionamento do SignalR se aplicam aos aplicativos Blazor que usam SignalR.

Para obter mais informações sobre aplicativos SignalR em Blazor, incluindo diretrizes de configuração, consulte ASP.NET Core BlazorSignalR diretrizes.

Transportes

Blazor funciona melhor ao usar WebSockets como o transporte do SignalR devido à menor latência, melhor confiabilidade e maior segurança. A Sondagem Longa é usada por SignalR quando WebSockets não está disponível ou quando o aplicativo está explicitamente configurado para usar a Sondagem Longa. Ao implantar no Serviço de Aplicativo do Azure, configure o aplicativo para usar o WebSockets nas configurações do portal do Azure para o serviço. Para obter detalhes sobre como configurar o aplicativo para Serviço de Aplicativo do Azure, confira as Diretrizes de publicação do SignalR.

Um aviso do console será exibido se a Sondagem Longa for utilizada:

Falha ao se conectar por meio de WebSockets usando o transporte de fallback de Sondagem Longa. Isso pode ocorrer porque uma VPN ou proxy está bloqueando a conexão.

Falhas globais de implantação e conexão

Recomendações para implantações globais nos data centers geográficos:

  • Implante o aplicativo nas regiões em que a maioria dos usuários reside.
  • Leve em consideração o aumento da latência para o tráfego em todos os continentes. Para controlar a aparência da UI de reconexão, veja a orientação do ASP.NET Core BlazorSignalR.
  • Para hospedagem do Azure, use o Serviço do Azure SignalR.

Serviço SignalR do Azure

Para Aplicativos Web Blazor que adotam a renderização interativa do lado do servidor, considere usar o Serviço SignalR do Azure. O serviço funciona em conjunto com o Hub Blazor do aplicativo para dimensionar até um grande número de conexões SignalR simultâneas. Além disso, o alcance global do serviço e os data centers de alto desempenho ajudam significativamente a reduzir a latência devido à geografia. Se o ambiente de hospedagem já lida com essas preocupações, o uso do Serviço SignalR do Azure não será necessário.

Considere usar o Serviço SignalR do Azure, que funciona em conjunto com o Hub Blazor do aplicativo para dimensionar até um grande número de conexões SignalR simultâneas. Além disso, o alcance global do serviço e os data centers de alto desempenho ajudam significativamente a reduzir a latência devido à geografia. Se o ambiente de hospedagem já lida com essas preocupações, o uso do Serviço SignalR do Azure não será necessário.

Importante

Quando WebSockets são desabilitados, o Serviço de Aplicativo do Azure simula uma conexão em tempo real usando a Sondagem Longa HTTP. A Sondagem Longa HTTP é visivelmente mais lenta do que a execução com WebSockets habilitados, o que não usa sondagem para simular uma conexão cliente-servidor. Caso a Sondagem Longa precise ser usada, talvez seja necessário configurar o intervalo máximo de sondagem (MaxPollIntervalInSeconds), que define o intervalo máximo de sondagem permitido para conexões de Sondagem Longa no Serviço SignalR do Azure se o serviço retornar de WebSockets para Sondagem Longa. Se a próxima solicitação de sondagem não entrar em MaxPollIntervalInSeconds, o Serviço do Azure SignalR limpará a conexão do cliente. Observe que o Serviço SignalR do Azure também limpa as conexões quando armazenadas em cache aguardando para gravar o tamanho do buffer é maior que 1 MB para garantir o desempenho do serviço. O valor padrão para MaxPollIntervalInSeconds é de 5 segundos. A configuração é limitada de 1 a 300 segundos.

É recomendável usar WebSockets para aplicativos Blazor do lado do servidor implantados no Serviço de Aplicativo do Azure. O Serviço SignalR do Azure usa WebSockets por padrão. Se o aplicativo não usar o Serviço do AzureSignalR, consulte Publicar um aplicativo SignalR ASP.NET Core para Serviço de Aplicativo do Azure.

Para obter mais informações, consulte:

Configuração

Para configurar um aplicativo para o Serviço SignalR do Azure, o aplicativo deve dar suporte a sessões autoadesivas, onde os clientes são redirecionados de volta para o mesmo servidor durante a pré-geração. A opção ServerStickyMode ou o valor de configuração é definido como Required. Normalmente, um aplicativo cria a configuração usando uma das seguintes abordagens:

  • Program.cs:

    builder.Services.AddSignalR().AddAzureSignalR(options =>
    {
        options.ServerStickyMode = 
            Microsoft.Azure.SignalR.ServerStickyMode.Required;
    });
    
  • Configuração (use uma das seguintes abordagens):

    • No appsettings.json:

      "Azure:SignalR:ServerStickyMode": "Required"
      
    • A Configuração>do serviço de aplicativoConfigurações de aplicativo no portal do Azure (Nome: Azure__SignalR__ServerStickyMode, Valor:Required). Essa abordagem é adotada para o aplicativo automaticamente se você provisionar o Serviço SignalR do Azure.

Observação

O seguinte erro é gerado por um aplicativo que não habilitou sessões autoadesivas para o Serviço SignalR do Azure:

blazor.server.js:1 Erro não capturado (em promessa): invocação cancelada devido ao fechamento da conexão subjacente.

Provisionar o Serviço SignalR do Azure

Para provisionar o Serviço SignalR do Azure para um aplicativo no Visual Studio:

  1. Crie um perfil de publicação dos Aplicativos do Azure no Visual Studio para o aplicativo .
  2. Adicione a dependência do Serviço SignalR do Azure ao perfil. Se a assinatura do Azure não tiver uma instância pré-existente do Serviço SignalR do Azure para atribuir ao aplicativo, selecione Criar uma nova instância do Serviço do AzureSignalR para provisionar uma nova instância de serviço.
  3. Publique o aplicativo no Azure.

O provisionamento do Serviço SignalR do Azure no Visual Studio habilita automaticamente as sessões autoadesivas e adiciona a cadeia de conexão SignalR à configuração do serviço de aplicativo.

Escalabilidade em Aplicativos de Contêiner do Azure

O dimensionamento de aplicativos Blazor do lado do servidor nos Aplicativos de Contêiner do Azure requer considerações específicas além de usar o Serviço SignalR do Azure. Devido à maneira como o roteamento de solicitações é tratado, o serviço de proteção de dados ASP.NET Core deve ser configurado para persistir chaves em um local centralizado que todas as instâncias de contêiner podem acessar. As chaves podem ser armazenadas no Armazenamento de Blobs do Azure e protegidas com o Azure Key Vault. O serviço de proteção de dados usa as chaves para desserializar componentes Razor.

Observação

Para obter uma exploração mais profunda desse cenário e o dimensionamento de aplicativos de contêiner, consulte Dimensionamento de aplicativos ASP.NET Core no Azure. O tutorial explica como criar e integrar os serviços necessários para hospedar aplicativos nos Aplicativos de Contêiner do Azure. As etapas básicas também são fornecidas nesta seção.

  1. Para configurar o serviço de proteção de dados para usar o Armazenamento de Blobs do Azure e o Azure Key Vault, faça referência aos seguintes pacotes NuGet:

    Observação

    Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.

  2. Atualize Program.cs com o seguinte código realçado:

    using Azure.Identity;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"];
    var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"];
    
    builder.Services.AddRazorPages();
    builder.Services.AddHttpClient();
    builder.Services.AddServerSideBlazor();
    
    builder.Services.AddAzureClientsCore();
    
    builder.Services.AddDataProtection()
                    .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri),
                                                    new DefaultAzureCredential())
                    .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI),
                                                    new DefaultAzureCredential());
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapRazorPages();
    
    app.Run();
    

    As alterações anteriores permitem que o aplicativo gerencie a proteção de dados usando uma arquitetura centralizada e escalonável. DefaultAzureCredential descobre a identidade gerenciada do aplicativo de contêiner depois que o código é implantado no Azure e o usa para se conectar ao armazenamento de blobs e ao cofre de chaves do aplicativo.

  3. Para criar a identidade gerenciada do aplicativo de contêiner e conceder a ela acesso ao armazenamento de blobs e a um cofre de chaves, conclua as seguintes etapas:

    1. No Portal do Azure, navegue até a página de visão geral do aplicativo de contêiner.
    2. À esquerda, selecione Conector de serviço.
    3. Selecione + Criar na navegação superior.
    4. No submenu Criar conexão, insira os seguintes valores:
      • Contêiner: selecione o aplicativo de contêiner que você criou para hospedar seu aplicativo.
      • Tipo de serviço: selecione Armazenamento de Blobs.
      • Assinatura: selecione a assinatura que possui o aplicativo de contêiner.
      • Nome da conexão: insira um nome de scalablerazorstorage.
      • Tipo de cliente: selecione .NET e, em seguida, selecione Avançar.
    5. Selecione Identidade gerenciada atribuída pelo sistema e selecione Avançar.
    6. Use as configurações de rede padrão e selecione Avançar.
    7. Depois que o Azure validar as configurações, selecione Criar.

    Repita as configurações anteriores para o cofre de chaves. Selecione o serviço e a chave do cofre de chaves apropriados na guia Básico.

Serviço de Aplicativo do Azure sem o Serviço de SignalR do Azure

Hospedar um aplicativo Web Blazor que usa a renderização interativa do lado do servidor no Serviço de Aplicativo do Azure requer configuração para afinidade de Application Request Routing (ARR) e WebSockets. O Serviço de Aplicativo também deve ser distribuído globalmente para reduzir a latência da interface do usuário. O uso do Serviço SignalR do Azure ao hospedar no Serviço de Aplicativo do Azure não é necessário.

Hospedar um aplicativo Blazor Server no Serviço de Aplicativo do Azure requer configuração para afinidade de Application Request Routing (ARR) e WebSockets. O Serviço de Aplicativo também deve ser distribuído globalmente para reduzir a latência da interface do usuário. O uso do Serviço SignalR do Azure ao hospedar no Serviço de Aplicativo do Azure não é necessário.

Use as seguintes diretrizes para configurar o aplicativo:

IIS

Ao usar o IIS, habilite:

Kubernetes

Crie uma definição de entrada com as seguintes Anotações do Kubernetes para sessões autoadesivas:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: <ingress-name>
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
    nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"

Linux com o Nginx

Siga as diretrizes para um aplicativo SignalR do ASP.NET Core com as seguintes alterações:

  • Altere o location caminho de /hubroute (location /hubroute { ... }) para o caminho raiz / (location / { ... }).
  • Remova a configuração para buffer de proxy (proxy_buffering off;), pois a configuração só se aplica a SSE (Eventos Enviados pelo Servidor), que não são relevantes para as interações cliente-servidor do aplicativo do Blazor.

Para obter mais informações e diretrizes de configuração, confira os seguintes recursos:

Linux com o Apache

Para hospedar um aplicativo Blazor por trás do Apache no Linux, configure ProxyPass para tráfego HTTP e WebSockets.

No exemplo a seguir:

  • O servidor Kestrel está em execução no computador host.
  • O aplicativo escuta o tráfego na porta 5000.
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

Habilite os seguintes módulos:

a2enmod   proxy
a2enmod   proxy_wstunnel

Verifique se há erros de WebSockets no console do navegador. Erros de exemplo:

  • O Firefox não pode estabelecer uma conexão com o servidor em ws://the-domain-name.tld/_blazor?id=XXX
  • Erro: falha ao iniciar o transporte 'WebSockets': Error: ocorreu um erro com o transporte.
  • Erro: falha ao iniciar o transporte 'LongPolling': TypeError: this.transport é indefinido
  • Erro: não é possível se conectar ao servidor com qualquer um dos transportes disponíveis. Falha no WebSockets
  • Erro: não é possível enviar dados se a conexão não estiver no Estado 'Conectado'.

Para obter mais informações e diretrizes de configuração, confira os seguintes recursos:

Medir latência de rede

JSO interoperabilidade pode ser usado para medir a latência de rede, como demonstra o exemplo a seguir.

MeasureLatency.razor:

@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}

Para uma experiência de interface do usuário razoável, recomendamos uma latência de interface do usuário sustentada de 250 ms ou menos.

Gerenciamento de memória

No servidor, um novo circuito é criado para cada sessão de usuário. Cada sessão de usuário corresponde à renderização de um único documento no navegador. Por exemplo, várias guias criam várias sessões.

Blazor mantém uma conexão constante com o navegador, chamado de circuito, que iniciou a sessão. As conexões podem ser perdidas a qualquer momento por vários motivos, como quando o usuário perde a conectividade de rede ou fecha abruptamente o navegador. Quando uma conexão é perdida, Blazor tem um mecanismo de recuperação que coloca um número limitado de circuitos em um pool "desconectado", dando aos clientes uma quantidade limitada de tempo para reconectar e restabelecer a sessão (padrão: 3 minutos).

Depois desse tempo, Blazor libera o circuito e descarta a sessão. Desse ponto em diante, o circuito é qualificado para GC (coleta de lixo) e é reivindicado quando uma coleta para a geração de GC do circuito é disparada. Um aspecto importante a ser compreendido é que os circuitos têm um longo tempo de vida, o que significa que a maioria dos objetos com raiz pelo circuito eventualmente chega à Gen 2. Como resultado, talvez você não veja esses objetos liberados até que ocorra uma coleção Gen 2.

Medir o uso de memória em geral

Pré-requisitos:

  • O aplicativo deve ser publicado na Configuração de versão. As medidas de configuração de depuração não são relevantes, pois o código gerado não representa o código usado para uma implantação de produção.
  • O aplicativo deve ser executado sem um depurador anexado, pois isso também pode afetar o comportamento do aplicativo e estragar os resultados. No Visual Studio, inicie o aplicativo sem depuração selecionando Depurar>Iniciar Sem Depuração na barra de menus ou Ctrl+F5 usando o teclado.
  • Considere os diferentes tipos de memória para entender quanta memória é realmente usada pelo .NET. Em geral, os desenvolvedores inspecionam o uso de memória do aplicativo no Gerenciador de Tarefas no sistema operacional Windows, que normalmente oferece um limite superior da memória real em uso. Para obter mais informações, confira os seguintes artigos:

Uso de memória aplicado a Blazor

Calculamos a memória usada pelo Blazor da seguinte maneira:

(Circuitos ativos × memória por circuito) + (Circuitos desconectados × Memória por circuito)

A quantidade de memória que um circuito usa e os circuitos ativos potenciais máximos que um aplicativo pode manter dependem em grande parte de como o aplicativo é gravado. O número máximo de circuitos ativos possíveis é descrito aproximadamente por:

Máximo de memória disponível / Memória pré-circuito = Máximo de circuitos ativos potenciais

Para que ocorra um vazamento de memória no Blazor, o seguinte deve ser verdadeiro:

  • A memória deve ser alocada pela estrutura, não pelo aplicativo. Se você alocar uma matriz de 1 GB no aplicativo, o aplicativo deverá gerenciar o descarte da matriz.
  • A memória não deve ser usada ativamente, o que significa que o circuito não está ativo e foi removido do cache de circuitos desconectados. Se você tiver o máximo de circuitos ativos em execução, ficar sem memória será um problema de escala, não um vazamento de memória.
  • Uma GC (coleta de lixo) para a geração de GC do circuito foi executada, mas o coletor de lixo não conseguiu reivindicar o circuito porque outro objeto na estrutura está mantendo uma forte referência ao circuito.

Em outros casos, não há perda de memória. Se o circuito estiver ativo (conectado ou desconectado), o circuito ainda estará em uso.

Se uma coleção para a geração de GC do circuito não for executada, a memória não será liberada porque o coletor de lixo não precisará liberar a memória nesse momento.

Se uma coleção de uma geração de GC for executada e liberar o circuito, você deverá validar a memória em relação às estatísticas do GC, não ao processo, pois o .NET pode decidir manter a memória virtual ativa.

Se a memória não for liberada, você deverá encontrar um circuito que não esteja ativo ou desconectado e com raiz por outro objeto na estrutura. Em qualquer outro caso, a incapacidade de liberar memória é um problema de aplicativo no código do desenvolvedor.

Reduzir o uso de memória

Adote qualquer uma das seguintes estratégias para reduzir o uso de memória de um aplicativo:

  • Limite a quantidade total de memória usada pelo processo .NET. Para obter mais informações, consulte Opções de configuração de runtime para coleta de lixo.
  • Reduza o número de circuitos desconectados.
  • Reduza o tempo em que um circuito tem permissão para estar no estado desconectado.
  • Dispare uma coleta de lixo manualmente para executar uma coleta durante períodos de tempo de inatividade.
  • Configure a coleta de lixo no modo estação de trabalho, que dispara agressivamente a coleta de lixo, em vez do modo servidor.

Tamanho de heap para alguns navegadores de dispositivos móveis

Ao criar um Blazor aplicativo que é executado no cliente e direcionado a navegadores de dispositivos móveis, especialmente Safari no iOS, pode ser necessário diminuir a memória máxima do aplicativo com a propriedade MSBuild EmccMaximumHeapSize. Para obter mais informações, confira Hospedar e implantar Blazor WebAssembly do ASP.NET Core.

Ações e considerações adicionais

  • Capture um despejo de memória do processo quando as demandas de memória forem altas e identificar se os objetos estão tirando mais memória e onde esses objetos estão com raiz (o que contém uma referência a eles).
  • Você pode examinar as estatísticas sobre como a memória do seu aplicativo está se comportando usando dotnet-counters. Para obter mais informações, consulte Investigar contadores de desempenho (dotnet-counters).
  • Mesmo quando uma coleta de lixo é disparada, o .NET retém a memória em vez de devolvê-la imediatamente ao sistema operacional, pois é provável que ela seja reutilizada no futuro próximo. Isso evita a confirmação e a anulação constante da memória, o que é caro. Você verá isso refletido se usar dotnet-counters, pois verá os GCs acontecerem e a quantidade de memória usada cair para 0 (zero), mas não verá o contador do conjunto de trabalho diminuir, o que é um sinal de que o .NET está mantendo a memória para reutilizá-la. Para obter mais informações sobre as configurações do arquivo de projeto (.csproj) para controlar esse comportamento, consulte Opções de configuração de runtime para coleta de lixo.
  • O GC do servidor não dispara a coleta de lixo até determinar que é absolutamente necessário fazê-lo para evitar o congelamento do aplicativo e considera que o aplicativo é a única coisa em execução no computador, portanto, pode usar toda a memória do sistema. Se o sistema tiver 50 GB, o coletor de lixo buscará usar os 50 GB completos de memória disponível antes de disparar uma coleção Gen 2.
  • Para obter informações sobre a configuração de retenção de circuito desconectada, consulte ASP.NET Core BlazorDiretrizes SignalR.

Medindo a memória

  • Publique o aplicativo na Configuração de versão.
  • Execute uma versão publicada do aplicativo.
  • Não anexar um depurador ao aplicativo em execução.
  • O disparos de uma coleção de compactação forçada Gen 2 (GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true))) libera a memória?
  • Considere se o seu aplicativo está alocando objetos no heap de objetos grandes.
  • Você está testando o crescimento da memória depois que o aplicativo é aquecido com solicitações e processamento? Normalmente, existem caches que são populados quando o código é executado pela primeira vez, adicionando uma quantidade constante de memória ao volume do aplicativo.