Por que Large Object Heap e por que nos importamos?

Eu li sobre Gerações e heap de objects grandes. Mas eu ainda não consigo entender qual é o significado (ou benefício) de ter heap de object grande?

O que poderia ter corrido mal (em termos de desempenho ou memory) se o CLR tivesse confiado apenas na Geração 2 (Considerando que o limite para Gen0 e Gen1 é pequeno para manipular objects Grandes) para armazenar objects grandes?

Uma garbage collection não apenas se livra de objects não referenciados, mas também compacta o heap. Essa é uma otimização muito importante. Ele não apenas torna o uso da memory mais eficiente (sem buracos não usados), mas torna o cache da CPU muito mais eficiente. O cache é realmente importante em processadores modernos, eles são uma ordem fácil de magnitude mais rápida que o barramento de memory.

A compactação é feita simplesmente copiando bytes. Isso, no entanto, leva tempo. Quanto maior o object, maior a probabilidade de que o custo de copiá-lo supere os possíveis aprimoramentos do uso do cache da CPU.

Então eles fizeram um monte de benchmarks para determinar o ponto de equilíbrio. E chegou a 85.000 bytes como o ponto de corte em que a cópia não melhora mais o desempenho. Com uma exceção especial para matrizes duplas, elas são consideradas “grandes” quando o array tem mais de 1000 elementos. Essa é outra otimização para o código de 32 bits, o alocador de heap de object grande tem a propriedade especial que aloca memory em endereços alinhados a 8, diferente do alocador de geração regular que aloca apenas alinhado a 4. Esse alinhamento é um grande negócio para o dobro ler ou escrever um duplo mal alinhado é muito caro. Estranhamente, as informações esparsas da Microsoft nunca mencionam matrizes de longo, não tenho certeza do que está acontecendo com isso.

Fwiw, há muita ansiedade do programador sobre o heap de objects grandes não ser compactado. Isso invariavelmente é acionado quando eles escrevem programas que consomem mais da metade de todo o espaço de endereço disponível. Seguido usando uma ferramenta como um profiler de memory para descobrir por que o programa bombardeou mesmo que ainda houvesse muita memory virtual não utilizada disponível. Tal ferramenta mostra os buracos no LOH, pedaços não utilizados de memory onde anteriormente um object grande vivia, mas recebia o lixo coletado. Tal é o preço inevitável do LOH, o buraco só pode ser reutilizado por uma alocação para um object que é igual ou menor em tamanho. O problema real é supor que um programa deve ter permissão para consumir toda a memory virtual a qualquer momento.

Um problema que de outra forma desaparece completamente apenas executando o código em um sistema operacional de 64 bits. Um processo de 64 bits possui 8 terabytes de espaço de endereço de memory virtual disponíveis, 3 ordens de magnitude a mais do que um processo de 32 bits. Você simplesmente não pode ficar sem buracos.

Resumindo a história, o LOH torna o código mais eficiente. Ao custo de usar espaço de endereço de memory virtual disponível menos eficiente.


UPDATE, .NET 4.5.1 agora suporta a compactação da propriedade LOH, GCSettings.LargeObjectHeapCompactionMode . Cuidado com as conseqüências, por favor.

Se o tamanho do object for maior do que algum valor fixado (85000 bytes no .NET 1), o CLR o colocará no heap de objects grandes. Isso otimiza:

  1. Alocação de objects (pequenos objects não são misturados com objects grandes)
  2. Coleta de lixo (LOH coletado somente no GC completo)
  3. Desfragmentação de memory (LOH nunca é compactado raramente)

A diferença essencial de Small Object Heap (SOH) e Large Object Heap (LOH) é, memory em SOH obtém compactada quando coletada, enquanto LOH não, como este artigo ilustra. A compactação de objects grandes custa muito. Similar aos exemplos no artigo, dizer que mover um byte na memory precisa de 2 ciclos e, em seguida, compactar um object de 8 MB em um computador de 2 GHz precisa de 8 ms, o que é um grande custo. Considerando objects grandes (arrays na maioria dos casos) são bastante comuns na prática, suponho que essa seja a razão pela qual a Microsoft fixa objects grandes na memory e propõe o LOH.

BTW, de acordo com este post , LOH geralmente não gera problemas de fragments de memory.

O principal é que é improvável (e possivelmente com um design ruim) que um processo crie muitos objects grandes de curta duração para que o CLR aloque objects grandes para um heap separado no qual ele executa o GC em um planejamento diferente para o heap regular. http://msdn.microsoft.com/pt-br/magazine/cc534993.aspx

Eu não sou um especialista no CLR, mas imagino que ter um heap dedicado para objects grandes pode evitar varreduras de GC desnecessárias dos heaps geracionais existentes. Alocar um object grande requer uma quantidade significativa de memory livre contígua . Para fornecer isso a partir dos “buracos” dispersos nos montes geracionais, você precisaria de compactações frequentes (que são feitas apenas com ciclos de GC).