Aspnetcore: Asp.net Core não está coletando lixo

Criado em 21 mar. 2017  ·  136Comentários  ·  Fonte: dotnet/aspnetcore

Não consigo entender por que o núcleo do Asp.net não parece estar coletando lixo. Na semana passada, deixei um serviço da web funcionar por alguns dias e meu uso de memória atingiu 20 GB. GC não parece estar funcionando corretamente. Então, para testar isso, escrevi um método web muito simples que retorna uma grande coleção de strings. O aplicativo começou usando apenas 124 MB, mas a cada vez que eu chamava o método web, o uso de memória ficava cada vez mais alto até atingir 411 MB. Teria ido mais alto se eu continuasse chamando o método da web. Mas decidi parar de testar.

Meu código de teste foi esse:

`

[HttpGet]
Tarefa assíncrona pública> TestGC() {
const string mensagem = "TESTE";
return Enumerable.Repeat(message, 100000000);
}
`

Embora eu possa estar ignorando algo... No meu entendimento, o uso de memória não deve aumentar a cada chamada para esse método. Depois que o objeto é criado e enviado para a interface do usuário, a memória deve ter sido liberada.

Como você pode ver na captura de tela abaixo, mesmo depois que o GC foi chamado, a memória não foi liberada.
netcorescreenshot

Obrigado pela ajuda!
Jay

investigate

Comentários muito úteis

Alguma novidade para isso? Estou usando o Core 2 sobre o Net Framework e isso ainda está acontecendo. Cada chamada para um _Controller_ aumentava a memória usada, mas nunca ficava inativa. (_Eu usei o modelo WebApi_)

Todos 136 comentários

@rynowak

Embora eu possa estar ignorando algo... No meu entendimento, o uso de memória não deve aumentar a cada chamada para esse método. Depois que o objeto é criado e enviado para a interface do usuário, a memória deve ter sido liberada.

A grande pilha de objetos provavelmente está mordendo você aqui. Se você alocar objetos > 85 KB de tamanho, eles serão colocados no LOH e compactados muito raramente. Consulte http://stackoverflow.com/questions/8951836/why-large-object-heap-and-why-do-we-care para obter mais detalhes (ou https://github.com/dotnet/coreclr/blob/master /Documentation/botr/garbage-collection.md#design-of-allocator se você quiser ir mais fundo).

Não consigo entender por que o núcleo do Asp.net não parece estar coletando lixo. Na semana passada, deixei um serviço da Web funcionar por alguns dias e meu uso de memória atingiu 20 GB

O que seu serviço está fazendo para criar objetos tão grandes? Você deve tentar fazer um despejo de memória antes que fique muito grande, isso mostrará claramente quais objetos estão permanecendo e por que estão sendo mantidos (você pode usar o visual studio para ver despejos ou uma ferramenta mais avançada como windbg ou perfview).

Tente alocar a matriz desde o início em vez de chamar Enumerable.Repeat
ou compacte a memória usando GCSettings.LargeObjectHeapCompactionMode (suportado no .Net Standard)

Obrigado @davidfowl e @MhAllan pelas respostas. Mas este exemplo foi inventado. Eu só queria algo que usasse uma quantidade notável de memória para que eu pudesse tirar uma captura de tela. A verdade é que isso está acontecendo com qualquer aplicativo independente do tamanho do objeto em questão. E para responder sua pergunta @davidfowl , meu serviço estava apenas puxando alguns dados do banco de dados com dapper e retornando o objeto resultante. Era uma linha de dados para cada chamada. Por isso, levou alguns dias para a memória crescer para esse valor. Na verdade, eu estava tentando testar o banco de dados quando me deparei com essa peculiaridade. Eu escrevi um pequeno aplicativo de console e continuei chamando o método repetidamente.

@zorthgo tem certeza que não é Dapper? Se você criar seus scripts injetando parâmetros diretamente em seus scripts SQL, como no PHP, você acabará com muitos scripts SQL em cache. Aqui está como Dapper faz isso
https://github.com/StackExchange/Dapper/blob/fe5c270aceab362c936456087a830e6fe1603cac/Dapper/SqlMapper.cs
Você deve usar um criador de perfil de memória para saber o que está mantendo as referências à memória alocada. O Visual Studio 2017 deve ser capaz de ajudá-lo a tirar alguns instantâneos da memória antes e depois de várias chamadas para seu aplicativo e compará-los.

@zorthgo Sim, eu também vi isso. Estava usando o servicestack no meu aplicativo de console .net core e toda vez que faço uma chamada para a API, o uso de memória aumenta em grandes quantidades de 50 MB. Eu assumi que era um bug do VS2017, mas confirmei o alto uso no gerenciador de tarefas. Como zorthgo afirmou, apenas fazendo chamadas simples para a api, o uso de memória aumenta significativamente e não parece liberar memória.

Isso só acontece no ASP.NET Core no .NET Core ou também é um problema com o ASP.NET Core no .NET Framework?

Você pode escrever um aplicativo semelhante usando MVC 5 (no System.Web) e verificar se não vê o mesmo comportamento?

Não consigo fazer nenhum progresso com relação a esse problema em seu estado atual.

Na minha instância, eu estava usando apenas o framework de destino .NetCoreApp 1.1 e meu aplicativo de console estava referenciando um modelo de objeto em um projeto compartilhado.

Não tenho certeza se isso vai ajudar alguém. um aplicativo de exemplo que chama hellorequest. Neste aplicativo, a memória de inicialização é de 85mb, então, por solicitação repetitiva, consegui aumentar o uso de memória para cerca de 145mb. Às vezes cai para 125mb, mas depois fica lá. Não tenho certeza se esse é um comportamento normal, pois não estou acostumado a aplicativos de console .Net Core. eu sempre assumi que estava fazendo algo errado ou não instanciando corretamente.

https://drive.google.com/open?id=0B0Gm-m-z_U84TU5keWFTMzc1ZWc

Enfrentando o mesmo problema aqui em um aplicativo Asp.Net Core implantado em produção com 3000-5000 usuários ativos.. a memória no servidor aumentou para 15 GB ontem... tive que configurar o IIS para reciclar o AppPool a cada 3 horas enquanto ainda tente descobrir qual é o problema.

Alguém fez um dump de memória e olhou o que está ocupando toda a memória em seus aplicativos específicos?

@davidfowl @Pinox
Estou trabalhando em uma grande empresa e queremos iniciar um novo projeto com ASP.NET Core, mas quando vi esse problema, fiquei com medo :worried: . esta é uma questão crítica e pode bloquear o ciclo de vida do nosso projeto.

Então, por favor, está relacionado ao ASP.NET Core ou .NET Core (CoreCLR)? teremos como alvo Full .NET (4.6) por isso estou perguntando.

@ikourfaln no meu caso eu estava usando .net core console app, servicesstack (.net core version) e kestrel. O estranho é que o uso de memória sobe para um nível, depois para de repente e não sobe novamente. Eu acho que o melhor é testá-lo do seu lado com uma pequena amostra e verificar o comportamento.

Talvez @zorthgo possa verificar do seu lado se ele vê um comportamento semelhante nessa memória usada até um certo nível e depois para de aumentar, pois esse é o comportamento que estou vendo. Atualizei meu aplicativo de exemplo para incluir o exemplo @zorthgo e não vejo a memória se esgotando. Ele sobe, mas acaba parando.

Mudei um pouco a fonte:

objeto público Qualquer (solicitação TestGC)
{
const string mensagem = "TESTE";
return Enumerable.Repeat(message, 100000);
}

@Pinox
Obrigado, vou verificar o comportamento do meu lado.

Que tal esse bug em 2.0?

Alguma novidade para isso? Estou usando o Core 2 sobre o Net Framework e isso ainda está acontecendo. Cada chamada para um _Controller_ aumentava a memória usada, mas nunca ficava inativa. (_Eu usei o modelo WebApi_)

Oi,

Eu tenho o mesmo problema com o ASP.NET Core 2. Fiz um despejo de memória e tentei analisar. Pelo que vejo, o problema é exatamente como o OP disse. Meu aplicativo começa com uma alocação de cerca de 75 MB, e muito rapidamente vai até ~750 MB, dos quais 608 MB são "memória não utilizada alocada para .NET".

