AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79550299
Accepted
RoXTar
RoXTar
Asked: 2025-04-02 17:51:24 +0800 CST2025-04-02 17:51:24 +0800 CST 2025-04-02 17:51:24 +0800 CST

Possível vazamento de memória no meu Blob Service / Blazor Server .NET

  • 772

Adicionei um novo recurso a um dos meus aplicativos Blazor. Isso permite o upload de arquivos para o armazenamento de blobs em blocos. Isso funciona muito bem. Dei uma olhada para processar memória no Visual Studio. Onde meu aplicativo tinha o tempo todo 80 MB, com todas as suas coisas. Mas quando inicio um upload, vejo que o consumo de memória aumenta em 10-20 MB e não volta. Também aumenta a cada upload de um arquivo. Observe que eu carrego arquivos em blocos de 10 MB, antes eu tinha 100 MB e aumentou 100-200 MB. Então agora não tenho certeza se é vazamento de memória (acho que é) ou se o gc demora muito.

aqui minha função de dentro do serviço para referência

public async Task UploadFileToBlobStorage(Stream stream, string fileName, string containerName,string contentType,IProgress<long> progress,CancellationToken cancellationToken)
{
    _logger.LogInformation($"Uploading file {fileName} to container {containerName}");
    var blobServiceClient = new BlobServiceClient(new Uri($"https://{accountName}.blob.core.windows.net"), new DefaultAzureCredential());
    var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
    await containerClient.CreateIfNotExistsAsync(cancellationToken:cancellationToken);
    var blockblobClient = containerClient.GetBlockBlobClient(fileName);

    var blockIds = new List<string>();
    var buffer = new byte[10 * 1024 * 1024]; // 10 MB
    int bytesRead;
    int blockNumber = 0;
    long totalBytesRead = 0;
               

    var blobHttpHeader = new BlobHttpHeaders
    {
        ContentType = contentType,
        ContentDisposition = $"attachment; filename={fileName}",              

    };

    while ((bytesRead = await stream.ReadAsync(buffer.AsMemory( 0, buffer.Length), cancellationToken)) > 0)            
    {
        var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockNumber.ToString("d6")));
        blockIds.Add(blockId);

        using (var blockStream = new MemoryStream(buffer, 0, bytesRead))
        {
            await blockblobClient.StageBlockAsync(blockId, blockStream,cancellationToken: cancellationToken);
        }
        totalBytesRead += bytesRead;
        progress?.Report(totalBytesRead);
        blockNumber++;
    }

    await blockblobClient.CommitBlockListAsync(blockIds,httpHeaders:blobHttpHeader,cancellationToken:cancellationToken);            
    _logger.LogInformation($"File {fileName} uploaded to container {containerName} successfull");
}

O fluxo variável que é passado para a função é um ibrowserfile que foi criado usando

using var stream = _selectedFile.OpenReadStream(maxAllowedSize: long.MaxValue);

Ficarei feliz em receber dicas.

  • Alguém vê algum erro grave?
  • Talvez não haja engano e minha interpretação esteja errada?

Muito obrigado

  • 2 2 respostas
  • 90 Views

