Node.js Por Baixo dos Panos #9: Coletando o Lixo
Tags: node, javascript, v8, computerscience
Published: 2020-04-06 14:19:10

*Photo por Jilbert Ebrahimi no Unsplash*

Em nosso [artigo mais recente](https://dev.to/khaosdoctor/node-js-por-baixo-dos-panos-8-entendendo-bytecodes-jib), passamos por bytecodes! Agora vamos ver algo um pouco mais a fundo!

# Garbage Collection

Houve um tempo em que os humanos precisavam escrever código pensando no gerenciamento de memória, mas, com o passar dos anos, não precisamos mais nos preocupar com isso. Isso ocorre devido a uma ferramenta mágica chamada [Garbage Collector (GC)](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan).

A coleta de lixo é uma prática comum para gerenciamento de memória na maioria das linguagens. O único trabalho de um [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) é recuperar a memória que está sendo ocupada por objetos não utilizados. Foi usado pela primeira vez no LISP em 1959.

Mas como ele sabe quando um objeto não é mais usado?

## Gerenciamento de memória no [Node.js](https://docs.microsoft.com/windows/nodejs/?WT.mc_id=nodeunder-devto-ludossan)

Como não precisamos mais nos preocupar com a memória, ela é totalmente gerenciada pelo compilador. Portanto, a alocação de memória é feita automaticamente quando precisamos alocar uma nova variável, e é limpa automaticamente quando essa memória não é mais necessária.

A maneira como o [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) sabe quando os objetos não são mais usados é por suas referências ou como eles referenciam um ao outro. Quando um objeto não está fazendo referência nem sendo referenciado por qualquer outro objeto, é coletado como lixo. Dê uma olhada neste diagrama:

![](https://raw.githubusercontent.com/khaosdoctor/my-notes/master/node/assets/gc1.png)

Você pode ver que existem alguns objetos referenciando outros e referenciados, mas existem dois objetos que não estão sendo referenciados ou fazem referência a ninguém. Portanto, eles serão excluídos e sua memória recuperada. Este é o diagrama após a varredura do [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan):

![](https://raw.githubusercontent.com/khaosdoctor/my-notes/master/node/assets/gc2.png)

As desvantagens do uso de coletores de lixo são que eles podem ter um enorme impacto no desempenho e ter travamentos e freezes imprevisíveis.

## Gerenciamento de memória na prática

Vamos usa um exemplo simples para mostrar como o gerenciamento de memória funciona:

```js
function add (a, b) {
  return a + b
}
add(4, 5)
```

Temos algumas camadas que precisamos entender:

- **A Stack (ou pilha)**: a pilha é o local onde todas as variáveis locais, ponteiros para objetos ou fluxo de controle do programa estão. Em nossa função, ambos os parâmetros serão colocados na pilha.
- **A Heap**: O heap é a parte do nosso programa onde objetos de instanciados são armazenados, como strings ou objetos. Portanto, o objeto `Point` abaixo será colocado na heap.

```js
function Point (x, y) {
  this.x = x
  this.y = y
}

const point1 = new Point(1, 2)
```

Se dermos uma olhada na memória na heap, teríamos algo como isto:

```
root -----------> point1
```

Agora vamos criar outros `Point`:

```js
function Point (x, y) {
  this.x = x
  this.y = y
}

const point1 = new Point(1, 2)
const point2 = new Point(2, 3)
const point3 = new Point(4, 4)
```

Teriamos isso:

```
     |-------------------> point1
root |-------------------> point2
     |-------------------> point3
```

Agora, se o [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) fosse executado, nada aconteceria, pois todo os nossos objetos armazenam referências ao objeto raiz.

Vamos adicionar alguns objetos no meio:

```js
function Chart (name) {
  this.name = name
}

function Point (x, y, name) {
  this.x = x
  this.y = y
  this.name = new Chart(name)
}

const point1 = new Point(1, 2, 'Chart1')
const point2 = new Point(2, 3, 'Chart2')
const point3 = new Point(4, 4, 'Chart3')
```

Agora teríamos isso:

```
     |-------------------> point1 ----> Chart1
root |-------------------> point2 ----> Chart2
     |-------------------> point3 ----> Chart3
```

O que aconteceria se a gente setasse nosso `point2` para `undefined`?

```
     |-------------------> point1 ----> Chart1
root |                     point2 ----> Chart2
     |-------------------> point3 ----> Chart3
```

Observe que, agora, o objeto `point2` não pode ser alcançado a partir do objeto raiz. Portanto, na próxima rodada do [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan), ele será eliminado:

```
     |-------------------> point1 ----> Chart1
root
     |-------------------> point3 ----> Chart3
```

É basicamente assim que o [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) funciona, ele caminha da raiz para todos os objetos, se houver houver objetos na lista de objetos que não tenham sido acessados por essa caminhada, então ele não poderá ser acessado pela raiz e, portanto, será removido.

O [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) pode rodar de diferentes formas.

## Métodos de [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan)

Existem vários métodos para executar um GC.

**New Space & Old Space**

Este é o método que o [Node.js](https://docs.microsoft.com/windows/nodejs/?WT.mc_id=nodeunder-devto-ludossan) usa.

A heap possui dois segmentos principais: o *new space* e o *old space*. O new space é onde as alocações estão acontecendo ativamente; este é o local mais rápido para coletar lixo, o new space tem cerca de 1 a 8 MBs. Todos os objetos no new space são chamados de **geração jovem (young generation)**.

Por outro lado, o old space é o local onde os objetos que sobreviveram à última coleta de lixo residem, no nosso caso, os objetos `point1` e `point3` estão no old space. Eles são chamados de **geração antiga (old generation)**. A alocação no old space é bastante rápida, no entanto, o [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) é caro, por isso quase nunca é realizado.

Porém, quase 20% da geração jovem sobrevive e é promovida à geração antiga; portanto, essa varredura no old space não precisa ser feita com muita frequência. Ele é executado apenas quando esse espaço está se esgotando, o que significa em torno de 512mb. 

> Você pode definir esse limite com o sinalizador `--max-old-size-size` no Node.js. 

Para recuperar a memória antiga, o [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan) usa dois algoritmos de coleta diferentes.

**Scavenge & Mark-Sweep Collection**

O método Scavenge é rápido, por isso roda somente an geração jovem. Enquanto o Mark-Sweep é mais lento e roda somente no old space.

O Mark & Sweep functiona somente com alguns passos bem básicos:

1. Começa com o objeto raiz. Raízes são variáveis globais que são referenciadas no código. Em [JS](https://docs.microsoft.com/azure/javascript/?WT.mc_id=nodeunder-devto-ludossan), esse pode ser o objeto `window` ou, no Node, o objeto `global`. A lista completa de todas essas raízes é criada pelo [GC](https://docs.microsoft.com/dotnet/standard/garbage-collection/fundamentals?WT.mc_id=nodeunder-devto-ludossan).
2. O algoritmo inspeciona todas as raízes e todos os seus filhos, marcando cada um como ativo - de modo que significa que ainda não são lixo - logicamente, qualquer outra coisa que a raiz não possa alcançar não será marcada como ativa, o que significa: lixo
3. Depois disso, todos os objetos não ativos são liberados.

![](https://raw.githubusercontent.com/khaosdoctor/my-notes/master/node/assets/mark-sweep.gif)

# Conclusão

Estamos a um artigo de terminar nossa série! Neste artigo, discutimos o tratamento da memória e a coleta de lixo; no próximo, discutiremos como o compilador otimiza todo o código! Fiquem ligados!

Não deixe de acompanhar mais do meu conteúdo no [meu blog](https://blog.lsantos.dev/?utm_source=devto&utm_medium=article&utm_campaign=node_under_the_hood) e se [inscreva na newsletter](https://blog.lsantos.dev/signup?utm_source=devto&utm_medium=article&utm_campaign=node_under_the_hood) para receber notícias semanais!