Primeiro instantâneo no início do aplicativo:
image

Segundo instantâneo após 3 minutos e 100 solicitações:
image

também estamos enfrentando o mesmo problema, nosso controlador está lidando com uma grande quantidade de dados (que é um design ruim e será substituído em breve), cada chamada para esse controlador está causando o crescimento da memória. A memória reduz, mas apenas 40-50% (ganho de 50 Mb, reduz 30-35 Mb), cada chamada aumenta a memória na faixa de 10-15 Mb de cada vez. O serviço é hospedado dentro da malha de serviço.

Parece que tenho um problema semelhante em nosso serviço de produção (20-100 req/s) usando uma combinação de:

  • ASP.NET Core 2.0.4
  • ServiceStack.Core 1.0.44
  • Skia Sharp 1.59.2
  • Docker v17.09.0-ce, compilar afdb6d4 no Ubuntu 16.04 x64
  • Servidores x4 @ 40 CPUs, 128 GB de memória
  • Servidor GC é verdadeiro
  • cada contêiner do Docker é definido para compartilhamentos de CPU de 12k (mhz), 8 GB de RAM

A aplicação tem um servidor web front-end e um trabalhador (mostrados respectivamente nos gráficos abaixo).

servidor web (últimas 6h)
screen shot 2018-01-13 at 1 06 24 pm

trabalhador (últimas 6h)
screen shot 2018-01-13 at 1 07 55 pm

Ambos fazem uso de matrizes de bytes grandes porque o serviço está agindo como um proxy de armazenamento de objetos e, consequentemente, coloca objetos no LOH. Minha pergunta é, esta é uma limitação conhecida do .NET Core no momento? Parece que o LOH nunca foi totalmente limpo ou fragmentado.

Dito isto, o SOH parece estar funcionando bem, pois os objetos típicos da API da Web são limpos. Alguma sugestão? Há algum problema com minha configuração? Analisei o código e não consigo encontrar nenhum vazamento de memória gritante e não estou usando nada de especial fora da biblioteca ServiceStack.

@sebastienros - alguma opinião sobre isso? Observamos algum comportamento semelhante em nossos sistemas?

  • Nós medimos isso apenas em 2.1, vou tentar adicionar a mesma coisa para 2.0
  • Todos os comentários parecem estar relacionados ao problema de LOH que foi mencionado, que deve ser levado em consideração na aplicação e agrupar grandes arrays o máximo possível

@sebastienros , várias perguntas:

  1. Eu usei o Ants profiler para medir o uso de memória, de acordo com ele, nenhuma fragmentação de LOH foi detectada. Você pode aconselhar como posso verificar se meu aplicativo sofre de problemas de fragmentação de LOH?
  2. Quais são os resultados no .net core 2.1? O problema foi resolvido porque o Kestrel está usando o Span?
  3. E se não pudermos agrupar matrizes - você pode fornecer uma solução alternativa? Devemos usar GCSettings.LargeObjectHeapCompactionMode.CompactOnce?

Quais são os resultados no .net core 2.1? O problema foi resolvido porque o Kestrel está usando o Span?

Pessoalmente, não vimos nenhuma evidência de que o problema esteja em Kestrel. Ainda parece um problema de aplicativo.

E se não pudermos agrupar matrizes - você pode fornecer uma solução alternativa? Devemos usar GCSettings.LargeObjectHeapCompactionMode.CompactOnce?

@jkotas @Maoni0 Algum conselho aqui?

como posso investigar se sofro do mesmo problema? o perfilador de memória redgate de acordo com o LOH é quase vazio como o @sinapis descreve, mas ainda usando mais de 1 gb para apenas um usuário

Colete o rastreamento e analise-o usando o perfview. Há vários tutoriais de Vance e outros sobre como rastrear vazamentos de memória .NET: https://www.bing.com/search?q=.NET%20memory%20leak%20perfview .

https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md tem instruções específicas do Linux para coletar rastreamentos.

Se você acredita que não há vazamento de memória e o GC está apenas mantendo mais memória do que você gostaria, você pode tentar:

  • Usando os limites de memória do objeto de trabalho do Windows ou do contêiner docker para limitar a quantidade de memória que o processo pode usar. O GC leva em consideração esses limites e funciona de forma mais agressiva quando está próximo deles.
  • Ou, alternando do GC do Servidor para o GC da Estação de Trabalho. Não é incomum que o GC do servidor tenha um conjunto de trabalho de pico muito mais alto. A estação de trabalho tem um conjunto de trabalho de pico mais baixo, mas também uma taxa de transferência mais baixa.

Oi,

Acho que estou tendo o mesmo tipo de problema com uma API Web .Net Core em produção.

O aplicativo está sendo executado no Windows Server 2016 com .Net Core 2.0.3. A máquina é uma VM Hyper-V com 28 núcleos de CPU e 24 GB de RAM. Se não reciclarmos o pool de aplicativos do IIS com frequência suficiente, eventualmente usaremos toda a memória disponível. Quando o aplicativo começa a usar muita memória (>=95% da memória total do sistema), o uso da CPU também aumenta fortemente (de 2% a 70% às vezes). Não tenho certeza se uma exceção OOM é acionada ou não, sempre reciclamos o pool antes que isso aconteça (o uso máximo de memória que vi foi de 98% da memória usada pelo dotnet.exe).

Analisando um dump de memória de produção com ".Net Memory Porfiler" (SciTech Software), aqui está o que encontrei:
image

Se esta análise estiver correta, cerca de 95% da memória está em "sobrecarga > não utilizada". Aqui está como este editor de perfil de memória está descrevendo esta categoria (em seu fórum):
_"Overhead->Unused" é a memória confirmada pelo tempo de execução do .NET para o heap gerenciado. Ele não está sendo usado no momento, mas está disponível para futuras alocações de instâncias. Existem muitas regras que o tempo de execução usa para decidir se deve manter a memória confirmada ou liberá-la para o sistema operacional. Depende de fatores como memória disponível, padrões de alocação, número de processadores, se o servidor GC é usado, etc._

@jkotas Vou aplicar suas recomendações (objeto de trabalho do Windows e alternar para a estação de trabalho GC) e informarei o resultado. Por favor, deixe-me saber se eu posso extrair qualquer outra informação útil dos despejos de memória de produção que tenho.

Obrigado

@sinapis @ronald7
Algum de vocês seria capaz de compartilhar um aplicativo que mostra o problema? Se eu pudesse reproduzi-lo, poderíamos encontrar o motivo, ou pelo menos remover parte do código peça por peça e isolar uma reprodução mínima.

@sebastienros Não posso compartilhar o aplicativo, mas posso compartilhar a sessão de PerfView session + memory dump .
Alguma descrição: Eu tenho uma API Web ASP.NET Core 2, criei um teste de carga de 200 usuários, todos enviando a mesma solicitação em 10 segundos. No total, foram processados ​​775 pedidos.

Este aplicativo saltou para quase 1 GB de uso de memória no gerenciador de tarefas e ficou assim. Olhando para o dump, posso contar cerca de 18 MB:

image

Então a questão é onde foi parar quase 1 GB?

@sinapis Obrigado

O comportamento que você está descrevendo não é inesperado, o GC alocará alguma memória conforme necessário no pico de carga e apenas a liberará com o tempo. É o modo GC Server e geralmente espera por períodos ociosos para liberá-lo e não afetar o desempenho do seu aplicativo. A quantidade de memória que irá reservar depende da memória total disponível no sistema.

Definitivamente veríamos um problema se continuasse aumentando. Suponho que, se você não enviar mais solicitações e deixar seu aplicativo em execução, verá o uso de memória diminuindo.

Você poderia executar a mesma coisa até consumir a maior parte da memória do sistema? Ou pelo menos o suficiente com a mesma carga para mostrar que cresce continuamente? Ainda vou dar uma olhada nos seus dumps atuais.

Você também pode fazer dumps durante e no final dos trabalhos, para que possamos ver os detalhes.

Olá @sebastienros

Infelizmente não posso compartilhar o app nem os despejos de memória, mas vou criar um aplicativo fictício (com a mesma arquitetura e dependências), executá-lo na mesma máquina, se conseguir reproduzir esse comportamento compartilho com vocês. Por favor, deixe-me saber se há alguma informação útil que eu possa extrair para você dos despejos de memória.

