Que tipo de coleta de lixo Go usa?

111

Go é uma linguagem de coleta de lixo:

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

Aqui está escrito que é um coletor de lixo de marcação e varredura, mas não se aprofunda em detalhes e uma substituição está em andamento ... ainda, este parágrafo parece não ter sido atualizado muito desde o lançamento do Go.

Ainda é marcar e varrer? É conservador ou preciso? É geracional?

user1003432
fonte
2
Para uma longa discussão sobre a história do coletor de lixo Go até julho de 2018, consulte blog.golang.org/ismmkeynote
Wildcard

Respostas:

117

Planos para o coletor de lixo Go 1.4+:

  • coletor híbrido stop-the-world / concorrente
  • parte stop-the-world limitada por um prazo de 10 ms
  • Núcleos de CPU dedicados à execução do coletor simultâneo
  • algoritmo de marcação e varredura tricolor
  • não geracional
  • não compactando
  • totalmente preciso
  • incorre em um pequeno custo se o programa mover ponteiros
  • menor latência, mas provavelmente também menor taxa de transferência, do que Go 1.3 GC

Atualizações do coletor de lixo do Go 1.3 além do Go 1.1:

  • varredura simultânea (resulta em tempos de pausa menores)
  • totalmente preciso

Coletor de lixo Go 1.1:

  • marcação e varredura (implementação paralela)
  • não geracional
  • não compactando
  • principalmente preciso (exceto frames de pilha)
  • pare o mundo
  • representação baseada em bitmap
  • custo zero quando o programa não está alocando memória (isto é: embaralhar ponteiros é tão rápido quanto em C, embora na prática isso seja um pouco mais lento do que C porque o compilador Go não é tão avançado quanto compiladores C como o GCC)
  • suporta finalizadores em objetos
  • não há suporte para referências fracas

Coletor de lixo Go 1.0:

  • igual ao Go 1.1, mas em vez de ser mais preciso, o coletor de lixo é conservador. O GC conservador é capaz de ignorar objetos como [] byte.

Substituir o GC por um diferente é controverso, por exemplo:

  • exceto para pilhas muito grandes, não está claro se um GC geracional seria mais rápido no geral
  • pacote "inseguro" torna difícil implementar GC totalmente preciso e compactar GC

fonte
Além disso, o coletor de lixo atual tem um certo grau de paralelismo para que possa ser executado mais rapidamente em sistemas multi-core.
uriel de
3
@uriel: Sim, mencionei isso no primeiro item da minha resposta - o texto "(implementação paralela)".
Esta resposta ainda é atual?
Kim Stebel
c # coletor de lixo é preciso e em c # como em go, você pode ter referência a um membro de um struck e c # tem um modo inseguro, mas não tenho certeza de como ele se compara à implementação insegura
skyde
3
Que tal atualizar esta resposta com 1.5.x apenas para fazer um bom registro do histórico.
Ismael de
32

(Para Go 1.8 - T1 2017, veja abaixo )

O próximo Go 1.5 Garbage Collector simultâneo do envolve a capacidade de "acompanhar" o gc.
Aqui está uma proposta apresentada neste artigo que pode servir para Go 1.5, mas também ajuda a entender o gc em Go.

Você pode ver o estado antes de 1.5 (Stop The World: STW)

Antes do Go 1.5, Go usava um coletor stop-the-world (STW) paralelo .
Embora a coleção STW tenha muitas desvantagens, ela pelo menos tem um comportamento de crescimento de heap previsível e controlável.

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

(Foto da apresentação GopherCon 2015 " Go GC: Resolvendo o problema de latência no Go 1.5 ")

O único botão de ajuste para o coletor STW era “GOGC”, o crescimento de heap relativo entre as coleções. A configuração padrão, 100%, acionou a coleta de lixo sempre que o tamanho do heap dobrou em relação ao tamanho do heap ativo da coleta anterior:

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

Cronometragem do GC no coletor STW.

Go 1.5 apresenta um coletor simultâneo .
Isso tem muitas vantagens sobre a coleta STW, mas torna o crescimento do heap mais difícil de controlar porque o aplicativo pode alocar memória enquanto o coletor de lixo está em execução .

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

