O Uso da Memória e Fragmentação de Classes de Imagens do .NET: Bitmap vs. Metafile

10

Devido a exceções de memória insuficientemente prematura, examinamos de perto o uso de memória de várias construções .NET ... particularmente objetos grandes que tendem a fragmentar o heap de objeto grande, causando exceções prematuras de memória insuficiente. Uma área que tem sido um pouco surpreendente são as classes de imagem do .NET: Bitmap e Metafile.

Veja o que achamos que aprendemos, mas não conseguimos encontrar a documentação da MS para verificar, por isso agradecemos qualquer confirmação que outros possam fornecer:

(1) Quando você cria um objeto Bitmap a partir de um arquivo de rasterização compactado (JPG, PNG, GIF, etc), ele consome memória para uma matriz de pixels totalmente descompactada, na resolução total desse arquivo. Assim, por exemplo, um JPG de 5 MB com 9000x3000 pixels seria expandido em 9000x3000x3 bytes (assumindo cores de 24 bits, sem alfa) ou 81MB de memória consumida. Correto?

(1a) Há algumas evidências (veja 2b abaixo) que também armazena o formato original compactado ... então, na verdade 86MB neste caso. Mas isso não está claro ... alguém sabe?

(2) Quando você cria um objeto Metafile e, em seguida, desenha um arquivo rasterizado (JPG, PNG, GIF, etc.) nele, ele consome apenas memória para o arquivo compactado. Portanto, se você desenhar um JPG de 5 MB com 9000x3000 pixels em um Metafile, ele consumirá apenas aproximadamente 5 MB de memória. Correto?

(2a) Para desenhar um arquivo raster em um objeto Metafile, a única maneira parece ser carregar um bitmap com o arquivo e, em seguida, desenhar o bitmap no metarquivo. Existe uma maneira melhor que não envolve o carregamento temporário de dados enormes de bitmap (e a fragmentação de memória associada)?

(2b) Quando você desenha um bitmap em um metarquivo, ele usa um formato compactado de tamanho semelhante ao arquivo compactado original. Isso é feito armazenando o arquivo compactado original no Bitmap? Ou faz isso comprimindo novamente o Bitmap expandido usando as configurações de compactação originais?

(3) Originalmente, assumimos que objetos de imagem grandes (> 85KB) seriam colocados no heap de objetos grandes. Na verdade, isso parece não ser o caso. Em vez disso, cada bitmap e cada metarquivo é um objeto de 24 bytes no heap de objeto pequeno que se refere a um bloco de memória nativa que contém os dados reais. Correto?

(3a) Assumimos que essa Memória Nativa é como Pilha de Objetos Grandes porque não pode ser compactada ... uma vez que o objeto grande é colocado na Memória Nativa, ele nunca será movido, e assim a fragmentação da Memória Nativa pode causar muitos problemas como fragmentação do Large Object Heap. Verdade? Ou há manipulação especial dos dados de bitmap / metaficheiro subjacentes que são mais eficientes?

(3b) Assim, parece haver quatro blocos independentes de memória que são gerenciados separadamente, e a execução de cada um deles pode resultar nas mesmas exceções Fora da Memória: Small Object Heap (objetos gerenciados < 85KB, compactados pelo GC, Large Object Heap (objetos gerenciados > 85KB que são coletados pelo GC, mas não compactados), Native Memory (objetos não gerenciados, presumivelmente não compactados) e Desktop Heap (onde os manipuladores do Windows e esses recursos limitados são gerenciados). Eu documentei esses quatro corretamente? Há outros que devemos estar cientes?

Qualquer clareza que alguém possa fornecer sobre o assunto acima seria muito apreciada. Se houver um bom livro ou artigo que explique totalmente o acima, por favor me avise. (Fico feliz em fazer a leitura necessária, mas a grande maioria dos livros não é tão profunda e, portanto, não me diz nada que eu já não saiba).

Obrigado!

    
por Brian Kennedy 20.03.2013 в 17:54
fonte

2 respostas

4

Existem duas maneiras de armazenar dados de imagem: como pixels ou como vetores. Bitmap é sobre pixels, Metafile é sobre os pixels e vetores. Dados vetoriais são muito mais eficientes para armazenar.

Para permitir a manipulação de bitmaps, seus dados devem ser armazenados descompactados na memória. Caso contrário, GetPixel e SetPixel teriam que descompactar, alterar e recompactar o bitmap para cada alteração (se é que seria possível começar com isso).

Metafiles foram criados pela Microsoft e pretendiam trabalhar com o GDI, por isso pode incorporar alguns algoritmos de compressão mais eficientes na memória que trabalhe diretamente com a placa gráfica. Além disso, não há métodos GetPixel SetPixel para metarquivos, portanto, não precisa ser descompactado na memória para permitir a manipulação.

Você não deve se preocupar com os conjuntos de memória que o tempo de execução usa. Há muito mais, e o tempo de execução decide onde coloca objetos. Além disso, você não deve se preocupar com exceções de falta de memória que podem surgir de usar objetos (grandes). O tempo de execução fará tudo o que puder (colocando objetos em intervalos entre outros objetos, compactando heaps, expandindo a memória virtual disponível) para garantir que você não receba uma exceção de falta de memória. Se de alguma forma você obtiver tal exceção, provavelmente haverá outro problema no seu código que deve ser corrigido (como um vazamento de memória).

Uma visão geral dos montes de memória, mapas e tabelas: ( fonte )

Além disso, sua suposição de que objetos com mais de 85 KiB são colocados na Pilha de objetos grandes não é totalmente correta. Is é correto para os objetos mais na versão atual do CLR, mas por exemplo uma matriz de 8 KiB de duplas (1000 duplas) também é alocada na Pilha de Objetos Grandes. Apenas deixe o tempo de execução se preocupar com isso.

    
por Daniel Pelsmaeker 20.03.2013 / 18:17
fonte
2

Eu sei as respostas para algumas delas:

(1) Sim, essa é a definição de uma imagem de bitmap.

(3) Sim, é por isso que o Bitmap implementa a interface IDisposable.

(3a) Isso parece surpreendente. Você está executando o método Dispose () em seus objetos Bitmap quando terminar com eles?

(3b) Pelo menos esses quatro, sim.

    
por Pieter Geerkens 20.03.2013 / 18:10
fonte