O que é coleta de lixo JavaScript?


Respostas:


192

Eric Lippert escreveu um post detalhado sobre este assunto há um tempo (comparando-o adicionalmente com o VBScript ). Mais precisamente, ele escreveu sobre o JScript , que é a implementação do ECMAScript da Microsoft, embora muito semelhante ao JavaScript. Eu imagino que você possa assumir que a grande maioria do comportamento seria a mesma para o mecanismo JavaScript do Internet Explorer. Obviamente, a implementação varia de navegador para navegador, embora eu suspeite que você possa usar vários princípios comuns e aplicá-los a outros navegadores.

Citado nessa página:

O JScript usa um coletor de lixo não-geracional de marcação e varredura. Funciona assim:

  • Toda variável que está "no escopo" é chamada de "limpador". Um limpador pode se referir a um número, um objeto, uma string, qualquer que seja. Mantemos uma lista de eliminadores - as variáveis ​​são movidas para a lista de eliminação quando entram no escopo e fora da lista de eliminação quando saem do escopo.

  • De vez em quando o coletor de lixo é executado. Primeiro, coloca uma "marca" em cada objeto, variável, string, etc. - toda a memória rastreada pelo GC. (O JScript usa a estrutura de dados VARIANT internamente e há muitos bits extras não utilizados nessa estrutura; portanto, apenas definimos um deles.)

  • Segundo, limpa a marca nos catadores e o fechamento transitivo das referências ao catador. Portanto, se um objeto eliminador faz referência a um objeto não destruidor, limpamos os bits no não destruidor e em tudo a que ele se refere. (Estou usando a palavra "fechamento" em um sentido diferente do que em minha postagem anterior.)

  • Neste ponto, sabemos que toda a memória ainda marcada é alocada e não pode ser alcançada por nenhum caminho de nenhuma variável no escopo. Todos esses objetos são instruídos a se derrubarem, o que destrói quaisquer referências circulares.

O principal objetivo da coleta de lixo é permitir que o programador não se preocupe com o gerenciamento de memória dos objetos que eles criam e usam, embora é claro que às vezes não há como evitá-lo - é sempre benéfico ter pelo menos uma idéia aproximada de como funciona a coleta de lixo. .

Nota histórica: uma revisão anterior da resposta tinha uma referência incorreta ao deleteoperador. Em JavaScript, o deleteoperador remove uma propriedade de um objeto e é totalmente diferente de deleteem C / C ++.


27
o guia da Apple é defeituoso: o autor usa deleteincorretamente; por exemplo, no primeiro exemplo, em vez de delete foo, você deve primeiro remover o ouvinte de evento via window.removeEventListener()e depois usar foo = nullpara substituir a variável; no IE, delete window.foo(mas não delete foo) também teria funcionado se foofosse global, mas mesmo assim não funcionaria no FF ou no Opera
Christoph

3
Esteja ciente de que o artigo de Eric deve ser considerado "apenas para fins históricos". Mas ainda é informativo.
Peter Ivan

2
Observe também - IE 6 e 7 NÃO use um coletor de lixo não-geracional de marcação e varredura. Eles usam um coletor de lixo de contagem de referência simples, mais vulnerável a problemas de referência circular na coleta de lixo.
Doug

1
O ECMAScript's deleteé um operador unário (uma expressão), não uma declaração (ou seja:) delete 0, delete 0, delete 3. Parece declaração quando expressa por uma expressão.
Hydroper

No momento, a resposta está desatualizada. A partir de 2012, os navegadores modernos usam um algorthm de marca / varredura. Portanto, não depende mais do escopo. Referência: developer.mozilla.org/pt-BR/docs/Web/JavaScript/…
sksallaj

52

Cuidado com referências circulares quando objetos DOM estiverem envolvidos:

Padrões de vazamento de memória em JavaScript

Lembre-se de que a memória só pode ser recuperada quando não há referências ativas ao objeto. Essa é uma armadilha comum com fechamentos e manipuladores de eventos, pois alguns mecanismos JS não verificam quais variáveis ​​realmente são referenciadas nas funções internas e apenas mantêm todas as variáveis ​​locais das funções anexas.

