Aguarde...

26 de outubro de 2020

O que é coleta de lixo em JavaScript e como funciona

O que é coleta de lixo em JavaScript e como funciona

A coleta de lixo não é nenhuma novidade sob o sol. No entanto, existem muitos desenvolvedores de JavaScript que não sabem muito sobre isso. Se você é um deles, não se preocupe. Este tutorial ajudará você a entender o básico da coleta de lixo em JavaScript. Você aprenderá o que é e como funciona.

Uma introdução rápida

Provavelmente, você já ouviu falar sobre essa coisa chamada “Coleta de lixo”. Se não, aqui está uma versão resumida. JavaScript é uma linguagem única. Ao contrário de outras linguagens, o JavaScript é capaz de alocar memória automaticamente quando necessário. Ele também pode liberar essa memória quando não for mais necessária.

Ao criar um novo objeto, você não precisa usar um método especial para alocar memória para esse objeto. Quando você não precisar mais desse objeto, não precisará usar outro método especial para liberar essa memória. JavaScript fará isso por você. Ele verificará automaticamente se há necessidade de alocação ou liberação de memória.

Se houver essa necessidade, o JavaScript fará o trabalho necessário para atender a essa necessidade. Ele fará tudo isso mesmo sem você saber. Isso é uma coisa boa e também ruim. É bom porque você não precisa se preocupar muito com isso. É uma coisa ruim porque pode fazer você pensar que não precisa se preocupar com isso.

O problema é que o JavaScript pode ajudá-lo com esse gerenciamento de memória apenas até certo ponto. Também não pode ajudar se você começar a lançar obstáculos no caminho. Antes de entrarmos na coleta de lixo, vamos falar rapidamente sobre memória e gerenciamento de memória.

Gerenciamento de memória e ciclo de vida da memória

Uma coisa que as linguagens de programação compartilham é algo chamado ciclo de vida da memória. Este ciclo de vida descreve a maneira como a memória é gerenciada. É composto por três etapas. A primeira etapa é alocar a memória necessária. Isso acontece quando você declara novas variáveis ​​e atribui os valores, chama uma função que cria valores, etc.

Todos esses novos valores precisam de algum espaço na memória. JavaScript aloca esse espaço e o torna disponível para você. A segunda etapa é sobre como usar essa memória alocada para tarefas como ler e gravar dados. Por exemplo, quando você deseja ler o valor de alguma variável ou propriedade do objeto, ou quando deseja alterar esse valor ou propriedade.

A terceira e última etapa é liberar a memória alocada. Você deseja liberar a memória alocada quando ela não for mais necessária. Por exemplo, quando você não precisa mais dessa variável, por que mantê-la para sempre? Você deseja que o JavaScript se livre dessa variável, para que não ocupe espaço na memória.

Esta terceira etapa é crítica. Sem ele, seu programa continuaria a consumir mais e mais memória até que não estivesse mais disponível. Então, ele iria travar. É também essa etapa final a mais difícil de ser executada corretamente. Seja para você como desenvolvedor em linguagem de baixo nível ou na própria linguagem.

Liberação de memória ou coleção de garagem

Como você sabe, o JavaScript cuida do gerenciamento de memória para você. Ele lida automaticamente com todas essas três etapas do ciclo de vida da memória. Tudo isso é bom, mas e a coleta de lixo? Onde isso entra em jogo? A resposta rápida é, na terceira etapa. Toda a terceira etapa, liberar a memória alocada, é sobre a coleta de lixo.

Coleta de lixo e como funciona

Como discutimos, a terceira etapa é a etapa mais difícil de todo o ciclo de vida da memória. Como a coleta de lixo sabe qual memória deve ser liberada? Existem poucas ferramentas e truques que a coleta de lixo usa para descobrir isso. Vamos dar uma olhada em cada uma dessas ferramentas e truques.

Referência e acessibilidade

O principal conceito no qual a coleta de lixo se baseia é o conceito de referências e acessibilidade. Ele distingue entre valores que são alcançáveis ​​e valores que não são. Os valores alcançáveis ​​são variáveis ​​locais e parâmetros em uma função atual. Se houver funções aninhadas na cadeia, os valores alcançáveis ​​também são parâmetros e variáveis ​​dessas funções aninhadas.

Por último, os valores alcançáveis ​​também são todas as variáveis ​​globais, variáveis ​​definidas no escopo global . Todos esses valores alcançáveis ​​são chamados de “raízes”. No entanto, este não é necessariamente o fim. Se houver alguns outros valores, valores que são alcançáveis ​​de uma raiz por uma referência ou cadeia de referências, então esses valores também se tornam acessíveis.

JavaScript possui um processo especial chamado coletor de lixo. Este processo é executado em segundo plano. O que ele faz é monitorar todos os objetos existentes. Quando algum objeto se torna inacessível, este coletor de lixo o remove. Vamos dar uma olhada em um exemplo de código simples.

Primeiro, vamos declarar uma nova variável global chamada “toRead” e atribuir a ela um objeto como um valor. Este valor será root porque está no escopo global e existe e a variável “toRead” funciona como uma referência ao objeto que contém, o valor daquela variável.

