it-swarm-pt.tech

Exceção C: Out of Memory

Hoje meu aplicativo hoje lançou um OutOfMemoryException. Para mim isso sempre foi quase impossível, já que tenho 4GB RAM e muita memória virtual também. O erro aconteceu quando tentei adicionar uma coleção existente a uma nova lista.

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);  

Pelo que entendi, não há muita memória alocada aqui, já que os veículos que minha nova lista deveria conter já existem dentro da memória. Eu tenho que admitir que Vehicle é uma classe muito complexa e eu tentei adicionar cerca de 50.000 itens à nova lista de uma só vez. Mas como todos os Vehicles no aplicativo vêm de um banco de dados com apenas 200 MB de tamanho: Não tenho idéia do que pode causar um OutOfMemoryException neste momento.

63
TalkingCode

Dois pontos:

  1. Se você estiver executando um Windows de 32 bits, não terá todos os 4 GB acessíveis, apenas 2 GB.
  2. Não se esqueça de que a implementação subjacente de List é uma matriz. Se sua memória estiver muito fragmentada, pode não haver espaço contíguo suficiente para alocar seu List, mesmo que no total você tenha bastante memória livre.
72
Tudor

Tópico de 3 anos, mas encontrei outra solução de trabalho. Se tiver certeza de que tem memória livre suficiente, executando o sistema operacional de 64 bits e ainda recebendo exceções, acesse a guia Project properties -> Build e certifique-se de definir x64 como Platform target.

enter image description here

78
Throw Exception Development

.net4.5 não tem mais uma limitação de 2GB para objetos. Adicione estas linhas a App.config

<runtime>
    <gcAllowVeryLargeObjects enabled="true" />    
</runtime>

e será possível criar objetos muito grandes sem obter OutOfMemoryException

Por favor, note que irá funcionar apenas em x64 OS's!

67
Lendmann

Os dados armazenados no banco de dados em comparação com a memória em seu aplicativo são muito diferentes.

Não há como obter o tamanho exato do seu objeto, mas você pode fazer isso:

GC.GetTotalMemory() 

Depois de uma certa quantidade de objetos ter sido carregada e ver o quanto sua memória está mudando conforme você carrega a lista.

Se for a lista que está causando o uso excessivo de memória, podemos procurar maneiras de minimizá-la. Por que você quer que 50.000 objetos sejam carregados na memória de uma só vez? Não seria melhor ligar para o DB conforme necessário?

Se você der uma olhada aqui: http://www.dotnetperls.com/array-memory você também verá que objetos no .NET são maiores que seus dados reais. Uma lista genérica é ainda mais do que um array de memória. Se você tiver uma lista genérica dentro de seu objeto, ela crescerá ainda mais rápido.

13
Adam Pedley

OutOfMemoryException (em máquinas de 32 bits) é tão frequente quanto a Fragmentação como limites rígidos reais de memória - você encontrará muito sobre isso, mas aqui está meu primeiro hit google discutindo brevemente: http: //blogs.msdn .com/b/joshwil/archive/2005/08/10/450202.aspx . (@Anthony Pegram está se referindo ao mesmo problema em seu comentário acima).

Dito isso, há uma outra possibilidade que vem à mente para o seu código acima: Como você está usando o construtor "IEnumerable" para a lista, você pode não dando ao objeto nenhuma indicação quanto ao tamanho da coleção que você está passando para o construtor List. Se o objeto que você está passando não é uma coleção (não implementa a interface ICollection), então, por trás das cenas, a implementação da Lista vai precisar crescer várias (ou muitas) vezes, cada vez deixando um muito pequeno. matriz que precisa ser coletada de lixo. O coletor de lixo provavelmente não obterá essas matrizes descartadas com rapidez suficiente, e você receberá seu erro.

A solução mais simples para isso seria usar o construtor List(int capacity) para informar à estrutura qual tamanho de matriz de backup alocar (mesmo se você estiver estimando e apenas adivinhando "50000", por exemplo) e, em seguida, usar o método AddRange(IEnumerable collection) para realmente preencher sua lista .

Então, o mais simples "Fix" se eu estiver certo: substitua

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);