Aqui está um exemplo simples:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Uma implementação ingênua de JS não pode ser coletada bigStringenquanto o manipulador de eventos estiver disponível. Existem várias maneiras de resolver esse problema, por exemplo, a configuração bigString = nullno final de init()( deletenão funcionará para variáveis ​​locais e argumentos de função: deleteremove propriedades dos objetos e o objeto variável está inacessível - o ES5 no modo estrito acionará um ReferenceErrorse você tentar para excluir uma variável local!).

Eu recomendo evitar o máximo possível de fechamentos desnecessários se você se preocupa com o consumo de memória.


20
O bug de referência circular do DOM é específico do JScript - nenhum outro navegador sofre, exceto o IE. Na verdade eu sou bastante certo que o ECMAScript especificação afirma explicitamente que o GC deve ser capaz de lidar com esses ciclos: - /
olliej

@olliej: Não vejo nenhuma menção ao GC na especificação do ECMAScript .
Janus Troelsen


16

Boa citação retirada de um blog

O componente DOM é "coletor de lixo", assim como o componente JScript, o que significa que, se você criar um objeto em um dos componentes e perder o controle, ele será limpo.

Por exemplo:

function makeABigObject() {
var bigArray = new Array(20000);
}

Quando você chama essa função, o componente JScript cria um objeto (chamado bigArray) que pode ser acessado na função. Assim que a função retorna, você "perde o controle" do bigArray porque não há mais como se referir a ela. Bem, o componente JScript percebe que você o perdeu e, portanto, o bigArray é limpo - sua memória é recuperada. O mesmo tipo de coisa funciona no componente DOM. Se você diz document.createElement('div'), ou algo semelhante, o componente DOM cria um objeto para você. Depois que você perde o controle desse objeto, o componente DOM limpa o relacionado.


13

Que eu saiba, os objetos do JavaScript são coletados periodicamente quando não há referências restantes para o objeto. É algo que acontece automaticamente, mas se você quiser saber mais sobre como ele funciona, no nível C ++, faz sentido dar uma olhada no código-fonte WebKit ou V8

Normalmente, você não precisa pensar nisso, no entanto, em navegadores mais antigos, como o IE 5.5 e versões anteriores do IE 6, e talvez nas versões atuais, os fechamentos criariam referências circulares que, quando desmarcadas, acabariam consumindo memória. No caso em particular que quero dizer sobre fechamentos, foi quando você adicionou uma referência JavaScript a um objeto dom e um objeto a um objeto DOM que se referia novamente ao objeto JavaScript. Basicamente, ele nunca poderia ser coletado e, eventualmente, causaria instabilidade no sistema operacional em aplicativos de teste em loop para criar falhas. Na prática, esses vazamentos geralmente são pequenos, mas para manter seu código limpo, você deve excluir a referência de JavaScript para o objeto DOM.

Geralmente, é uma boa ideia usar a palavra-chave delete para des-referenciar imediatamente objetos grandes, como dados JSON, que você recebeu de volta e fez o que for necessário, especialmente no desenvolvimento da Web móvel. Isso faz com que a próxima varredura do GC remova esse objeto e libere sua memória.


O problema de referência circular JavaScript -> DOM -> JavaScript foi resolvido nas versões mais recentes do IE? Se sim, desde quando? Eu pensei que era arquitetonicamente muito profundo e improvável que algum dia fosse consertado. Você tem alguma fonte?
11119 erikkallen

Apenas anedótico. Não notei os vazamentos malucos no IE 8 sendo executados no modo padrão, não no modo quebrado. Vou ajustar minha resposta.
Miser Heat #

1
@erikkallen: sim, o bug do GC foi corrigido nas versões 8+ do IE, pois os mais antigos estavam usando um algoritmo de coleta de lixo muito ingênuo, o que tornava impossível o GC de um par de objetos se referindo um ao outro. Os mark-and-sweepalgoritmos de estilo mais recentes cuidam disso .
precisa saber é o seguinte

6

coleta de lixo (GC) é uma forma de gerenciamento automático de memória, removendo os objetos que não são mais necessários.

qualquer processo relacionado à memória siga estas etapas:

1 - aloque seu espaço de memória necessário

2 - faça algum processamento

