Dicen que la compactación de recolectores de basura es más rápida que la administración de memoria tradicional porque solo tienen que recolectar objetos en vivo, y al reorganizarlos en la memoria para que todo esté en un bloque contiguo, terminas sin fragmentación de pila. ¿Pero cómo se puede hacer eso rápidamente? Me parece que eso es equivalente al problema del empaquetamiento de contenedores, que es NP-hard y no puede completarse en un período de tiempo razonable en un gran conjunto de datos dentro de los límites actuales de nuestro conocimiento sobre el cálculo. ¿Qué me estoy perdiendo?¿Cómo funciona la compactación en pila?
Respuesta
Compactación significa mover objetos en la RAM para que se eliminen algunos objetos (los objetos muertos, que se supone que el GC debe reclamar) y todos los objetos restantes se vuelven contiguos en la RAM.
La mayoría de las compactaciones de GC funcionan asignando objetos dentro de un único, área contigua obtenida del sistema operativo. La compactación es como quitar los objetos muertos y luego empujar todos los objetos vivos restantes "hacia la izquierda", exprimiendo los agujeros. Si el GC funciona por compactación, entonces la asignación es simplemente una cuestión de mover hacia arriba el puntero de "fin del área asignada". Sintéticamente, dentro del área de asignación, hay un puntero tal que el área libre consta de los bytes después de ese puntero. Para asignar espacio para un objeto, el puntero simplemente se mueve hacia arriba por el nuevo tamaño del objeto. Ocasionalmente, el GC decide que es hora de correr, detecta el objeto muerto, exprime los agujeros y, por lo tanto, reduce el puntero de asignación.
Las mejoras en el rendimiento de un GC compactación provienen de varias fuentes:
- Para la asignación, no hay necesidad de encontrar un "agujero" adecuada: por la construcción, el espacio libre es, en todo momento, una sola , área grande, y basta con mover un puntero hacia arriba.
- No hay fragmentación: la compactación mueve todos los objetos vivos juntos, pero esto se puede ver moviendo todos los agujeros juntos en un único espacio libre grande.
- Localidad mejorada. Al mover objetos vivos a áreas adyacentes, se mejora el comportamiento con respecto a la memoria caché. En particular, los algoritmos de compactación tienden a mantener los objetos en su orden de asignación respectivo (los objetos se deslizan pero no se intercambian) que se ha informado que son heurísticamente buenos para la mayoría de las aplicaciones.
Si el sistema operativo se niega a dar una sola área de la asignación, en lugar de ceder varios bloques, entonces las cosas se vuelven un poco más complejo, y podría empezar a parecerse al problema de bin-embalaje, debido a que el GC compactar a continuación, tiene que decidir en qué bloque va cada objeto vivo. Sin embargo, la complejidad del bin-packing se trata de encontrar el partido "perfecto" en el caso general; una solución aproximada ya es lo suficientemente buena para un asignador de memoria.
La dificultad algorítmica en un algoritmo de compactación consiste en actualizar todos los punteros, de modo que apunten a la nueva ubicación del objeto. A través de una tipificación estricta, la máquina virtual .NET puede decidir inequívocamente si cada palabra en la RAM es un puntero o no, pero actualizar todos los punteros de manera eficiente sin utilizar demasiada RAM extra puede ser complicado. H.B.M. Jonkers ha descrito un algoritmo maravillosamente inteligente para eso, en "Un algoritmo de compactación rápida de basuras" (Information Processing Letters, Volumen 9, Número 1, 1979, pp. 26-30). No pude encontrar una copia de ese documento en Vast Internet, pero el algoritmo se describe en el libro "Garbage Collection" de Jones and Lins (sección 5.6). Recomiendo encarecidamente ese libro a cualquiera que esté interesado en entender a los recolectores de basura. El algoritmo de Jonkers requiere dos pasos lineales en los objetos en vivo y resulta fácil de implementar (unas pocas docenas de líneas de código, nada más, la parte más difícil es entender por qué funciona).
La complejidad adicional proviene de colectores generacionales que intentan, la mayoría de las veces, dejar la mayoría de los objetos intactos, trabajando preferentemente solo con objetos jóvenes. Aquí, esto significa compactar solo el final del montón; la compactación completa se aplica solo en raras ocasiones. El punto aquí es que una compactación completa, aunque lineal, aún podría inducir una pausa notable. Generational GC intenta hacer tales pausas más raras. Una vez más, el libro de Jones y Lins es una lectura obligada.
Creo que puede implementar un GC de compactación ingenuo al tener referencias en el lenguaje implementadas como punteros a una tabla de punteros, de modo que después de la compactación, el GC solo necesite actualizar la tabla. Esto hace que cada referencia sea una doble desreferencia y requiere el espacio extra para la tabla. –
Joseph: esto en realidad se hace bastante a menudo con los llamados recursos, es decir, pequeñas asignaciones del núcleo. Al menos, el clásico Mac OS lo hizo de esa manera. –
un enlace al artículo: http://oai.cwi.nl/oai/asset/9391/9391A.pdf –
- 1. compactación/minifying HTML dinámico
- 2. ¿Qué hace Casandra durante la compactación?
- 3. NDK-pila no funciona
- 4. Compactación de corriente CUDA: entendiendo el concepto
- 5. Creación de objetos en la pila/pila?
- 6. GCC - Cómo realinear la pila?
- 7. ¿Cómo rebosar la pila sin empujar nuevos marcos de pila?
- 8. OCaml en la pila empresarial
- 9. PInvoke desequilibra la pila
- 10. La pila .NET vs pila de Windows
- 11. ¿Cómo definir una pila de pila?
- 12. ¿Cómo se especifica la dirección de retorno en la pila?
- 13. ¿Cómo ver la pila de actividades en la depuración?
- 14. ¿Cómo administrar la pila de actividades?
- 15. Modificar dirección de devolución en la pila
- 16. Cómo obtener la pila completa de StackOverflowError
- 17. Aparentemente estoy corrompiendo la pila, pero ¿cómo?
- 18. ¿Cómo crear una estructura en la pila en C?
- 19. ¿Cómo lidiar con matrices (declaradas en la pila) en C++?
- 20. Error de desbordamiento de la pila de Java: ¿cómo aumentar el tamaño de la pila en Eclipse?
- 21. Cómo obtener la pila de pila nativa de las excepciones nativas capturadas en el código administrado
- 22. Alternativa para la pila
- 23. ¿Cómo funciona Microsoft Detours y cómo lo uso para obtener un seguimiento de pila?
- 24. printf usando la pila?
- 25. Will .hashcode() devolverá un int diferente debido a la compactación del espacio de tenencia?
- 26. ¿Cómo funciona Luabind?
- 27. pila frente a la cola?
- 28. ¿Cómo funciona la recursividad de Haskell tail?
- 29. ¿Cómo funciona la función jQuery pushStack?
- 30. Cómo eliminar un elemento de pila que no está en la parte superior de la pila en C#
A menudo me he preguntado esto. Mono no tiene un recolector de basura desfragmentando que compacta el montón, pero lo hace .NET de Microsoft. ¿Cómo lo hicieron? –
La compactación no es bin-packing: en el bin-packing se debe responder la pregunta "¿es posible llenar exactamente este espacio con algunos de estos bloques?". Un GC compactante no presenta tal problema y la compactación suele ser lineal en el tamaño de la memoria activa. –
@Callum Rogers Una forma es esta: Fase 1: invierta todos los punteros en el lugar para que los niños señalen a sus padres. Fase 2: mueve el bloque en vivo con la dirección más baja hasta el comienzo del montón. Use los punteros invertidos para actualizar a todos los padres. Mueve el segundo bloque en vivo con la dirección más baja justo después del primero .... Fase 3 - Invierte todos los punteros nuevamente. –