(Foto da apresentação do GopherCon 2015 " Go GC: Resolvendo o problema de latência no Go 1.5 ")

Para atingir o mesmo limite de crescimento de heap, o tempo de execução deve iniciar a coleta de lixo mais cedo, mas o quanto antes depende de muitas variáveis, muitas das quais não podem ser previstas.

  • Inicie o coletor muito cedo e o aplicativo executará muitas coletas de lixo, desperdiçando recursos da CPU.
  • Inicie o coletor tarde demais e o aplicativo excederá o crescimento máximo de heap desejado.

Alcançar o equilíbrio certo sem sacrificar a simultaneidade requer um ritmo cuidadoso do coletor de lixo.

O ritmo de GC visa otimizar ao longo de duas dimensões: crescimento de heap e CPU utilizada pelo coletor de lixo.

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

O design do ritmo GC consiste em quatro componentes:

  1. um estimador para a quantidade de trabalho de digitalização que um ciclo de GC exigirá,
  2. um mecanismo para que modificadores executem a quantidade estimada de trabalho de varredura até que a alocação de heap atinja a meta de heap,
  3. um programador para verificação em segundo plano quando o modificador ajuda a subutilizar o orçamento da CPU, e
  4. um controlador proporcional para o gatilho GC.

O design equilibra duas visões diferentes de tempo: tempo de CPU e tempo de heap .

  • O tempo da CPU é como o tempo padrão do relógio de parede, mas passa GOMAXPROCSmais rápido.
    Ou seja, se GOMAXPROCSfor 8, então oito segundos de CPU passam a cada segundo de parede e o GC obtém dois segundos de tempo de CPU a cada segundo de parede.
    O agendador de CPU gerencia o tempo de CPU.
  • A passagem do tempo de heap é medida em bytes e avança conforme os modificadores alocam.

A relação entre o tempo de heap e o tempo de parede depende da taxa de alocação e pode mudar constantemente.
O Mutator auxilia no gerenciamento da passagem do tempo de heap, garantindo que o trabalho de varredura estimado tenha sido concluído no momento em que o heap atinge o tamanho desejado.
Por fim, o controlador de gatilho cria um loop de feedback que une essas duas visões de tempo, otimizando os objetivos de tempo de heap e de CPU.

VonC
fonte
20

Esta é a implementação do GC:

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

Dos documentos na fonte:

O GC é executado simultaneamente com threads de mutação, é de tipo preciso (também conhecido como preciso), permite que várias threads de GC sejam executadas em paralelo. É uma marca e varredura simultâneas que usa uma barreira de gravação. Não é geracional e não compacta. A alocação é feita usando tamanho segregado por áreas de alocação P para minimizar a fragmentação enquanto elimina bloqueios no caso comum.

berdario
fonte
8

O Go 1.8 GC pode evoluir novamente, com a proposta "Eliminar re-digitalização de pilha STW"

A partir do Go 1.7, a única fonte remanescente de tempo de parada do mundo (STW) ilimitado e potencialmente não trivial é a nova varredura da pilha.

Propomos eliminar a necessidade de escaneamento de pilha mudando para uma barreira de gravação híbrida que combina uma barreira de gravação de exclusão no estilo Yuasa [Yuasa '90] e uma barreira de gravação de inserção no estilo Dijkstra [Dijkstra '78] .

Experimentos preliminares mostram que isso pode reduzir o tempo STW do pior caso para menos de 50 µs , e esta abordagem pode tornar prático eliminar a terminação da marca STW completamente.

O anúncio está aqui e você pode ver que o commit de origem relevante é d70b0fe e anteriores.

VonC
fonte
3

Não tenho certeza, mas acho que o GC atual (dica) já é paralelo ou pelo menos é um WIP. Assim, a propriedade stop-the-world não se aplica mais ou não será em um futuro próximo. Talvez outra pessoa possa esclarecer isso com mais detalhes.

jnml
fonte
7
É parar o mundo. O GC potencialmente roda em paralelo depois que o mundo foi parado. Você provavelmente quis dizer GC simultâneo.