3 - libere esse espaço de memória

existem dois algoritmos principais usados ​​para detectar quais objetos não são mais necessários.

Coleta de lixo com contagem de referência : esse algoritmo reduz a definição de "um objeto não é mais necessário" para "um objeto não tem outro objeto fazendo referência a ele", o objeto será removido se nenhum ponto de referência a ele

Algoritmo de marcação e varredura : conecte cada objeto à origem raiz. qualquer objeto não se conecta à raiz ou a outro objeto. este objeto será removido.

atualmente os navegadores mais modernos usando o segundo algoritmo.


1
E para adicionar uma fonte disso, consulte o MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos

4

"Na ciência da computação, a coleta de lixo (GC) é uma forma de gerenciamento automático de memória. O coletor de lixo, ou apenas um coletor, tenta recuperar o lixo ou a memória usada por objetos que nunca serão acessados ​​ou alterados pelo aplicativo".

Todos os mecanismos JavaScript possuem seus próprios coletores de lixo e podem ser diferentes. Na maioria das vezes, você não precisa lidar com eles, porque eles fazem o que deveriam.

Escrever um código melhor depende principalmente de quanto você conhece os princípios de programação, a linguagem e a implementação específica.


1

O que é coleta de lixo JavaScript?

verifique isso

O que é importante para um programador da Web entender sobre a coleta de lixo JavaScript, a fim de escrever um código melhor?

Em Javascript, você não se importa com alocação e desalocação de memória. Todo o problema é exigido ao intérprete Javascript. Ainda são possíveis vazamentos em Javascript, mas são erros do intérprete. Se você está interessado neste tópico, pode ler mais em www.memorymanagement.org


Qual dos vários sistemas de gerenciamento de memória do artigo ao qual você vincula é o utilizado pelo JavaScript? "Vazamentos ainda são possíveis em Javascript, mas são erros do intérprete." - Isso não significa que os programadores JS podem simplesmente ignorar todo o problema; por exemplo, há um problema de referência circular JS <-> DOM bastante conhecido nas versões mais antigas do IE, que você pode solucionar em seu código JS. Além disso, a maneira JS fechamentos de trabalho é uma característica de projeto, não um erro, mas você pode amarrar pedaços maiores de memória do que o pretendido, se você usar fechamentos "inadequada" (estou não dizendo que não usá-los).
nnnnnn

3
Vazamentos de memória são uma fera em JavaScript. Se você está escrevendo um aplicativo simples para "projeto de faculdade", não se preocupe. Mas quando você começa a escrever aplicativos de nível empresarial de alto desempenho, o gerenciamento de memória em JavaScript é obrigatório.
Doug

1

No Windows, você pode usar o Drip.exe para encontrar vazamentos de memória ou verificar se sua rotina de memória livre funciona.

É realmente simples, basta digitar o URL de um site e você verá o consumo de memória do renderizador IE integrado. Em seguida, clique em Atualizar. Se a memória aumentar, você encontrará um vazamento de memória em algum lugar da página da web. Mas isso também é muito útil para verificar se as rotinas para liberar memória funcionam no IE.


1

Os tipos de referência não armazenam o objeto diretamente na variável à qual está atribuído, portanto, a variável de objeto neste exemplo não contém realmente a instância do objeto. Em vez disso, ele mantém um ponteiro (ou referência) para o local na memória em que o objeto existe

var object = new Object();

se você atribuir uma variável a outra, cada variável obterá uma cópia do ponteiro e as duas ainda farão referência ao mesmo objeto na memória.

var object1 = new Object();
var object2 = object1;

Duas variáveis ​​apontando para um objeto

O JavaScript é uma linguagem de coleta de lixo , portanto, você realmente não precisa se preocupar com alocações de memória ao usar tipos de referência. No entanto, é melhor desreferenciar objetos que você não precisa mais para que o coletor de lixo possa liberar essa memória. A melhor maneira de fazer isso é definir a variável de objeto como nula.

var object1 = new Object();
// do something
object1 = null; // dereference

A desreferenciação de objetos é especialmente importante em aplicativos muito grandes que usam milhões de objetos.

de Os princípios do JavaScript orientado a objetos - NICHOLAS C. ZAKAS

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.