2 respostas

  • Voted
  1. Best Answer
    Guru Stron
    2025-04-02T19:33:34+08:002025-04-02T19:33:34+08:00

    À primeira vista, não encontrei um vazamento claro, mas há uma coisa que você precisa considerar: você está criando um buffer de 10 MB ( var buffer = new byte[10 * 1024 * 1024]; // 10 MB) para cada upload, o que é maior que o tamanho padrão do Large Object Heap (LOH) :

    Se um objeto for maior ou igual a 85.000 bytes em tamanho, ele é considerado um objeto grande. Esse número foi determinado pelo ajuste de desempenho. Quando uma solicitação de alocação de objeto é de 85.000 bytes ou mais, o tempo de execução o aloca no heap de objeto grande.

    Portanto, cada matriz irá diretamente para o LOH, que é coletado apenas com a 2ª geração e não é compactado por padrão:

    Como o LOH é coletado apenas durante os GCs da geração 2, o segmento LOH só pode ser liberado durante esse GC

    e

    Mas como a compactação é cara, o GC varre o LOH; ele faz uma lista livre de objetos mortos que podem ser reutilizados mais tarde para satisfazer grandes solicitações de alocação de objetos. Objetos mortos adjacentes são transformados em um objeto livre. ... Como o LOH não é compactado, às vezes o LOH é considerado a fonte de fragmentação

    Então, mesmo que a memória não seja realmente necessária (ou seja, nenhuma referência é mantida a ela), ela potencialmente ainda não será coletada/liberada por um bom tempo. É bem provável que seu aplicativo esteja sendo executado no modo GC do servidor, que pode ser bem ganancioso/"preguiçoso" e, em alguns casos, raramente pode executar a coleta de 2ª geração.

    Você pode tentar acionar manualmente a coleta e a compactação:

    GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true);
    

    Mas essa não é a abordagem recomendada em casos gerais (embora você possa usá-la se souber o que está fazendo ou para testar vazamento de memória - para ver se a memória é realmente reivindicada pelo GC).

    A primeira pergunta que você deve fazer é se esse buffer grande é realmente necessário e tente usar um buffer que se ajuste ao Small Object Heap (SOH), ou seja, menos de 85.000 bytes (eu sugeriria muito menos). Se por algum motivo essa não for uma abordagem que você possa usar - você pode considerar usar a abordagem de pooling (ou seja, ArrayPool<T>), mas ela ainda pode alocar dependendo do número de uploads simultâneos. Finalmente, você pode brincar com as configurações de GC, especialmente o Large object heap threshold , mas eu sugeriria ir com a primeira opção ainda (ou seja, usar um buffer menor).

    Passando dos comentários:

    Adicionei a writeable: falsebandeira. Essa é a chave, é ótima para servir a memória.

    • 3
  2. RoXTar
    2025-04-03T21:52:24+08:002025-04-03T21:52:24+08:00

    para leitores de acompanhamento. Acabamos com o código funcional acima, mas com uma melhoria. O código funciona como um encanto para muitos usuários que carregam bigfiles.

    public async ValueTask UploadFileToBlobStorage(Stream stream, string fileName, string containerName,string contentType,IProgress<long> progress,CancellationToken cancellationToken)
    {
        _logger.LogInformation($"Uploading file {fileName} to container {containerName}");
        var blobServiceClient = new BlobServiceClient(new Uri($"https://{accountName}.blob.core.windows.net"), new DefaultAzureCredential());
        var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
        await containerClient.CreateIfNotExistsAsync(cancellationToken:cancellationToken);
        var blockblobClient = containerClient.GetBlockBlobClient(fileName);
    
        var blockIds = new List<string>();
        var buffer = new byte[10 * 1024 * 1024]; // 10 MB
        
        int bytesRead;
        int blockNumber = 0;
        long totalBytesRead = 0;
                   
    
        var blobHttpHeader = new BlobHttpHeaders
        {
            ContentType = contentType,
            ContentDisposition = $"attachment; filename={fileName}",              
    
        };
    
        while ((bytesRead = await stream.ReadAsync(buffer.AsMemory( 0, buffer.Length), cancellationToken)) > 0)            
        {
            var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockNumber.ToString("d6")));
            blockIds.Add(blockId);
    
            using (var blockStream = new MemoryStream(buffer, 0, bytesRead,writable:false))
            {
                await blockblobClient.StageBlockAsync(blockId, blockStream,cancellationToken: cancellationToken);
            }
            totalBytesRead += bytesRead;
            progress?.Report(totalBytesRead);
            blockNumber++;
        }
    
        await blockblobClient.CommitBlockListAsync(blockIds,httpHeaders:blobHttpHeader,cancellationToken:cancellationToken);
        _logger.LogInformation($"File {fileName} uploaded to container {containerName} successfull");
    }
    

    Observe "writable:false" no fluxo de memória.

    Divirta-se muito

    • 1

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve