¿Qué tipo de recolección de basura usa Go?

111

Go es un lenguaje de recolección de basura:

http://golang.org/doc/go_faq.html#garbage_collection

Aquí dice que es un recolector de basura de marcar y barrer, pero no profundiza en los detalles, y se está trabajando en un reemplazo ... sin embargo, este párrafo parece no haberse actualizado mucho desde que se lanzó Go.

¿Todavía es marca y barrido? ¿Es conservador o preciso? ¿Es generacional?

usuario1003432
fuente
2
Para una larga discusión de la historia del recolector de basura Go hasta julio de 2018, consulte blog.golang.org/ismmkeynote
Comodín

Respuestas:

117

Planes para el recolector de basura Go 1.4+:

  • colector híbrido stop-the-world / concurrente
  • parte de stop-the-world limitada por un plazo de 10 ms
  • Núcleos de CPU dedicados a ejecutar el colector concurrente
  • algoritmo de marcación y barrido tricolor
  • no generacional
  • no compactante
  • totalmente preciso
  • incurre en un pequeño costo si el programa está moviendo los punteros
  • menor latencia, pero probablemente también menor rendimiento, que Go 1.3 GC

Actualizaciones del recolector de basura Go 1.3 además de Go 1.1:

  • barrido concurrente (resulta en tiempos de pausa más pequeños)
  • totalmente preciso

Ir al recolector de basura 1.1:

  • mark-and-sweep (implementación paralela)
  • no generacional
  • no compactante
  • mayormente precisos (excepto marcos de pila)
  • parar el mundo
  • representación basada en mapas de bits
  • costo cero cuando el programa no está asignando memoria (es decir: barajar punteros es tan rápido como en C, aunque en la práctica esto se ejecuta algo más lento que C porque el compilador Go no es tan avanzado como los compiladores C como GCC)
  • admite finalizadores en objetos
  • no hay soporte para referencias débiles

Ir al recolector de basura 1.0:

  • Igual que Go 1.1, pero en lugar de ser mayormente preciso, el recolector de basura es conservador. El GC conservador puede ignorar objetos como [] byte.

Reemplazar el GC por uno diferente es controvertido, por ejemplo:

  • a excepción de montones muy grandes, no está claro si un GC generacional sería más rápido en general
  • El paquete "inseguro" dificulta la implementación de GC totalmente precisos y GC compactadores

fuente
Además, el recolector de basura actual tiene un cierto grado de paralelismo para que pueda ejecutarse más rápido en sistemas de múltiples núcleos.
uriel
3
@uriel: Sí, mencioné esto en el primer elemento de mi respuesta: el texto "(implementación paralela)".
¿Sigue vigente esta respuesta?
Kim Stebel
El recolector de basura c # es preciso y en c #, como en go, puede tener una referencia al miembro de un golpe y c # tiene un modo inseguro, pero no estoy seguro de cómo se compara con la implementación insegura
skyde
3
¿Qué hay de actualizar esta respuesta con 1.5.x solo para hacer un buen registro de historial?
Ismael
32

(Para Go 1.8 - Q1 2017, ver más abajo )

El próximo recolector de basura concurrente de Go 1.5 implica poder "marcar el ritmo", dijo gc.
Aquí hay una propuesta presentada en este documento que podría ser compatible con Go 1.5, pero también ayuda a comprender el gc en Go.

Puede ver el estado antes de 1.5 (Stop The World: STW)

Antes de Go 1.5, Go utilizaba un colector paralelo de stop-the-world (STW).
Si bien la recopilación de STW tiene muchas desventajas, al menos tiene un comportamiento de crecimiento de montón predecible y controlable.

https://40.media.tumblr.com/49e6556b94d75de1050c62539680fcf9/tumblr_inline_nr6qq8D9FE1sdck2n_540.jpg

(Foto de la presentación de GopherCon 2015 " Go GC: Resolviendo el problema de latencia en Go 1.5 ")

La única perilla de ajuste para el colector STW era "GOGC", el crecimiento relativo del montón entre colecciones. La configuración predeterminada, 100%, activaba la recolección de basura cada vez que el tamaño del montón se duplicaba sobre el tamaño del montón en vivo de la colección anterior:

https://docs.google.com/drawings/image?id=sLJ_JvGfPfPnojLlEGLCWkw&rev=1&h=113&w=424&ac=1

Sincronización de GC en el colector STW.

Go 1.5 presenta un colector concurrente .
Esto tiene muchas ventajas sobre la recolección de STW, pero dificulta el control del crecimiento del montón porque la aplicación puede asignar memoria mientras se ejecuta el recolector de basura .

https://40.media.tumblr.com/783c6e557b427a5c023520578740eb94/tumblr_inline_nr6qqpmaJx1sdck2n_540.jpg

(Foto de la presentación de GopherCon 2015 " Go GC: Resolviendo el problema de latencia en Go 1.5 ")

Para lograr el mismo límite de crecimiento del montón, el tiempo de ejecución debe comenzar la recolección de basura antes, pero cuánto antes depende de muchas variables, muchas de las cuales no se pueden predecir.

  • Inicie el recolector demasiado pronto y la aplicación realizará demasiadas recolecciones de basura, desperdiciando recursos de la CPU.
  • Inicie el recopilador demasiado tarde y la aplicación superará el crecimiento de pila máximo deseado.

Lograr el equilibrio adecuado sin sacrificar la simultaneidad requiere que el recolector de basura pasee con cuidado.

El ritmo de GC tiene como objetivo optimizar en dos dimensiones: el crecimiento del montón y la CPU utilizada por el recolector de basura.

https://docs.google.com/drawings/image?id=sEZYCf7Mc0E0EGmy4gho3_w&rev=1&h=235&w=457&ac=1

El diseño de la estimulación GC consta de cuatro componentes:

  1. un estimador de la cantidad de trabajo de escaneo que requerirá un ciclo de GC,
  2. un mecanismo para que los mutadores realicen la cantidad estimada de trabajo de escaneo para cuando la asignación del montón alcance el objetivo del montón,
  3. un programador para escaneo en segundo plano cuando el mutador ayuda a subutilizar el presupuesto de la CPU, y
  4. un controlador proporcional para el gatillo del GC.

El diseño equilibra dos vistas diferentes del tiempo: tiempo de CPU y tiempo de pila .

  • El tiempo de CPU es como el tiempo estándar de un reloj de pared, pero pasa GOMAXPROCSmás rápido.
    Es decir, si GOMAXPROCSes 8, pasan ocho segundos de CPU por segundo de pared y GC obtiene dos segundos de tiempo de CPU por segundo de pared.
    El programador de la CPU administra el tiempo de la CPU.
  • El paso del tiempo de pila se mide en bytes y avanza a medida que los mutadores asignan.

La relación entre el tiempo de pila y el tiempo de pared depende de la tasa de asignación y puede cambiar constantemente.
Mutator ayuda a gestionar el paso del tiempo de pila, asegurando que el trabajo de escaneo estimado se haya completado para cuando la pila alcance el tamaño objetivo.
Por último, el controlador de disparo crea un bucle de retroalimentación que une estas dos vistas del tiempo, optimizando tanto el tiempo de pila como los objetivos de tiempo de CPU.

VonC
fuente
20

Esta es la implementación del GC:

https://github.com/golang/go/blob/master/src/runtime/mgc.go

De los documentos en la fuente:

El GC se ejecuta al mismo tiempo que los subprocesos mutadores, es de tipo preciso (también conocido como preciso), permite que varios subprocesos de GC se ejecuten en paralelo. Es una marca y un barrido simultáneos que utiliza una barrera de escritura. Es no generacional y no compacta. La asignación se realiza utilizando áreas de asignación de tamaño segregadas por P para minimizar la fragmentación y eliminar bloqueos en el caso común.

berdario
fuente
8

Go 1.8 GC podría evolucionar nuevamente, con la propuesta "Eliminar el re-escaneo de la pila STW"

A partir de Go 1.7, la única fuente restante de tiempo ilimitado y potencialmente no trivial de detener el mundo (STW) es el reexamen de la pila.

Proponemos eliminar la necesidad de volver a escanear la pila cambiando a una barrera de escritura híbrida que combina una barrera de escritura de eliminación de estilo Yuasa [Yuasa '90] y una barrera de escritura de inserción de estilo Dijkstra [Dijkstra '78] .

Los experimentos preliminares muestran que esto puede reducir el tiempo STW en el peor de los casos a menos de 50 µs , y este enfoque puede hacer que sea práctico eliminar por completo la terminación de la marca STW.

El anuncio está aquí y puede ver que la confirmación de fuente relevante es d70b0fe y anterior.

VonC
fuente
3

No estoy seguro, pero creo que el GC actual (punta) ya es paralelo o al menos es un WIP. Por lo tanto, la propiedad de detener el mundo ya no se aplica o no se aplicará en un futuro próximo. Quizás alguien más pueda aclarar esto con más detalle.

jnml
fuente
7
Es stop-the-world. GC potencialmente se ejecuta en paralelo después de que el mundo se haya detenido. Probablemente te refieres a GC concurrente.