Enquanto essa referência existir, o objeto que ela contém, o valor da variável, não será removido pelo coletor de lixo. Ele permanecerá na memória porque ainda está acessível.

// Create object in a global scope, a root value
let toRead = { bookName: 'The Art of Computer Programming' }
// JavaScript allocates memory for object { bookName: 'The Art of Computer Programming' },
// the "toRead" becomes reference for this object
// this existing reference prevents { bookName: 'The Art of Computer Programming' } object
// from being removed by garbage collector

Digamos que você não precise mais desse objeto. Uma maneira simples de saber se o JavaScript é redundante é removendo todas as referências a ele. Neste momento, existe apenas uma referência existente, a variável “toRead”. Se você remover este coletor de lixo de referência, o objeto ao qual ele se refere não é mais necessário e ele será removido.

// Remove reference to { bookName: 'The Art of Computer Programming' } object
let toRead = null
// Garbage collector can now detect
// that the { bookName: 'The Art of Computer Programming' } object
// is no longer needed, no longer reachable, and it can remove it,
// release it from the memory

Referências múltiplas

Outro cenário é quando você tem um objeto e há várias referências a esse objeto. Por exemplo, você declara uma nova variável global e atribui a ela um objeto. Depois disso, você declara outra variável e atribui a ela o primeiro objeto fazendo referência à primeira variável.

Enquanto pelo menos uma dessas referências existir, este objeto não será removido. O espaço na memória que ocupa não será liberado. Para que isso aconteça, você terá que remover as duas referências existentes ou mais.

// Create object in a global scope, a root value
let toRead = { bookName: 'The Art of Computer Programming' }
// This is the first reference to { bookName: 'The Art of Computer Programming' } object

// Create another reference for { bookName: 'The Art of Computer Programming' } object
let alreadyRead = toRead

O resultado disso ainda será um objeto ocupando algum espaço alocado na memória. No entanto, haverá duas referências existentes para este objeto.

// Remove the first reference to { bookName: 'The Art of Computer Programming' } object
let toRead = null
// The { bookName: 'The Art of Computer Programming' } object
// is still reachable through the second reference
// and garbage collector can't remove it, release it from memory

// Remove the second reference to { bookName: 'The Art of Computer Programming' } object
let alreadyRead = null

// All references to the { bookName: 'The Art of Computer Programming' } object
// are gone and this object is now available
// for the garbage collector to be removed

Objetos interligados ou referência circular

Onde este conceito de acessibilidade e referências é insuficiente, estão os objetos interligados. Isso também é chamado de referência circular . Esta situação acontece quando dois objetos fazem referência um ao outro. Nesse caso, o coletor de lixo não pode remover nenhum deles porque cada um tem pelo menos uma referência.

// Create function that creates circular reference
function createCircularReference(obj1, obj2) {
  // Interlink both objects passed as arguments
  obj1.second = obj2
  obj2.first = obj1

  // Return new object based on the interlinked object
  return {
    winner: obj1,
    loser: obj2
  }
}

// Declare new variable and assign it the result
// of calling the createCircularReference() function
let race = createCircularReference({ name: 'Jack' }, { name: 'Spencer' })
// The value of "race" variable will be the third object
// created by interlinking the two objects
// passed to createCircularReference() function.
// These three objects are now all reachable
// because they reference each other
// and the "race" is a global variable, root

Algoritmo de marcação e varredura

O último truque que a coleta de lixo usa é o algoritmo marcar e varrer. Este algoritmo é executado periodicamente e executa um conjunto de etapas. Primeiro, ele pega todas as raízes existentes e as marca. Basicamente, ele salva em sua memória. Em seguida, visita todas as referências que partem dessas raízes. Ele marca essas referências também.

Depois disso, ele visita novamente os objetos marcados e marca suas referências. Este processo de visita e marcação continua e um até que cada referência alcançável seja visitada. Quando essa situação acontece, o coletor de lixo sabe quais objetos estão marcados e quais não estão.

Os objetos que não estão marcados são considerados inacessíveis e seguros para serem removidos. No entanto, isso não significa que esses objetos serão removidos imediatamente. Pode haver alguma lacuna antes que um objeto seja selecionado para a coleta de lixo e quando ele for realmente removido.

Coleta de lixo manual

Além dessas ferramentas e truques, também existem outras otimizações para fazer seu código funcionar de maneira mais suave, melhor e mais rápida. Essas otimizações incluem coleta geracional, coleta incremental e coleta de tempo ocioso. O que não está incluído, o que nem é possível, é uma espécie de coleta de lixo manual.

Essa é a grande vantagem da coleta de lixo. Isso acontece automaticamente em segundo plano. Você não precisa fazer nada. Isso também é ruim porque funciona apenas automaticamente. Você não pode ativá-lo ou forçá-lo, nem superá-lo ou evitá-lo. A coleta de lixo acontecerá, você nunca sabe quando, mas acontecerá.

Postado em Blog
Escreva um comentário