Atualizei o modo GC de _server_ para _workstation_ em um servidor de produção, informarei em algumas horas se isso mudar alguma coisa no uso da memória.

Também fiz outro teste: estamos rodando nossa aplicação atrás de um balanceador de carga, em 4 máquinas virtuais. Depois de remover uma das máquinas do pool do balanceador de carga, a memória usada pelo dotnet.exe não diminuiu e permaneceu no mesmo nível mesmo após 30 minutos. (No entanto, o aplicativo ainda estava processando algumas solicitações: uma solicitação enviada pelo SCOM em um endpoint fictício a cada 30 segundos). Nenhuma memória foi liberada e devolvida ao sistema.

Obrigado

@sinapis Eu olhei para o seu rastreamento ETW. é intrigante para mim - você sobreviveu muito pouco no último GC gen2 induzido, mas ainda assim optamos por manter tanta memória comprometida. seu aplicativo parece um caso extremo (na maioria das vezes você fez apenas GCs em segundo plano devido a alocações de LOH) - gostaria de saber se temos alguns erros de contabilidade lá (outra possibilidade são erros nos números relatados, mas se você já verificou que está muito comprometido, isso é um menor possibilidade). se você puder reproduzir algo que possa compartilhar comigo, seria fantástico; caso contrário, se for possível executar seu aplicativo com algum log do GC (posso fornecer um commit que faça isso), também seria útil.

@Maoni0 , por favor, compartilhe como devo habilitar o registro do GC
Se houver outros dados que você gostaria que eu fornecesse para refutar o erro contábil, por favor, deixe-me saber o que devo fornecer a você e como (talvez diga ao perfview para coletar mais dados?)
Vou tentar criar uma reprodução mínima, mas não tenho certeza se vou conseguir, pois não sei onde está o problema.

@sebastienros espero fornecer outro dump com mais consumo de memória hoje

Olá @sebastienros @Maoni0 ,

Executei nosso aplicativo com o modo GC da estação de trabalho por 12 horas, mas o mesmo resultado. Também recompilei a aplicação com o .Net 2.1 Preview 2 em um único nó de produção por 1 hora, avisarei o resultado, mas por enquanto o processo já está usando 2GB+ de RAM.

image

Eu tenho o PerfView em execução nesta mesma máquina e estou coletando dumps do GC, existe um endereço de e-mail para onde eu possa enviar o link do OneDrive, infelizmente não posso compartilhá-lo diretamente neste tópico.

Se puder ajudar, também posso coletar mais métricas ou logs de GC. Obrigado

@ronald7 _redacted_ posso encaminhar para @Maoni0

Oi @sebastienros @Maoni0 Acabei de lhe enviar um email com dois PerfView gcdump e um arquivo VMMap, espero que isso possa ajudar. Do meu lado, ainda estou tentando reproduzir esse comportamento de alto uso de memória com um aplicativo fictício.

Obrigado!

Também estou passando pelo mesmo problema. A coleta de lixo nunca acontece! A captura de tela mostra o uso de memória depois de fazer cerca de 50 solicitações usando um aplicativo de API da Web dotnet core bastante simples.

memory-profile2

Acabei de atualizar um aplicativo ASP.NET Core em execução no Ubuntu 16.04 de 1.1 para 2.0 e encontrei esse problema. É bastante grave, fazendo com que o kernel mate o aplicativo com frequência devido a erros de OOM, e estou pensando em fazer o downgrade de volta para 1.x. Existem certas páginas que não podemos carregar - mesmo após uma reinicialização do Kestrel, o aplicativo esgota imediatamente a memória disponível após uma única solicitação! Pensei em atualizar o servidor, mas com base nos comentários aqui sobre aplicativos ASP.NET Core usando toda a memória disponível, não espero que ajude. Nossa pilha é basicamente ASP.NET MVC Core + EF Core... nada muito extravagante. Se eu tiver algum tempo, tentarei criar uma amostra para reproduzir o problema - acho que não deve ser tão difícil, dada a simplicidade de nossa pilha.

FWIW, o sistema que eu atualizei também tem um aplicativo de console .NET Core, e isso não parece ter nenhum problema de memória após a atualização 2.0, então isso definitivamente parece ser um problema relacionado ao ASP.NET Core.

@danports você tentou chamar GC.Collect() para ver se o uso de memória cai drasticamente? isso nos daria uma pista por onde deveríamos começar. se GC.Collect() (ou a sequência GC.Collect/GC.WaitingForPendingFinalizers/GC.Collect) não for capaz de fazer com que o uso de memória diminua drasticamente, significa que há simplesmente tanta memória que precisa estar ativa para que o GC não possa recuperá-la.

@Maoni0 Ainda não tentei isso. Não acho que meu problema seja com o GC, porque vi o uso de memória cair de tempos em tempos - parece que meus aplicativos .NET Core 2.0 consomem aproximadamente 2-3x a memória que consumiam em comparação com quando estavam sendo executados no . NET Core 1.1. 😞

Fiz downgrade de volta para o .NET Core 1.1 por enquanto e revisitarei isso mais tarde quando tiver mais tempo, provavelmente após o lançamento do .NET Core 2.1. (Eu me deparei com uma pilha de problemas com o 2.0 e este foi apenas um deles.)

GC.Collect() não ajuda. Tentei uma API da Web ASP.NET Core 2.0 e 2.1 muito simples que possui um controlador que retorna um dicionário de 200k ints. A memória alocada continua aumentando a cada solicitação, mesmo que o aplicativo não use mais memória.

@Serjster retornando 200K inteiros (4B) levaria 800KB. Nesse caso, você está atingindo o problema explicado neste comentário: https://github.com/aspnet/Home/issues/1976#issuecomment -289336916

Nesse caso, você deve usar um conjunto de arrays para reutilizá-los nas solicitações.

Também é bom saber que, se o código estiver sendo executado no modo de 64 bits, as matrizes/listas etc. que contêm ponteiros terão o dobro do tamanho em comparação com 32 bits. Se bem me lembro, o framework completo executa qualquer código de CPU de 32 bits no sistema operacional de 64 bits por padrão. Portanto, as pessoas que migram seu código podem acidentalmente atingir problemas de LOH.

Estou trabalhando com @Serjster e aqui está o que encontrei. Se eu criar um projeto de API web vanilla usando o asp.net core (usei 2.1 no meu último teste), noto que quando executo a ferramenta de diagnóstico (ou até mesmo verifico a memória de trabalho do processo definida no código), o número de bytes retorna continua subindo quando atingi um ponto final. Por exemplo, se eu tiver um único ponto de extremidade de API da Web retornando um Dicionáriocom 20.000 itens, acontece o seguinte:

  1. A primeira visita ao método do controlador coloca a memória do processo em 83 MB.
  2. Aguardo alguns segundos e, na segunda visita, ele passa para 86 MB.
  3. Aguardo alguns segundos e a terceira visita passa para 90 MB.
  4. Novamente - 94 MB.
  5. Eu faço isso n vezes, e finalmente chega a cerca de 304 MB. Uma vez que ele faz isso, ele se nivela.

Se o objeto de retorno for um objeto de tamanho diferente, todos os números acima são apenas maiores/menores (incluindo a quantidade de nivelamento), mas o padrão de crescimento é o mesmo (ou seja, ele crescerá e crescerá até nivelar após muitas solicitações) .

