Este artigo necessita de uma revisão editorial.
Esta tradução está incompleta. Ajude atraduzir este artigo.
Introdução
Linguagens de baixo nível, como C, tem primitivas de gerenciamento de memória de baixo nível como malloc() e free(). Em contrapartida, os valores do JavaScript são alocados quando coisas (objetos, strings, etc.) são criadas e "automáticamente" liberadas quando não são mais usadas. Este ultimo processo se chama garbage collection. Facilmente se torna uma fonte de confusão e dá aimpressão aos desenvolvedores JavaScript (e outras linguagens de auto nível) que eles não precisam se preocupar com o consumo de memória. Isto é um erro.
Ciclo de vida da memória
Independentemente da linguagem de programação, o ciclo de vida da memória é praticamente sempre o mesmo:
- Aloca a memória que você precisa
- Utiliza (lê, escreve)
- Libera a memória alocada quando não é mais necessária
A primeira e a segunda parte são explícitas em todas as linguagens. A última parte é explicita em linguagens de baixo nível, porém implícito em linguagens de alto nível como JavaScript.
Alocação no JavaScript
Inicialização de valor
A fim de não incomodar o programador com alocações, o JavaScript faz isso com os valores conforme são declarados.
var n = 123; // aloca memória para um número
var s = "azerty"; // aloca memória para uma string
var o = {
a: 1,
b: null
}; // aloca memória para um objeto e seus valores contidos
// (assim como o objeto) aloca memória para o array e
// seus valores contidos
var a = [1, null, "abra"];
function f(a){
return a + 2;
} // aloca uma function (que é um objeto que pode ser chamado)
// expressões function também alocam um objeto
someElement.addEventListener('click', function(){
someElement.style.backgroundColor = 'blue';
}, false);
Alocação via chamada de uma function
Algumas functions quando chamadas resultam na alocação de um objeto.
var d = new Date();
// aloca um elemento do DOM
var e = document.createElement('div');
Alguns métodos alocam novos valores ou objetos:
var s = "azerty"; var s2 = s.substr(0, 3); // s2 é uma nova string // Como as strings são valores imutáveis, // o JavaScript pode decidir não alocar memória, // mas apenas armazenar o intervalo [0, 3]. var a = ["ouais ouais", "nan nan"]; var a2 = ["generation", "nan nan"]; var a3 = a.concat(a2); // novo array com 4 elementos sendo // a concatenação dos elementos a e a2
Utilização de valores
A utilização de valores basicamente significa leitura e escrita em memória alocada. Isto pode ser feito ao ler ou escrever o valor de uma variável ou a propriedade de um objeto ou até mesmo ao passar um argumento para uma função.
Libere quando a memória não for mais necessária
A maioria dos problemas relacionados ao gerenciamento de memória aparecem nesta fase. A tarefa mais complicada aqui é descobrir quando "a memória alocada não é mais necessária". Geralmente exige que o desenvolvedor determine aonde no programa tal pedaço da memória não é mais necessária e liberá-la.
Linguagens de alto nível vêm com um pedaço de software chamado "garbage collector" (coletor de sujeira), cujo trabalho é monitorar a alocação de memória a fim de descobrir quando um pedaço de código não é mais necessário e neste caso, automáticamente liberá-lo. Este processo é algo aproximado já que em geral saber se um pedaço de memória é necessário é algo indecidível (que não pode ser resolvido através de um algoritmo).
Garbage collection
Como foi mencionado acima, em geral o problema de automáticamente descobrir se a memória "não é mais necessária" é indecidível. Como consequencia, os garbage collections implementam uma limitação de uma solução ao problema em geral. Esta seção irá explicar os conceitos necessários para entender os principais algoritmos de garbage collection e suas limitações.
Referências
O principal conceito de algoritmos do garbage collection depende do conceito de referência. Dentro do contexto de gerenciamento de memória, um objeto é dito para fezer referência á outro objeto, caso o primeiro tenha um acesso á este último (de maneira implícita ou explícita). Por exemplo, um objeto JavaScript tem uma referencia ao seu prototype (referência implícita) e para os valores de suas propriedades (referência explícita).
Neste contexto, o conceito de "objeto" se extende para algo mais abrangente do que os objetos comuns do JavaScript, e também contém escopos de função (ou o escopo lexical global).
Referência de contagem do garbage collection
Este é o algoritmo mais ingênuo de garbage collection. este algoritmo reduz a definição de "um objeto não mais necessário" para "um objeto não tem outro objeto referenciando ele". Um objeto pode ser coletado pelo garbage collector se não existir referência apontando para este objeto.
Exemplo
var o = {
a: {
b:2
}
};
// 2 objetos são criados. Um é referenciado pelo outro como uma de suas propriedades.
// O outro é referenciado pelo fato de ser atribuído á variável 'o'.
// Obviamente, nenhum pode ser coletado pelo garbage collector
var o2 = o; // a varável 'o2' é a segunda coisa que tem
// uma referência ao objeto
o = 1; // agora, o objeto que estava originalmente no 'o' tem uma referência única
// encorporada pela variável 'o2'
var oa = o2.a; // Referência para a propriedade 'a' do objeto.
// Este objeto agora tem 2 referências: uma como uma propriedade,
// a outra como a variável 'oa'
o2 = "yo"; // The object that was originally in 'o' has now zero
// references to it. It can be garbage-collected.
// However what was its 'a' property is still referenced by
// the 'oa' variable, so it cannot be free'd
oa = null; // what was the 'a' property of the object originally in o
// has zero references to it. It can be garbage collected.
Limitation : cycles
This naive algorithm has the limitation that if objects reference one another (and form a cycle), they may be "not needed anymore" and yet not garbage collectable.
function f(){
var o = {};
var o2 = {};
o.a = o2; // o references o2
o2.a = o; // o2 references o
return "azerty";
}
f();
// Two objects are created and reference one another thus creating a cycle.
// They will not get out of the function scope after the function call, so they
// are effectively useless and could be free'd.
// However, the reference-counting algorithm considers that since each of both
// object is referenced at least once, none can be garbage-collected.
Real-life example
Internet Explorer 6 and 7 are known to have reference-counting garbage collectors for DOM objects. Cycles are a common mistake that can generate memory leaks:
var div;
window.onload = function(){
div = document.getElementById("myDivElement");
div.circularReference = div;
div.lotsOfData = new Array(10000).join("*");
};
In the above example, the DOM element "myDivElement" has a circular reference to itself in the "circularReference" property. If the property is not explicitly removed or nulled, a reference-counting garbage collector will always have at least one reference intact and will keep the DOM element in memory even if it was removed from the DOM tree. If the DOM element holds lots of data (illustrated in the above example with the "lotsOfData" property), the memory consumed by this data will never be released.
Mark-and-sweep algorithm
This algorithm reduces the definition of "an object is not needed anymore" to "an object is unreachable".
This algorithm assumes the knowledge of a set of objects called roots (In JavaScript, the root is the global object). Periodically, the garbage-collector will start from these roots, find all objects that are referenced from these roots, then all objects referenced from these, etc. Starting from the roots, the garbage collector will thus find all reachable objects and collect all non-reachable objects.
This algorithm is better than the previous one since "an object has zero reference" leads to this object being unreachable. The opposite is not true as we have seen with cycles.
As of 2012, all modern browsers ship a mark-and-sweep garbage-collector. All improvements made in the field of JavaScript garbage collection (generational/incremental/concurrent/parallel garbage collection) over the last few years are implementation improvements of this algorithm, but not improvements over the garbage collection algorithm itself nor its reduction of the definition of when "an object is not needed anymore".
Cycles are not a problem anymore
In the first above example, after the function call returns, the 2 objects are not referenced anymore by something reachable from the global object. Consequently, they will be found unreachable by the garbage collector.
The same thing goes with the second example. Once the div and its handler are made unreachable from the roots, they can both be garbage-collected despite referencing each other.
Limitation: objects need to be made explicitly unreachable
Although this is marked as a limitation, it is one that is rarely reached in practice which is why no one usually cares that much about garbage collection.