com

List<Vehicle> vList = new List<Vehicle>(50000);  
vList.AddRange(selectedVehicles);

Todos os outros comentários e respostas ainda se aplicam em termos de decisões gerais de design - mas isso pode ser uma solução rápida.

Nota (como @Alex comentou abaixo), isso é apenas um problema se selectedVehicles não for uma ICollection.

8
Tao

Minha equipe de desenvolvimento resolveu esta situação:

Adicionamos o seguinte script Post-Build no projeto .exe e compilamos novamente, definindo o destino como x86 e aumentando em 1.5 gb e também o aumento de destino da plataforma x64 usando 3.2 gb. Nosso aplicativo é de 32 bits.

URLs relacionados:

Roteiro:

if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
    call "$(DevEnvDir)..\tools\vsvars32.bat"
    editbin /largeaddressaware "$(TargetPath)"
)
5
Mehmet Kurt

Você não deve tentar trazer toda a lista de uma vez, o tamanho dos elementos no banco de dados não é o mesmo que aquele que leva na memória. Se você quiser processar os elementos, você deve usar um para cada loop e aproveitar o carregamento preguiçoso da estrutura de entidade para não trazer todos os elementos para a memória de uma só vez. Caso você queira mostrar a lista use paginação (.Skip () e .take ())

3
Sr.PEDRO

Enquanto o GC compacta o heap de objeto pequeno como parte de uma estratégia de otimização para eliminar falhas de memória, o GC nunca compacta o heap de objeto grande por motivos de desempenho ** (o custo de compactação é muito alto para objetos grandes (maior que 85KB) ) ** Portanto, se você estiver executando um programa que usa muitos objetos grandes em um sistema x86, poderá encontrar exceções OutOfMemory. Se você estiver executando esse programa em um sistema x64, poderá ter um heap fragmentado.

3
Ali Bayat

Eu sei que esta é uma questão antiga, mas desde que nenhuma das respostas mencionou o heap grande objeto, isso pode ser útil para os outros que encontrarem essa questão ...

Qualquer alocação de memória no .NET com mais de 85.000 bytes é proveniente do heap de objeto grande (LOH) e não do heap de objeto pequeno normal. Por que isso importa? Porque o heap de objeto grande não é compactado. O que significa que o heap de objeto grande fica fragmentado e, na minha experiência, isso inevitavelmente leva a erros de falta de memória.

Na pergunta original, a lista tem 50.000 itens. Internamente, uma lista usa uma matriz e assume 32 bits, que requer 50.000 x 4 bytes = 200.000 bytes (ou o dobro, se for 64 bits). Então essa alocação de memória está vindo do heap de objetos grandes.

Então, o que você pode fazer sobre isso?

Se você estiver usando uma versão .net antes de 4.5.1, então tudo que você pode fazer é estar ciente do problema e tentar evitá-lo. Então, neste caso, em vez de ter uma lista de veículos, você poderia ter uma lista de listas de veículos, desde que nenhuma lista tivesse mais de 18.000 elementos nela. Isso pode levar a algum código feio, mas é um trabalho viável.

Se você estiver usando .net 4.5.1 ou posterior, o comportamento do coletor de lixo mudou sutilmente. Se você adicionar a seguinte linha onde você está prestes a fazer grandes alocações de memória:

System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;

ele forçará o coletor de lixo a compactar o heap de objeto grande - somente na próxima vez.

Pode não ser a melhor solução, mas o seguinte funcionou para mim:

int tries = 0;
while (tries++ < 2)
{
  try 
  {
    . . some large allocation . .
    return;
  }
  catch (System.OutOfMemoryException)
  {
    System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
    GC.Collect();
  }
}

é claro que isso só ajuda se você tiver a memória física (ou virtual) disponível.

0
Brian Cryer

Como o .Net progride, o mesmo acontece com a sua capacidade de adicionar novas configurações de 32 bits que rastreia todo mundo.

Se você está no .Net Framework 4.7.2, faça o seguinte:

Vá para as propriedades do projeto

Construir

Desmarque a opção "preferir 32 bits"

Felicidades!

0
user2326106