Se eu adicionar GC.Collect na chamada do método (assim ocorre em todas as requisições, o nível de é muito menor, mas ainda há um período de crescimento até que se estabilize.

O outro ponto interessante de detalhe é o número de objetos e o tamanho do heap ao fazer snapshots que permanecem praticamente inalterados a cada visita. Mas o gráfico Process Memory continua mostrando um número cada vez mais alto (isso também é verdade se você pegar o processo e extrair o valor definido da memória de trabalho).

Estou começando a suspeitar que o gráfico está mostrando a memória alocada (e essa memória cresce com base em alguma lógica de previsão de uso/demanda do núcleo asp.net), mas isso não é necessariamente memória consumida/vazada. Eu não sei o suficiente para confirmar, então me pergunto se alguém mais experiente pode entrar na conversa.

EDIT - re @davidfowl comentário: Em relação ao seu comentário sobre as coisas serem coletadas raramente ... isso pode fazer sentido. Mas quanto tempo normalmente demora? Eu posso ir mais de 30 segundos entre as solicitações, e o GC nunca parece diminuir esse número de memória no gráfico de diagnóstico. Tenho certeza de que sou ignorante em algo aqui, mas apenas curioso.

EDIT 2 - Agora que li o link SO que David postou acima com mais detalhes, estou começando a pensar que esse é definitivamente o problema que estamos vendo. Se estivermos executando em um ambiente com memória limitada (que estamos em nosso ambiente de desenvolvimento, onde estamos vendo isso porque estamos sendo baratos), teremos problemas com isso.

Editar 3 - Uma pergunta persistente. Por que a memória do processo está subindo consistentemente, mas o tamanho do heap não está subindo se esse for um problema de LOH? Na verdade, eu posso entender isso agora. O heap é a memória usada. A memória alocada pelo processador é a memória usada mais os blocos de memória fragmentados que não são usados.

@RemyArmstro você pode alterar Dictionary<int, int> para SortedDictionary<int, int> ? O dicionário provavelmente está alocando memória contínua, pode até adicionar alguns dados extras a cada entrada. Como o SortedDictionary é implementado, ele fará muitas pequenas alocações em vez de uma grande.

Edit: Se você serializar para string e não diretamente para o Stream de saída de resposta, isso também poderá causar alocações de LOH.

@wanton7 Sua resposta está perdendo o ponto. O dicionário é apenas a ponta do iceburg. Podemos usar listas, arrays, etc. etc. e todos eles fazem a mesma coisa. No entanto, como foi apontado, se o LOH está causando isso, como parece, esse comportamento provavelmente está bem? Exceto que isso pode ter alguns efeitos colaterais preocupantes, como o que acontece quando você fica sem memória? Seu aplicativo simplesmente trava?

@Serjster ok, pensei que você tinha apenas pequenos casos em que isso está acontecendo. Para mim, é muito incomum ter grandes listas, arrays como este e enviar tantos dados em uma chamada de API se não for binário. Normalmente, quando você tem algum tipo de API da Web e obtém alguns dados dela, usa a paginação. Você não deve enviar 10.000 entradas para o lado do cliente.
Mas se você tiver muitos problemas como esse e não houver como alterar como sua API funciona, acho que você deve criar suas próprias implementações de Lista e Dicionário em partes. Se você realmente usa arrays desse tamanho, pode substituí-los por suas listas em partes ou tentar agrupá-los quando o aplicativo for iniciado.

Eu gostaria que a Microsoft criasse implementações em partes que todos pudessem usar em situações como essa.

@wanton7 mais uma vez você está perdendo o ponto. Não importa o tamanho da lista. Mesmo um único item ou uma pequena lista faz com que esse problema aconteça.

@Serjster talvez eu seja apenas cego, mas não vejo nenhuma postagem sua em que você disse que enviar um único item ou uma pequena lista fará com que isso aconteça. Você excluiu?

Ou de @RemyArmstro Ele fala sobre dicionários de tamanhos diferentes. Eu verifiquei o corefx e o dicionário irá alocar array ou estes

private struct Entry
{
  public int hashCode;    // Lower 31 bits of hash code, -1 if unused
  public int next;        // Index of next entry, -1 if last
  public TKey key;           // Key of entry
  public TValue value;         // Value of entry
}

A alocação de 85.000 bytes causará a alocação de LOH, portanto, o Dicionário com capacidade de 5313 entradas de chave int e valor int causará alocação de LOH. A capacidade não é igual ao número ou às entradas, a capacidade parece ser expandida por números primos, verifique o método de Redimensionamento privado do Dicionário privado. Cada struct pode ter alocação extra mais preenchimento de memória, portanto, entradas ainda mais baixas podem causar alocação de LOH.

Detalhes da implementação do dicionário Dictionary.cs

Editar: URL fixo

@wanton7 Obrigado. Acho que percebemos qual é o problema agora. É apenas uma chatice que não há solução ótima + simples para isso. Basicamente, tudo se resume a estar mais ciente disso e ajustar como você escreve o código. A desvantagem é que essa memória não gerenciada começa a parecer um pouco mais gerenciada. :( No final, podemos ter apenas algumas áreas que realmente violam esse limite de alocação, mas uma das áreas é muito importante para nosso aplicativo, então vemos muito isso atualmente. Acho que só precisamos repensar essa parte, monitore e tente descobrir quaisquer outras áreas em que notamos essa rasteja. Obrigado novamente!

Na verdade, temos uma situação semelhante em breve e precisamos criar uma implementação IList<T> em partes. Vou usar algum tamanho para pedaços que são bitshiftable para que eu possa usar apenas bitshift e mask para indexação.

Gostaria de saber qual é mais benéfico para GC, pedaço maior ou pedaço menor? De tamanhos entre 1KB e 64KB. Pedaços menores significam mais referências para GC, mas eu acho que um pedaço maior pode ser pior para compactação e fragmentação.

seu entendimento está exatamente correto - eu usaria tamanhos não muito grandes; provavelmente tente 4k/8k.

@Maoni0 obrigado!

Eu escolhi 4KB, para que não tenhamos nenhuma surpresa desagradável se rodarmos nosso código em Mono. Descobri lendo http://www.mono-project.com/docs/advanced/garbage-collector/sgen/working-with-sgen/ que o limite de LOH é de apenas 8000 bytes no Mono.

Existe algum progresso para esta questão?

Estamos observando o mesmo problema em que a memória do processo continua a crescer apesar do tamanho do heap permanecer o mesmo (ou diminuir).

@sebastienros você pode dar outra olhada nisso? Talvez possamos fornecer algumas instruções detalhadas para ajudar as pessoas a investigar melhor seus cenários?

Aqui estão alguns itens a serem considerados com a nossa situação:

  • Estamos retornando apenas um objeto Dictionary<int, int> com 1.000 valores inteiros armazenados. Nada mais do que isso.
  • Convertemos nosso projeto do .NET Core 2.0 --> 2.1
  • Estamos vendo esse problema no MS Visual Studio 2017

Código da seguinte forma:

    public async Task<IActionResult> SampleAction()
    {
        var list = new Dictionary<int, int>();
        for (int n = 0; n < 1000; n++) list.Add(n, n);
        return Ok(list);
    }

Para reproduzir você deve simular alguma forma de carga moderada. Acabamos de clicar rapidamente usando o Postman e pudemos observar esse comportamento. Quando paramos de simular a carga, observamos o tamanho do heap diminuir, mas a memória do processo permaneceu a mesma (mesmo quando forçamos o GC).

Estou vendo isso também em um dos meus projetos, mas também posso recriá-lo em uma nova API .net core direcionada ao .Net Core 2.1 (SDK 2.1.302).

Anexei o projeto de API de exemplo que criei usando o Visual Studio 15.8.0 Preview 4. Para mostrar o aumento de memória, fiz com que um aplicativo de console .net core atingisse os valores padrão GET endpoint a cada meio segundo para obter as 2 strings. O uso da memória do processo é lento, mas em um projeto que retorna mais dados, isso pode aumentar rapidamente.

screenshot
WebApplication1.zip

Encontrei este post sobre troca de pilha:

https://stackoverflow.com/questions/48301031/why-doesnt-garbage-collector-in-net-core-2-0-free-all-memory

Alguém perfilou seus aplicativos no modo de lançamento para ver se esse comportamento está presente? Vou dar uma chance hoje e ver se o problema persiste.

Edit : tentei criar perfil no modo de lançamento e o problema ainda persiste. Eu até forço um GC para ver se isso terá algum efeito.

image

@chrisaliotta obrigado por linkar para esse post, eu não sabia desse comportamento. Seria realmente interessante ver se isso explica o que as pessoas estão vendo.

@Eilon @chrisaliotta Obrigado, mas isso não está relacionado a esta discussão. .NET não liberar memória no modo de depuração é um comportamento bem conhecido e é por isso que medimos apenas o consumo de memória (e possíveis vazamentos) no modo de liberação. Além disso, mesmo no modo de liberação, você verá a memória aumentar até certo ponto por causa do modo Server GC. Portanto, este exemplo não prova nada por duas razões diferentes.

@sebastienros Então, o comportamento que @beef3333 e eu estamos observando é consistente com o que se espera? Ou seja, onde os bytes privados permanecem elevados apesar da diminuição do tamanho do heap? Nesse caso, parece-me estranho que cada solicitação incremental continue a fazer com que os bytes privados cresçam, apesar de haver espaço de heap livre.

No modo de depuração sim. Então, por favor, tente usar o modo de liberação e deixe seu estresse por um longo tempo. Se a memória aumentar indefinidamente, há um vazamento de memória. Se a memória for reciclada (mesmo que exija uma quantidade significativa de memória), tudo estará bem no seu caso.

Acabei de testar novamente usando o mesmo projeto que anexei no modo Release e estou vendo o mesmo comportamento exato.

Vou testar sua aplicação então, obrigado.

Executei o aplicativo fornecido pelo @beef3333 localmente por 2 horas, com taxa de 5K RPS, e a memória está estável (variação de 1 MB no geral, a 400 MB em uma máquina com 32 GB). O GC é corretamente chamado regularmente. Também inspecionei vários despejos ao longo do tempo e as várias instâncias são criadas e coletadas conforme o esperado. Estou usando 2.1.1.

@sebastienros Obrigado por investigar isso. Acho que a sacada disso tudo é que:

  • O gerenciamento de memória se comportará de maneira diferente para um aplicativo Web .NET Core em relação ao seu aplicativo de desktop comum.
  • Os desenvolvedores devem se concentrar no consumo médio de memória como solicitações de função por segundo (RPS) durante um período de tempo.
  • O crescimento em bytes privados nem sempre pode ser indicativo de vazamentos de memória.

Corrija-me se estiver errado, mas parece que o .NET Core aumentará a memória alocada com base na média de solicitações para garantir o tempo de resposta mais rápido? Se for verdade, seria seguro assumir que é provável que não libere essa memória alocada até que o pool de aplicativos seja redefinido - ou liberará essa memória alocada ao longo do tempo se o RPS diminuir?

Mesmo problema.
Eu tenho um serviço de back-end webapi asp.net.
As pilhas são asp.net mvc, autofac, automapper, castle.dynamicproxy, entity framework core.
Toda a memória será consumida e o serviço falhará.

A versão é 2.1.0

@atpyk Atualização para 2.1.1. Se isso não ajudar, você deve realmente traçar o perfil do que está mantendo essa memória. Eu usei https://www.jetbrains.com/dotmemory/ mas provavelmente existem outras ferramentas que também podem fazer isso. Ele pode mostrar o que está realmente alocado no Large Object Heap (LOH).

Você está rodando no modo 32 bits? Porque as alocações de heap de objetos grandes (maiores que ~ 85.000 bytes) podem causar exceções de falta de memória no modo de 32 bits devido à fragmentação. Você pode ultrapassar este limite muito facilmente usando o Dicionário. Verifique este comentário https://github.com/aspnet/Home/issues/1976#issuecomment -393833505

Se você estiver executando seu código em .Net Framework completo, o comportamento padrão é executar qualquer código cpu no modo de 32 bits. Você precisa desmarcar Preferir 32 bits nas configurações do projeto ou definir alguma configuração de registro no registro do servidor para o padrão de 64 bits.

@wanton7 Muito obrigado. Vou tentar suas soluções.

Atualizei para 2.1.2 e implantei no aplicativo da Web do Microsoft Azure com win-x64, mas sem efeito. @wanton7
dotnetcorememory

@atpyk , crie um instantâneo de memória (dump_ e analise-o (Visual Studio, MemoScope) para ver qual objeto está ocupando toda a memória ou apenas aumentando a contagem. Você também pode tirar dois e compará-los ao longo do tempo.

@sabastienros Eu acredito que pessoas suficientes levantaram uma preocupação sobre isso que você/MS deveria realmente começar a analisar isso você mesmo. Talvez isso esteja funcionando como você pretendia, mas o design é falho.

Nossos aplicativos estão ficando sem memória e travando, e tudo isso sendo executado em um ambiente de produção do Azure. Isto não é aceitável.

@atpyk então soa como um vazamento de memória. Perfile sua memória para ver o que está mantendo essa memória como disse @sebastienros .

Uma pergunta, você está mesmo usando ASP.NET Core? Eu li seu primeiro comentário novamente e você mencionou ASP.NET MVC. ASP.NET MVC e ASP.NET Core são dois produtos completamente diferentes. Esses problemas e este repositório para ASP..NET Core.

Editar: Apenas a partir dos números de versão, parece que você está usando o ASP.NET Core, mas queria ter certeza.

Usamos .net core MVC. @wanton7
Estou analisando a memória. Talvez o Castle Dynamic Proxy leve a um vazamento de memória.
memroy1
memroy2
memroy3
dynamicproxy

@Serjster seus programas estão sendo executados no .NET Core de 32 bits e travando com exceções de memória insuficiente? Se o seu código faz muitas alocações de LOH, a fragmentação de memória provavelmente é o motivo.
Se você estiver executando em ambiente de 32 bits, você tem duas opções, corrija seu código para evitar LOH ou mude para ambiente de 64 bits.

Não estou muito familiarizado com o Azure, mas depois de pesquisar um pouco, encontrei isso https://blogs.msdn.microsoft.com/webdev/2018/01/09/64-bit-asp-net-core-on-azure-app -serviço/

@Serjster Acredito que nem todos os relatórios aqui são iguais e prefiro verificar a validade de cada caso diferente. Algo como "meu aplicativo tem um vazamento de memória" não significa que seja por causa da estrutura, então prefiro garantir que cada caso seja real.

Tomando o seu caso, por exemplo, "eu acredito" que respondi a razão pela qual sua memória está aumentando. Você conseguiu consertar depois da minha explicação? Ou não resolveu o problema e neste caso você pode fornecer um aplicativo que eu possa executar localmente para reproduzir o problema?

@sebastienros O que acabamos descobrindo foi que a alocação de memória continuou aumentando, embora o consumo de memória não. Eu sei que o ASP.NET Core está fazendo algumas heurísticas para dizer a ele que deve pegar mais, mas parecia estar constantemente alocando mais e mais em cada solicitação. Quase me parece ganancioso a esse respeito, mas posso estar errado.

De qualquer forma, acho que o ponto do @Serjster é que esse tópico continua crescendo porque há claramente alguma confusão aqui. Na terra ASP.NET antiga (antes do núcleo 1, acredito), não precisávamos ver esse comportamento (pelo menos não nessa magnitude. Pode não ser realmente um bug/problema, mas definitivamente é algo que causa muitas pessoas colocar a mesma pergunta repetidamente.

Seria bom se houvesse um artigo oficial que realmente abordasse esse tópico de cima para baixo, em vez de andar em círculos como tem sido. Espero que ajude a esclarecer.

Seria bom se houvesse um artigo oficial que realmente abordasse esse tópico de cima para baixo, em vez de andar em círculos como tem sido. Espero que ajude a esclarecer.

Eu concordo. Seria bom sabermos por que o .NET Core 2.1 é mais "oportunístico" quando se trata de gerenciamento de memória do que as versões anteriores.

@sebastienros , por favor, podemos ter um resumo dos problemas aqui? há 81 comentários - tenho a impressão de que eles não são todos sobre os problemas (embora eu não os tenha lido cuidadosamente, então posso estar enganado). em caso afirmativo, podemos listar todos os problemas distintos aqui e ver se temos reproduções para cada um deles? há pessoas suficientes que mencionaram o aumento de memória, acho que isso nos justifica obter repros e descobrir se esses são problemas gerais ou não.

Certo. Atualmente, estou tentando identificar todos esses tópicos e ver qual é o status de cada um deles. Eventualmente, fecharei esta edição e reabrirei outras mais específicas que focam em cada relatório, porque esse único tópico não é mais sustentável. Vou vinculá-los aqui para aqueles neste tópico que quiserem segui-los.

Paralelamente, trabalharei em um documento que lista todas as recomendações, os problemas de memória conhecidos (LOB, HttpClient, ...) e as formas de analisar e relatar esses problemas.

Apenas para garantir que nos preocupamos com esse problema e para detectar vazamentos de memória preventivamente, nos últimos 6 meses temos executado aplicativos ASP.NET continuamente no Azure, no Linux e no Windows, por 24 horas e 7 dias. Esses testes buscam a fonte ASP.NET mais recente em cada iteração (diária ou semanal). Medimos RPS, latência, CPU e uso de memória. O aplicativo usa EF Core, MVC e Razor e é mantido em 50% da CPU para simular uma carga significativa.

Você pode ver os resultados publicamente aqui neste site (procure o relatório diário): https://msit.powerbi.com/view?r=eyJrIjoiYTZjMTk3YjEtMzQ3Yi00NTI5LTg5ZDItNmUyMGRlOTkwMGRlIiwidCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImMiOjV9&pageName=ReportSectioneeff188c61c9c6e3a798

Isso nos permitiu corrigir alguns problemas do passado e ter certeza de que não há vazamentos fundamentais no sistema no momento. Mas não chega nem perto de usar todos os componentes que enviamos e pode haver problemas que precisamos identificar. Fornecer dumps e aplicativos que reproduzam os problemas são as principais maneiras de nos ajudar.

@sebastienros Obrigado pelas atualizações. Eu sei que no nosso caso o problema era mais sobre uma nova preocupação de "alocação de memória gananciosa", que originalmente confundimos com um vazamento de memória. Eu nem tenho certeza se há um problema agora, pode ser que as novas heurísticas de otimização sejam muito mais agressivas. Não tenho certeza... mas acho que você está no caminho certo ao realmente avaliar este tópico e apresentar algumas explicações/resumos consolidados sobre o que as pessoas estão vendo/compreendendo. Boa sorte!

Todas as denúncias individuais foram isoladas e terão acompanhamento individualizado, sendo encerradas caso já tenham sido solucionadas. Sinta-se à vontade para se inscrever para manter-se atualizado.

Paralelamente, trabalharei em um documento que lista todas as recomendações, os problemas de memória conhecidos (LOB, HttpClient, ...) e as formas de analisar e relatar esses problemas.

Este é um enorme +1 de mim. Eu sinto que um dos maiores problemas aqui é o quão difícil é 'parecer' reunir informações, para então tentar ajudar a determinar _qual_ é o problema. Ter alguns ótimos documentos que podem nos permitir seguir algumas instruções para (i) coletar, (ii) tentar um autodiagnóstico e (iii) postar nossos despejos/descobertas de uma maneira eficiente para a equipe do MS pode realmente ajudar tanto lados da cerca.

Se nós (os desenvolvedores) pudermos estar mais capacitados para diagnosticar e/ou fornecer informações, isso será uma vantagem para todos.

Obrigado novamente por ouvir @sebastienros - muito apreciado, companheiro!

O que você acha da situação na imagem abaixo?

Rodamos 4 WebApps dentro do mesmo Plano. Inicial era B1, dimensionado para S2 e a memória continuou crescendo até que eu configurei o faminto webapp csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

e também desabilitar o Application Insights

  1. Acredito que, como a memória pode ser mantida sob controle com a configuração acima, não há lea de memória. Correto?
  2. O comportamento apresentado é normal?

memory eaten up

Mesma situação aqui do @alexiordan , tínhamos um console .net core 2.1, que rodava alguns IHostedServices hospedados no Kube, FROM microsoft/ dotnet:2.1-runtime AS base. Queríamos habilitar o HealthChecks, então adicionamos o asp.net apenas com o middleware HealthChecks e alteramos a imagem base para microsoft/ dotnet:2.1-aspnetcore-runtime. O resultado foi OOM. Conseguimos estabilizar a alocação de memória adicionandofalso em csproj.

Nossa análise mostrou que em um aplicativo asp.net O GC coleta com menos frequência, também a Fila do Finalizador é percorrida com menos frequência.

Além disso, se forçarmos o GC a coletar e percorrer a fila do finalizador adicionando o seguinte em nosso pipeline,

System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();

a alocação de memória permaneceu estável.

O que você acha da situação na imagem abaixo?

Rodamos 4 WebApps dentro do mesmo Plano. Inicial era B1, dimensionado para S2 e a memória continuou crescendo até que eu configurei o faminto webapp csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

e também desabilitar o Application Insights

  1. Acredito que, como a memória pode ser mantida sob controle com a configuração acima, não há lea de memória. Correto?
  2. O comportamento apresentado é normal?

memory eaten up

Oi @alexiordan

Também estamos vendo um perfil de memória muito semelhante ao usar o AI (aplicativo da web no net core 2.1), você progrediu mais na solução disso? Obviamente, queremos manter a IA nos aplicativos.

É estranho que o uso cresça a cada solicitação, mas definir o acima como false parece pará-lo para mim? estranho porque você pensaria que verdadeiro seria o valor que o habilita, mas parece o contrário...

Esqueci de mencionar que logo depois de anunciar minha intenção de escrever um artigo sobre os problemas descritos neste tópico eu realmente fiz isso. Você pode vê-lo aqui: https://github.com/sebastienros/memoryleak

Ele vem com um pequeno aplicativo que renderiza os padrões em um gráfico, ao vivo.

mas definir o acima para false parece pará-lo para mim? estranho porque você pensaria que verdadeiro seria o valor que o habilita, mas parece o contrário...

A coleta de lixo do cliente (otimizada para compartilhamento de memória com muitos aplicativos e mantendo a memória livre) é mais agressiva do que a coleta de lixo do servidor (otimizada para taxa de transferência e simultaneidade).

Definindo SGC como false, minha api core do asp.net caiu de 150mb para 48mb e não cresceu em cada solicitação depois disso. Então, na realidade, esse é o melhor cenário para produção, por enquanto?

@kgrosvenor na verdade, depende. Citando o excelente artigo do @sebastienros :

Em um ambiente de servidor web típico, o recurso de CPU é mais crítico que a memória, portanto, o uso do GC do servidor é mais adequado. No entanto, alguns cenários de servidor podem ser mais adaptados para um Workstation GC, por exemplo, em uma alta densidade que hospeda várias aplicações web onde a memória se torna um recurso escasso.

Obrigado, isso é muito útil, terei em mente - seguirei este tópico para mais atualizações, sendo que estou adorando o asp.net core :)

Também sofra com isso com um aplicativo de console .net core 2.1. crescimento constante da memória. definimos os contêineres do docker em um máximo baixo para que ele o atinja e reinicie, o que está funcionando bem, mas é feio.

Alguma novidade deste lado? Também temos o mesmo comportamento no ASP.Net Core v2.1. Pelo que posso ver no commit https://github.com/aspnet/AspNetCore/commit/659fa967a1900653f7a82f02624c7c7995a3b786 parece que houve um problema de gerenciamento de memória que será corrigido na v3.0?

@flo8 você tentou atualizar para 2.2 e verificou o comportamento? https://dotnet.microsoft.com/download

Executando o 2.2 mais recente e também tem esse problema - apenas criar uma lista de 1 milhão de ints e retornar um emptyResult() aumentará meu heap em algumas centenas de MB a cada solicitação até ficar sem memória. Definir ServerGarbageCollection para false curou-o, embora não pareça a correção correta ...

@dre99gsx como parece que você conseguiu uma reprodução fácil, você poderia compartilhar um projeto e etapas para que eu possa fazer a mesma coisa localmente?

Neste caso deve preencher o LOB mas devem ser recolhidos no gen2. Além disso, compartilhe em qual ambiente posso reproduzir isso, SO, memória, carga.

Desculpe pela resposta enigmática. Muito fácil:
(Windows 7 64bit, 16GB ram, Google chrome para solicitações http, comunidade VS2017) - ainda estou me acostumando a adicionar código a esses threads, perdoe a aparência)

  • iniciar um novo aplicativo Web .NET Core 2.2
  • Injete uma classe de serviço, com escopo (nada de especial ..) no construtor do controlador
  • Faça com que a ação Index() do controlador chame um método desta classe de serviço
  • Crie uma classe de modelo (DumbClass) com uma propriedade: public int ID {get; definir;}
  • No método de classe de serviço, instancie uma lista e preencha-a:
    var lst = nova lista();
    for (i=0; i<10000000; i++) <--- nota: 10 milhões de iterações
    {
    lst.add(new DumbClass(){ID=i});
    }
  • return do método, não precisa passar nada de volta, mas você também pode passar essa lista de volta ...
  • index() return new EmptyResult();

Agora é só chamar a ação a cada 8 segundos e ver a memória do criador de perfil subir. No meu sistema, isso é o que vejo em Bytes Privados:

Iniciar aplicação: 155 MB
1ª solicitação de obtenção de http: 809 MB
2º: 1,2 GB
3º: 1,4 GB
4º: 1,8 GB
5º: 2,1 GB... 2,3 GB... 2,6 GB...

Agora, em algum momento, o GC parece entrar em ação, mas nunca cai abaixo de 3 GB neste exemplo. Como mencionado, se eu transformar o ServerGC em falso, nunca sobe acima de 1 GB, embora ainda suba lá e paire em 1 GB.

Deixe-me saber se isso ajuda, e se você pode reproduzi-lo. Ah, eu li sua postagem no github: "Gerenciamento de memória e padrões no ASP.NET Core", artigo fantástico, obrigado por contribuir!

@dre99gsx 👋

Ainda estou me acostumando a adicionar código a esses tópicos, perdoe a aparência)

Sem problemas :) Pergunta: você poderia realmente colocar todo o aplicativo de amostra no GitHub (repositórios gratuitos) ou em algum lugar semelhante? Essa é a maneira mais simples para qualquer outra pessoa clonar/baixar rapidamente _todo_ o aplicativo/repositório de amostra exato com o qual você está brincando.

