He leído sobre Generaciones y montón de objetos grandes. Pero todavía no entiendo cuál es el significado (o beneficio) de tener un montón de objetos grandes.
¿Qué podría haber salido mal (en términos de rendimiento o memoria) si CLR solo hubiera confiado en la Generación 2 (considerando que el umbral para Gen0 y Gen1 es pequeño para manejar objetos grandes) para almacenar objetos grandes?
.net
garbage-collection
clr
large-object-heap
Manish Basantani
fuente
fuente
Respuestas:
Una recolección de basura no solo se deshace de los objetos no referenciados, sino que también compacta el montón. Esa es una optimización muy importante. No solo hace que el uso de la memoria sea más eficiente (sin agujeros sin usar), sino que hace que la memoria caché de la CPU sea mucho más eficiente. El caché es un gran problema en los procesadores modernos, son un orden de magnitud más rápido que el bus de memoria.
La compactación se realiza simplemente copiando bytes. Sin embargo, eso lleva tiempo. Cuanto más grande sea el objeto, es más probable que el costo de copiarlo supere las posibles mejoras en el uso de la memoria caché de la CPU.
Así que ejecutaron varios puntos de referencia para determinar el punto de equilibrio. Y llegó a 85.000 bytes como punto de corte donde la copia ya no mejora el rendimiento. Con una excepción especial para las matrices de double, se consideran "grandes" cuando la matriz tiene más de 1000 elementos. Esa es otra optimización para el código de 32 bits, el asignador de montón de objetos grandes tiene la propiedad especial de que asigna memoria en direcciones que están alineadas con 8, a diferencia del asignador generacional regular que solo asigna alineado con 4. Esa alineación es un gran problema para el doble , leer o escribir un doble mal alineado es muy caro. Curiosamente, la escasa información de Microsoft nunca menciona arreglos largos, no estoy seguro de qué pasa con eso.
Fwiw, hay mucha angustia entre los programadores porque el montón de objetos grandes no se compacta. Esto invariablemente se activa cuando escriben programas que consumen más de la mitad de todo el espacio de direcciones disponible. Seguido mediante el uso de una herramienta como un generador de perfiles de memoria para averiguar por qué el programa falló a pesar de que todavía había mucha memoria virtual sin usar disponible. Dicha herramienta muestra los agujeros en el LOH, trozos de memoria no utilizados donde anteriormente vivía un objeto grande pero se recolectaba la basura. Tal es el precio inevitable del LOH, el agujero solo puede ser reutilizado por una asignación para un objeto que sea de tamaño igual o menor. El verdadero problema es asumir que un programa debería poder consumir toda la memoria virtual en cualquier momento.
Un problema que, de lo contrario, desaparece por completo con solo ejecutar el código en un sistema operativo de 64 bits. Un proceso de 64 bits tiene 8 terabytes de espacio de direcciones de memoria virtual disponible, 3 órdenes de magnitud más que un proceso de 32 bits. No puedes quedarte sin agujeros.
En pocas palabras, LOH hace que el código se ejecute de manera más eficiente. A costa de utilizar un espacio de direcciones de memoria virtual disponible menos eficiente.
ACTUALIZACIÓN, .NET 4.5.1 ahora admite la compactación de la propiedad LOH, GCSettings.LargeObjectHeapCompactionMode . Cuidado con las consecuencias por favor.
fuente
Si el tamaño del objeto es mayor que algún valor fijado (85000 bytes en .NET 1), CLR lo coloca en el Montón de objetos grandes. Esto optimiza:
Nuncarara vez compactada)fuente
La diferencia esencial de Small Object Heap (SOH) y Large Object Heap (LOH) es que la memoria en SOH se compacta cuando se recopila, mientras que LOH no, como ilustra este artículo . Compactar objetos grandes cuesta mucho. Al igual que en los ejemplos del artículo, digamos que mover un byte en la memoria necesita 2 ciclos, luego compactar un objeto de 8 MB en una computadora de 2 GHz necesita 8 ms, lo cual es un gran costo. Teniendo en cuenta que los objetos grandes (matrices en la mayoría de los casos) son bastante comunes en la práctica, supongo que esa es la razón por la que Microsoft fija objetos grandes en la memoria y propone LOH.
Por cierto, de acuerdo con esta publicación , LOH generalmente no genera problemas de fragmentos de memoria.
fuente
El principio es que es poco probable (y posiblemente un mal diseño) que un proceso cree muchos objetos grandes de corta duración, por lo que CLR asigna objetos grandes a un montón separado en el que ejecuta GC en un horario diferente al del montón normal. http://msdn.microsoft.com/en-us/magazine/cc534993.aspx
fuente
No soy un experto en CLR, pero me imagino que tener un montón dedicado para objetos grandes puede evitar barridos GC innecesarios de los montones generacionales existentes. La asignación de un objeto grande requiere una cantidad significativa de memoria libre contigua . Para proporcionar eso a partir de los "agujeros" dispersos en los montones generacionales, necesitaría compactaciones frecuentes (que solo se realizan con ciclos de GC).
fuente