Entendendo Alocação de Memória no Go

Entendendo Alocação de Memória no Go

Em Go, graças ao garbage collector, não precisarmos nos preocupar em gerenciar manualmente a memória alocada. Tanto o compilador como o runtime da linguagem desempenham papéis fundamentais no processo de alocação de memória. Que tal entender como eles fazem isso?

Nessa palestra, iremos mergulhar no funcionamento dos alocadores de memória, e entenderemos como estes interagem com o Sistema Operacional e gerenciam a memória alocada.

Aprenderemos como funciona o algoritmo utilizado pelo runtime do Go para alocação de memória, qual o papel do compilador e porque conhecer os internos da linguagem pode ajudar a escrever códigos mais otimizados (e quando não fazer isso).

B39c757b2a5fdbdec05a65e882dd07eb?s=128

André Carvalho

July 21, 2018
Tweet

Transcript

  1. Entendendo alocação de memória no Go André Carvalho @andresantostc 1

  2. Desenvolvedor @ Globo.com tsuru.io https://andrestc.com 2

  3. 3

  4. Por que? • Conhecer os trade-offs • Conhecer uma camada

    de abstração abaixo • Porque não? 4
  5. func main() { rand.Seed(time.Now().UnixNano()) i := rand.Intn(100) fmt.Printf("%v at %p\n",

    i, &i) for { } } 5
  6. 6 $ ./vmemory 53 at 0xc420016110 $ ./vmemory 68 at

    0xc420016110 Rodando o programa duas vezes ao mesmo tempo... Mesmo endereço
  7. Memória Virtual • Processos não leem diretamente da memória física

    ◦ Segurança ◦ Coordenação entre múltiplos processos • Memória Virtual abstrai isso dos processos ◦ Segmentation ◦ Page tables 7
  8. Memória Virtual 8 Frame 0 Frame 1 Frame 2 Frame

    3 Frame 4 Frame 5 Frame 6 Frame 7 RAM Disk Other process Page 0 Page 1 Page 2 Page 3 Page 4 Page 5 Page 6 Page 7 Page Process Frame Page Table 3 6
  9. Layout de um Processo 9 Text Data Heap BSS Stack

    Program Break Code Initialized static variables Uninitialized static variables Dynamic allocated variables Function stack frames
  10. Alocação na Stack 10 Stack Used Stack Pointer (SP) Unused

    Allocation SP += size; return Stack[SP-size]; Deallocation SP -= size;
  11. Alocação na Heap • Objetos com tamanho conhecido apenas em

    tempo de execução • C tem malloc e free • C++ tem new e delete • Go usa escape analysis e garbage collection 11
  12. Alocador Simples 12

  13. Alocador Simples Precisamos implementar duas funções 13 void* malloc(size_t size)

    void free(void *ptr)
  14. Alocador Simples 14 Application Allocator OS malloc mmap Alocador utiliza

    syscalls como mmap/munmap para falar com o OS munmap madvise free
  15. Alocador Simples Lista encadeada de objetos disponíveis size=n next=* Header

    n bytes size=m next=nil m bytes 15 Head
  16. Alocador Simples - Alocação malloc(10) 16 Head NULL

  17. Alocador Simples 17 Virtual Address Space 0x000000c000000000 mmap( 0x000000c000000000, 4096,

    PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, ...) Start Address Size Permission Flags
  18. Alocador Simples - Alocação malloc(10) 18 4084 12 4084 4096

    Head
  19. Alocador Simples - Alocação malloc(10) 19 4062 Head 10 22

  20. Alocador Simples - Alocação malloc(10) 20 4062 Head 10 P

    Allocator returns p, which points right after the header
  21. 21 Alocador Simples - Desalocação 4062 Head free(p) 10

  22. free(p) 10 p - size(header) 22 Alocador Simples - Desalocação

    4062 Head
  23. Alocador Simples • Pode ser implementado em algumas centenas de

    LOCs • Questões não endereçadas ◦ Fragmentação ◦ Devolver memória para o OS ▪ Quando? ▪ Como? munmap, madvise... ◦ Multi-thread ◦ ... 23
  24. Go Runtime Allocator 24 • TCMalloc • Invocando o Alocador

    • Go’s Allocator
  25. Thread-Caching Malloc (TCMalloc) • Implementado originalmente em C pelo Google

    • Serve como base para o algoritmo usado pelo Go • Diminui lock-contention em programas multi-threaded 25
  26. TCMalloc • Cada thread tem um cache local • Dois

    tipos de alocação ◦ Small allocations (<= 32 kB) ◦ Large allocations 26
  27. TCMalloc - Small Allocations • Atendidas pelo cache local da

    thread • Tamanho é arredondado para o de uma das classes 27 malloc(965 bytes) ⇒ malloc(1024 bytes) malloc(1023 bytes) ⇒ malloc(1024 bytes)
  28. TCMalloc - Small Allocations Class 0 Class 1 Class 2

    ... Local Thread Cache 28
  29. TCMalloc - Small Allocations Class 0 Class 1 Class 2

    ... Local Thread Cache 29
  30. TCMalloc - Small Allocations Class 0 Class 1 Class 2

    ... Local Thread Cache Span Span Span ... Central Free List Class 1 30 Run of contiguous pages Span
  31. TCMalloc - Small Allocations Class 0 Class 1 Class 2

    ... Local Thread Cache Span Span Span ... Central Free List Class 1 31
  32. TCMalloc - Small Allocations Span Span Span Central Free List

    Class X Span 1 page 2 pages ... > 255 pages Span Span Span Span Span Span Span Central Heap 32
  33. TCMalloc - Small Allocations Span Span Span Central Free List

    Class X Span 1 page 2 pages ... > 255 pages Span Span Span Span Span Span Central Heap Span ... 33 Span
  34. TCMalloc - Small Allocations Application Local Thread Cache Central Free

    List Central Heap 4 bytes N 8-byte objects X pages OS Y*X pages 34 X pages N 8-byte objects Y*X pages
  35. TCMalloc - Large Allocations • Atendidas pela Heap central •

    Tamanho é arredondado para numero de paginas 35 malloc(34 kB) ⇒ malloc(36 kB) ⇒ 9 pages malloc(33 kB) ⇒ malloc(36 kB) ⇒ 9 pages
  36. TCMalloc - Large Allocations 1 page 2 pages ... >

    255 pages Span Span Span Span Span Span Central Heap Span Span 36
  37. TCMalloc - Deallocation Page 1 Page 2 Page 3 Page

    4 Page 5 Page 6 Span A Span B Span C 37
  38. TCMalloc - Deallocation free( ) Page Span 38

  39. TCMalloc - Deallocation free( ) Page Span Class 0 Class

    1 Class 2 ... Local Thread Cache Small object 39
  40. TCMalloc - Deallocation free( ) Page Span Large object Page

    1 Page 2 Page 3 Page 4 Span A Span B Span C 40
  41. TCMalloc - Deallocation free( ) Page Span Large object Page

    1 Page 2 Page 3 Page 4 Span A Span B 41
  42. TCMalloc - Deallocation free( ) Page Span Large object 1

    page 2 pages ... > 255 pages Span B Central Heap 42
  43. TCMalloc - Deallocation free( ) Page Span Large object 1

    page 2 pages ... > 255 pages Span B Central Heap 43
  44. Go Runtime Allocator 44 • TCMalloc • Invocando o Alocador

    • Go’s Allocator
  45. package main func main() { f() } //go:noinline func f()

    *int { i := 10 return &i } 45
  46. package main func main() { f() } //go:noinline func f()

    *int { i := 10 return &i } 46 $ go build -gcflags "-m -m" main.go # command-line-arguments ./main.go:8:6: cannot inline f: marked go:noinline ./main.go:3:6: cannot inline main: non-leaf function ./main.go:10:9: &i escapes to heap ./main.go:10:9: from ~r0 (return) at ./main.go:10:2 ./main.go:9:2: moved to heap: i
  47. 47 $ go tool compile -S main.go ... 0x001d 00029

    (main.go:9) LEAQ type.int(SB), AX 0x0024 00036 (main.go:9) MOVQ AX, (SP) 0x0028 00040 (main.go:9) PCDATA $0, $0 0x0028 00040 (main.go:9) CALL runtime.newobject(SB) ...
  48. 48 $ go tool compile -S main.go ... 0x001d 00029

    (main.go:9) LEAQ type.int(SB), AX 0x0024 00036 (main.go:9) MOVQ AX, (SP) 0x0028 00040 (main.go:9) PCDATA $0, $0 0x0028 00040 (main.go:9) CALL runtime.newobject(SB) ... func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) }
  49. Go Runtime Allocator 49 • TCMalloc • Invoking the Allocator

    • Go’s Allocator
  50. Go’s Allocator • Acoplado ao GC e outras partes do

    runtime ◦ Difícil de trocar por outras implementações • Três tipos de alocação ◦ Tiny Allocations (no pointers, size < 16 bytes) ◦ Small Allocations (size <= 32 kbytes) ◦ Large Allocations 50
  51. Go’s Allocator - Large Allocations 51 1 page 2 pages

    ... > 255 pages Span Span Span Span Span Span mheap Busy Spans Span Span Span Antes de alocar, mheap faz o sweep do mesmo número de paginas
  52. Go’s Allocator - Large Allocations 52 1 page 2 pages

    ... > 255 pages Span Span Span Span Span Span Span Span Span Span Span mheap Free Spans
  53. Go’s Allocator - Large Allocations 53 1 page 2 pages

    ... > 255 pages Span Span Span Span Span Span mheap Free Spans Span Span Span Span Span mtreap ⇒ randomized binary tree
  54. Go’s Allocator - Large Allocations 54 Depois de alocar, dependendo

    da quantidade de memória em uso... • A goroutine pode ter que trabalhar para o GC • Stop the World
  55. Go’s Allocator - Small Allocations 55 P 1 mcache Cada

    processador lógico (P) tem um cache local (mcache) P 2 mcache
  56. Go’s Allocator - Small Allocations 56 P 1 P 2

    mcache mcache Cada mcache mantem um span para cada tamanho (size class) Span Span ... class 1 class 2 Span Span ... class 1 class 2
  57. Go’s Allocator - Small Allocations 57 class bytes/obj bytes/span objects

    1 8 8192 1024 2 16 8192 512 3 32 8192 256 4 64 8192 170 ... 65 28672 57344 2 66 32768 32768 1
  58. Go’s Allocator - Small Allocations 58 P 1 mcache mcache

    retorna o endereço de um objeto livre no span Span Span ... class 1 class 2 Span
  59. Go’s Allocator - Small Allocations 59 P 1 mcache mcache

    pede um novo span para o mcentral dessa size class Span Span ... class 1 class 2 Span
  60. Go’s Allocator - Small Allocations 60 P 1 mcache Cada

    mcentral tem duas listas, empty e nonempty spans Span Span ... ... class 1 class 2 mcentral mcentral Span Span Span Span
  61. Go’s Allocator - Small Allocations 61 P 1 mcache Span

    com objetos livres vai ser entregue ao mcache Span ... ... class 1 class 2 mcentral mcentral Span Span Span Span
  62. Go’s Allocator - Small Allocations 62 P 1 mcache mcentral

    vai tentar fazer sweep de spans vazios Span Span ... ... class 1 class 2 mcentral mcentral Span Span If there are no nonempty spans….
  63. Go’s Allocator - Small Allocations 63 Se nada funciona, mcentral

    vai pedir um novo span para a mheap class 0 mcentral Span Span mheap
  64. Go’s Allocator - Small Allocations 64 mcentral vai dar esse

    span para a mcache class 0 mcentral Span Span mheap Span
  65. Go’s Allocator - Tiny Allocations Alocações para objetos sem pointers

    menores que < 16 bytes The main targets of tiny allocator are small strings and standalone escaping variables. On a json benchmark the allocator reduces number of allocations by ~12% and reduces heap size by ~20%. 65
  66. 66 Go’s Allocator - Tiny Allocations 64 bytes Allocated Free

    Allocated Free • Cada P mantem um objeto de 64-bytes que foi alocado de um span • Cada tiny allocation adiciona um subobjeto ao final do objeto • O GC não sabe sobre esses subobjetos!
  67. 67 Go’s Allocator - Tiny Allocations Allocated Free mcache P

    1 • Pega um novo bloco do mcache ≃ small allocation • Eventualmente, o GC vai coletar o bloco antigo Free P 1 mcache
  68. Garbage Collector ⇒ Concurrent mark and sweep 68 Go’s Allocator

    - Sweeping 1. Scan all objects 2. Mark objects that are live 3. Sweep objects that are not live
  69. • Runtime periodicamente devolve memória ao OS • Devolve spans

    que foram garbage collected a mais de 5 minutos • No Linux, usa a syscall madvise(2) 69 Go’s Allocator - Devolvendo memória ao OS madvise(addr, size, _MADV_DONTNEED)
  70. Analisar alocações • runtime.ReadMemStats • pprof • go tool trace

    70
  71. 71 stats := runtime.MemStats{} runtime.ReadMemStats(&stats) type MemStats struct { ...

    // Heap memory statistics. HeapAlloc uint64 HeapSys uint64 HeapIdle uint64 HeapInuse uint64 HeapReleased uint64 HeapObjects uint64 ... }
  72. Referências 1. http://goog-perftools.sourceforge.net/doc/tcmalloc.html 2. https://www.ardanlabs.com/blog/2017/05/language-mechanics-on-stacks-and-pointers.html 3. https://gabrieletolomei.wordpress.com/miscellanea/operating-systems/in-memory-layout/ 4. Lec 10

    | MIT 6.172 Performance Engineering of Software Systems, Fall 2010 5. https://faculty.washington.edu/aragon/pubs/rst89.pdf 6. http://man7.org/linux/man-pages/man2/mmap.2.html 7. http://man7.org/linux/man-pages/man2/madvise.2.html 8. https://github.com/andrestc/linux-prog/blob/master/ch7/malloc.c 72
  73. Obrigado! andrestc.com @andresantostc 73