Além disso, capturas de tela do uso de memória usando TaskManager ajudariam (se no Windows - ou o equiv no * nix .. que é o comando top ??)

Grande esforço até agora!

/me volta a assistir silenciosamente a este tópico com seriedade.

Um lembrete, você pode ver algumas demonstrações e explicações para todos os sintomas descritos no tópico aqui: https://github.com/sebastienros/memoryleak

Além disso, cada um desses problemas foi tratado individualmente e nenhum provou ser bugs no dotnet core __até agora__, mas o comportamento esperado.

@dre99gsx Agora, voltando ao comentário recente, peço que você registre um problema separado deste tópico. Estando no meu telefone, não percebi que era "este";). Desde o seu primeiro comentário você afirma

até eu ficar sem memória

Então, eu esperaria uma exceção OutOfMemory, e é por isso que pedi uma reprodução. Mas no seu próximo comentário você afirma:

Agora, em algum momento, o GC parece entrar em ação, mas nunca cai abaixo de 3 GB neste exemplo

Portanto, não há problema de memória. Isso é típico de uma máquina com muitos núcleos e muita memória disponível. O GC liberará os heaps gerenciados, mas a memória ainda será confirmada, pois não há motivo para descommitá-la (muita memória disponível). Este é um comportamento padrão no .NET, e eu o demonstro no artigo que apontei. Você também pode ler isto: https://blogs.msdn.microsoft.com/maoni/2018/11/16/running-with-server-gc-in-a-small-container-scenario-part-0/

Eu sei que a equipe de tempo de execução está trabalhando atualmente em maneiras de restringir aplicativos .NET executados em contêineres para que não use tanta memória, visando 3.0, para resolver alguns cenários de microsserviços.

E como você mesmo descobriu, se seu aplicativo não pode usar toda a memória disponível no servidor, você deve usar o modo GC da estação de trabalho.

Correto, quando eu disse "falta de memória", estou me baseando em ver visualmente nenhuma memória disponível para qualquer outro aplicativo através do Conjunto de Trabalho Privado do Windows (Gerenciador de Tarefas); não uma "exceção de falta de memória".

Quer saber, você está certo. Isso está se parecendo cada vez mais com o comportamento esperado e, se há tanta memória disponível, por que gastar recursos liberando-a se ninguém mais precisar dela! Entendi. Contanto que o GC seja inteligente o suficiente para liberar memória para que outros aplicativos não sejam limitados , vou mantê-lo como está e deixá-lo fazer suas coisas. Obrigado novamente!

Eu desenvolvi meu aplicativo no Asp.net core 2.2 também enfrentando o mesmo problema relacionado à liberação de memória.
cada chamada aumenta a memória no intervalo de 40-50 Mb cada vez que nunca é liberado.

Eu também adicionei a tag mencionada ServerGarbageCollection>false
ainda enfrentando o mesmo problema para 50 usuários, está utilizando aproximadamente 2 GB de RAM no modo em processo (processo de trabalho w3wp iis)

Por favor ajude.

Por favor ajude !! o mesmo problema que ankitmori14

@ankitmori14 , @ikourfaln - se você leu o comentário de @sebastienros em https://github.com/aspnet/AspNetCore/issues/1976#issuecomment -449675298 e ainda acredita que há um problema de memória, registre um novo problema com etapas detalhadas para reproduzir o problema, além de quaisquer outras informações e rastros que você tenha sobre o comportamento. Lembre-se de que, a menos que o aplicativo/processo esteja realmente com erros, é improvável (mas não impossível) que haja um bug. O Coletor de Lixo 'Servidor' padrão não tenta usar a menor quantidade de memória possível; ele entra em ação quando precisa , como quando a memória está realmente acabando. Portanto, mesmo um aplicativo pequeno pode usar 2 GB de memória e isso não é um problema, porque ainda há 4 GB livres.

Oi,

Estamos enfrentando esse problema: https://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux

Basicamente a memória cresce ao longo dos dias no processo até que o Kubernetes a mata porque atinge o limite configurado de 512Mb. Engraçado, a memória caiu drasticamente ao fazer um despejo de memória, sem que o processo fosse reiniciado nem nada. Examinando o despejo de memória, vimos muitos objetos sem root.

Desativamos também o GC concorrente (ou seja: GC em segundo plano) ontem e parece estar melhor agora, mas teremos que esperar pelo menos uma semana para confirmar.

<PropertyGroup>
  <ServerGarbageCollection>false</ServerGarbageCollection>
  <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>

@vtortola pergunta, quando você configurou o limite de 512Mb para seu aplicativo, você tinha uma ideia do número de solicitações simultâneas que desejava manipular ou testou quantas solicitações simultâneas seu aplicativo poderia manipular antes de cair?

Fizemos alguns testes preliminares e preliminares e verificamos que poderíamos lidar com 500 websockets simultâneos por pod usando 512Mb. Corremos por horas com 2 pods e 1000 conexões simultâneas com memória inferior a 150Mb. A aplicação implantada, com 2 pods, tem entre 150 e 300 conexões simultâneas a qualquer momento, e a memória varia de menos de 100Mb nos primeiros dias, até atingir os 512Mb em cerca de 2 semanas. Não parece haver correlação entre o número de conexões e a memória utilizada. Mais de 70% das conexões duram 10 minutos.

Você poderia compartilhar um despejo de memória quando estiver em 100 MB e 512 MB para ver quais instâncias ainda estão vivas?

Receio não poder compartilhar um dump, pois contém cookies, tokens e muitos dados privados.

Você pode comparar então localmente então? Em termos de qual objeto consome mais memória e se o número deles não corresponde à sua carga. Como se você tivesse 300 conexões, não deveria haver objetos de conexão de 3K.

Infelizmente o teste de configuração <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection> não ajudou e o processo ainda está acumulando memória.

Temos um dump linux do processo, a única ferramenta que temos para analisá-lo é o lldb e somos muito noob nesse assunto.

Aqui estão alguns dados caso soe um sino:

(lldb) eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00007F8481C8D0B0
generation 1 starts at 0x00007F8481C7E820
generation 2 starts at 0x00007F852A1D7000
ephemeral segment allocation context: none
         segment             begin         allocated              size
00007F852A1D6000  00007F852A1D7000  00007F853A1D5E90  0xfffee90(268430992)
00007F84807D0000  00007F84807D1000  00007F8482278000  0x1aa7000(27947008)
Large object heap starts at 0x00007F853A1D7000
         segment             begin         allocated              size
00007F853A1D6000  00007F853A1D7000  00007F853A7C60F8  0x5ef0f8(6222072)
Total Size:              Size: 0x12094f88 (302600072) bytes.
------------------------------
GC Heap Size:            Size: 0x12094f88 (302600072) bytes.

E o resultado de dumpheap -stat : https://pastebin.com/ERN7LZ0n

Você pode descobrir como o Grafana estava relatando o estado da memória em https://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux

Temos outro grupo de serviços em dotnet core que estão funcionando perfeitamente, embora não usem websockets.

@vtortola , você se importaria de criar um novo problema para não bifurcar este. Acho que o fato de estar usando WebSockets o torna único e precisamos criar pelo menos um aplicativo de exemplo que se comporte como o seu e o estresse por um longo período de tempo. Você seria capaz de descrever o que seus clientes fazem com o websocket, como eles se conectam a ele e por quanto tempo? Eu poderia configurar um aplicativo como este e executá-lo por dias e fazer despejos de memória se eu vir a memória crescendo.

Também pode ser algum conflito entre o GC e o Kubernetes, de forma que o GC não seja avisado de que está próximo do limite do pod.

@richlander está trabalhando em um problema semelhante. Rich esse caso (leia apenas os últimos 6 comentários) pode estar relacionado ao que você está trabalhando?

se isso faz alguma diferença, eu tenho o mesmo problema (ou muito semelhante). Eu tenho uma dúzia de aplicativos de console .net core 2.1 e 2.2 em execução no docker - cada contêiner com um limite de memória de 512 MB, também usando websockets (cliente), e os aplicativos atingem o limite de memória do contêiner a cada ~ 3 dias. O contêiner então é encerrado e reiniciado.

Tudo está usando serviços autenticados, então não posso fornecer o que tenho, mas talvez consiga montar algo que use um site de teste para reproduzir o problema.

Quais parâmetros você usa para definir os limites de memória? Apenas --memory ou também --memory-swap ?

Quando eu testo, posso ver que é levado em conta muito bem, nunca ultrapassando o limite que eu estabeleci, mesmo com um limite super baixo como 16MiB, porém ele troca de disco como um louco. E estou testando com um aplicativo que faz consultas db (EF) e renderização de visualizações Razor.

@sebastienros absolutamente, criei https://github.com/aspnet/AspNetCore/issues/6803

Deixe-me saber se você precisar de mais informações.

Atualmente estou enfrentando problemas de memória.

Após uma hora em produção, toda a memória é usada pelo processo w3wp.exe (executando o .NET Core InProcess). Alterar o GC para Workstation não funcionou para mim.

Depois de analisar um despejo de memória, encontrei esse problema semelhante, https://github.com/aspnet/AspNetCore/issues/6102.

Espero testá-lo em produção ainda hoje, depois de atualizar para o tempo de execução do .NET Core mais recente, atualmente 2.2.3. Eu vou deixar você saber como ele vai.

Estou tendo problemas semelhantes com o uso de memória. Se eu definir limites em meus contêineres do Linux, isso tornará o OOM mais rápido. Isso acontece até mesmo usando os modelos básicos do Visual Studio. Uma coisa que encontrei - Core 2.1 e 2.2 são afetados. Core 2.0 não é - mas é EOL :(

Veja acima

Eu sei que a equipe de tempo de execução está trabalhando atualmente em maneiras de restringir aplicativos .NET executados em contêineres para que não use tanta memória, visando 3.0, para resolver alguns cenários de microsserviços.

E como você mesmo descobriu, se seu aplicativo não pode usar toda a memória disponível no servidor, você deve usar o modo GC da estação de trabalho.

Infelizmente, o modo de estação de trabalho não ajuda em nada - infelizmente, não estou em condições de experimentar uma das visualizações 3.0 ou similares.

@PrefabPanda verifique se você está realmente executando no modo GC da estação de trabalho verificando se System.Runtime.GCSettings.IsServerGC é false .

Então isso pode ser um vazamento de memória genuíno. Abra um problema separado talvez o melhor passo.

@wanton7 - obrigado, verifiquei duas vezes e isso está definitivamente definido.

@DAllanCarr - vai fazer

Tenho quase certeza de que não é um vazamento de memória. Mais como as configurações do Docker podem não ser aplicadas corretamente e o GC não é ativado antes que o limite seja atingido. Eu sei que 3.0 introduz correções nesse sentido.

@richlander esse problema se parece com algo que é conhecido por não funcionar no 2.2?

@PrefabPanda você se importaria de compartilhar a versão exata do docker que você está usando e o arquivo de composição do docker? Estou tentando reproduzir, mas entre as versões do docker e do docker-compose, tenho dificuldade em replicar esse problema localmente.

@sebastienros , @richlander - Obrigado por me responder. Eu realmente gostei disso.

Minhas versões do Docker:

Comunidade da área de trabalho do Docker
Versão 2.0.0.2 (30215)

Motor: 18.09.1
Compor: 1.23.2

Veja em anexo todo o projeto de teste:
WebApplication1.zip

teste curl request.zip

Apenas no caso, meu Dockerfile:

DE mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /aplicativo
EXPOR 80
EXPO 443

DE mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
CÓPIA DE . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app

DE construir AS publicar
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app

DA base COMO final
WORKDIR /aplicativo
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]

docker-compose.yml:

versão: '2.4'

Serviços:
aplicativo da web1:
imagem: ${DOCKER_REGISTRY-}webapplication1
mem_reservation: 128m
mem_limit: 256m
memswap_limit: 256m
processador: 1
Construir:
contexto: .
dockerfile: WebApplication1/Dockerfile

docker-compose-override.yml:

versão: '2.4'

Serviços:
aplicativo da web1:
meio Ambiente:
- ASPNETCORE_ENVIRONMENT=Desenvolvimento
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_HTTPS_PORT=44329
- DOTNET_RUNNING_IN_CONTAINER=true
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=verdadeiro
- ASPNETCORE_preventHostingStartup=true
portas:
- "50996:80"
- "44329:443"
volumes:
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/ https:ro
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/us ersecrets:ro

@sebastienros - mesmo que haja uma maneira de colocar uma variável de ambiente separada no contêiner para o GC examinar, isso funcionaria bem para mim.

Eu tentei chamá-lo explicitamente, mas não ajuda - suponho que mesmo ao chamá-lo no código, ele ainda vê o tamanho de memória errado do contêiner/máquina.

@PrefabPanda quando você disse "Eu tentei chamá-lo explicitamente", você poderia elaborar o que significa exatamente? O GC vê o limite de memória do contêiner correto se você tiver um especificado, seja ele acionado naturalmente ou induzido. se você chamou GC.Collect(), ele fará uma coleção de bloqueio completa; e se você fizer GC.Collect(2, GCCollectionMode.Default, true, true) ele fará uma compactação completa de GC, quando isso retornar o heap está no menor tamanho possível, independente do limite do container ou qualquer outra coisa.

Oi @Maoni0 - Eu tentei GC.Collect(2, GCCollectionMode.Default, true, true)

Acabei de ver outro comentário dizendo que 256 MB é muito pequeno para 2.2, mas pode ser 'corrigido' em 3.0. Parece que vou ter que experimentar um pouco mais...

se você tentou GC.Collect(2, GCCollectionMode.Default, true, true) e a memória não caiu conforme o esperado, isso significa que você tem um vazamento de memória real. não tenho certeza de quantas ferramentas estão disponíveis em seu ambiente. você pode executar comandos sos em tudo? em caso afirmativo, você sempre pode olhar para ver o que resta no heap após o GC induzido

Obrigado @Maoni0 - lembre-se de que estou usando um modelo no Visual studio - literalmente não há código próprio neste projeto de teste que anexei. Tenho outros comentários para trabalhar, entrarei em contato. Muito Obrigado.

@Maoni0 - Tentei definir o limite mais alto, sem diferença. Parece que vou ter que experimentar a pré-visualização 3.0

Eu gostaria de bloquear os comentários sobre este assunto, pois é um assunto muito antigo que teve todos os seus casos tratados em questões separadas. Comentei este por engano pensando que estava comentando https://github.com/aspnet/AspNetCore/issues/10200

Obrigado

Esta página foi útil?
0 / 5 - 0 avaliações