aboutsummaryrefslogtreecommitdiff
path: root/files/pt-pt/web/javascript/memory_management/index.html
blob: 56bfec0f7f96350d3394e2600ef2631afa755b13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
---
title: Gestão de memória
slug: Web/JavaScript/Gestao_Memoria
tags:
  - Desempenho
  - JavaScript
  - memoria
translation_of: Web/JavaScript/Memory_Management
---
<div>{{JsSidebar("Advanced")}}</div>

<h2 id="Introdução">Introdução</h2>

<p>Linguagens de baixo nível, como o C, têm funções pimitivas de gestão de memória, como por exemplo, <code>malloc()</code> e <code>free()</code>. Por outro lado, os valores em JavaScript são alocados quando coisas (objetos, variáveis de texto, etc.) são criadas e "automaticamente" libertadas da memória quando deixam de ser utilizadas. O último processo é chamado de <em>coleção do lixo</em>. Este "automatismo" é uma fonte de mal entendidos e transmite aos programadores de JavaScript ( e aos de outras linguagens de alto nível ) a impressão de que podem decidir não se preocupar com a gestão da memória. Isto é uma erro.</p>

<h2 id="Ciclo_da_duração_da_memória">Ciclo da duração da memória</h2>

<p>Independentemente da linguagem de programação, o ciclo da duração da memória é praticamente sempre o mesmo:</p>

<ol>
 <li>Alocação da memória necessária</li>
 <li>Utilização da memória alocada ( leitura, escrita )</li>
 <li>Libertar a memória alocada quando já não é necessária</li>
</ol>

<p>A segunda etapa é explícita em todas as linguagens. A primeira e a última etapa são explícitas em linguagens de baixo nível mas são, na sua maioria, implícitas em linguagens de alto nível como o JavaScript.</p>

<h3 id="Alocação_no_JavaScript">Alocação no JavaScript</h3>

<h4 id="Inicialização_do_valor">Inicialização do valor</h4>

<p>Com o intuito de não incomodar o programador com alocações, o JavaScript fá-lo juntamente com a declaração de valores.</p>

<pre class="brush: js">var n = 123; // alocação da memória para um número
var s = 'azerty'; // alocação de memória para um texto

var o = {
  a: 1,
  b: null
}; // alocação de memória para um objeto e o seu conteúdo

// (similar ao objeto) alocação de memória para a matriz e
// os seus valores
var a = [1, null, 'abra'];

function f(a) {
  return a + 2;
} // alocação de uma função (que é um objeto que pode ser chamado)

// Expressões de funções também alocam um objeto
someElement.addEventListener('click', function() {
  someElement.style.backgroundColor = 'blue';
}, false);
</pre>

<h4 id="Alocação_através_de_chamadas_de_funções">Alocação através de chamadas de funções</h4>

<p>Algumas chamadas de funções resultam na alocação de um objeto.</p>

<pre class="brush: js">var d = new Date(); // alocação de um objeto <em>Date</em>

var e = document.createElement('div'); // alocação de um elemento DOM</pre>

<p>Alguns métodos alocam novos valores ou objetos:</p>

<pre class="brush: js">var s = 'azerty';
var s2 = s.substr(0, 3); // s2 é uma nova variável de texto
// Dado que as variáveis de texto são de valor imutável,
// 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);
// nova matriz de 4 elementos resultando
// da concatenação dos elementos de <em>a</em> e <em>a2</em>
</pre>

<h3 id="Utilização_de_valores">Utilização de valores</h3>

<p>Basicamente, a utilização de valores significa a leitura e a escrita na memória alocada. Isto pode ser feito através da leitura ou escrita do valor de uma variável ou uma propriedade de um objeto ou até a passagem de um argumento para uma função.</p>

<h3 id="Remoção_quando_a_memória_já_não_é_necessária">Remoção quando a memória já não é necessária</h3>

<p>A maioria dos problemas de gestão de memória aparecem nesta fase. A tarefa mais difícil é descobrir quando "a memória alocada já não é necessária". Em geral, requer que o programador determine quando, no seu código, este pedaço de memória já não é necessário e a liberte.</p>

<p>Linguagens de alto nível têm embutidas um pedaço de código chamado "coleção de lixo", cuja tarefa é rastrear a alocação e uso da memória, para detetar quando um pedaço de memória já não é necessário e neste caso, automaticamente libertá-la. Este processo é uma aproximação dado que o problema geral de se saber quando um pedaço da memória é necessário, é <a class="external" href="https://pt.wikipedia.org/wiki/Decidibilidade">indecisível</a> ( não pode ser solucionado por um algoritmo ).</p>

<h2 id="Coleção_de_lixo">Coleção de lixo</h2>

<p>Como mencionado anteriormente, o problema geral de automaticamente descobrir quando alguma ocupação de memória "já não é necessária", é indecisível. Como consequência, as coleções de lixo implementam uma restrição à solução para o problema em geral. Esta seção explicará as noções necessárias para se compreender os principais algoritmos da coleção de lixo e as suas limitações.</p>

<h3 id="Referências">Referências</h3>

<p>A noção principal dos algoritmos de coleção de lixo baseiam-se na noção de <em>referência</em>. Dentro do contexto da gestão de memória, um objeto é dito referenciar outro objeto, se o primeiro tiver acesso ao segundo ( quer implicitamente quer explicitamente ). Por exemplo, um objeto JavaScript tem uma referência ao seu <a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">protótipo</a> ( referência implícita )  e aos valores das suas propriedades ( referência explícita ).</p>

<p>Neste contexto, a noção de "objeto" é expandida a algo mais abrangente que um objeto JavaScript e também contém âmbitos de funções ( ou âmbito léxico global ).</p>

<h3 id="Contagem_de_referências_na_coleção_de_lixo">Contagem de referências na coleção de lixo</h3>

<p>Este é o algoritmo mais simples da coleção de lixo. Este algoritmo reduz a definição de "um objeto já não é necessário" para "um objeto já não é referenciado por nenhum outro objeto". Um objeto é considerado coletável para a coleção de lixo se tiver nenhuma referenciação por parte de outro objeto.</p>

<h4 id="Exemplo">Exemplo</h4>

<pre class="brush: js">var o = {
  a: {
    b: 2
  }
};
// 2 objetos criados. Um é referenciado por outro como sendo suas propriedades
// O outro é referenciado por virtude ao ser assignado para a variável 'o'
// Obviamente, nenhum podem ser coletado pela coleção de lixo


var o2 = o; // A variável 'o2' é a segunda coisa que
            // tem uma referência ao objeto
o = 1;      // agora, o objeto que era originalmente o 'o' tem uma referência única
            // corporificada pela variável 'o2'

var oa = o2.a; // referência à propriedade 'a' do objeto.
               // Este objeto tem agora 2 referências: uma como propriedade,
               // a outra como variável 'oa'

o2 = 'yo'; // O objeto que era o original 'o' tem agora zero
           // referências a si próprio. Pode ser coletado para a coleção de lixo
           // No entanto, a propriedade 'a' ainda é referenciada pela
           // variável 'oa', logo não pode ser libertado

oa = null; // A propriedade 'a' do objeto original 'o'
           // tem zero referências a si próprio. Pode ser coletado para a  coleção de lixo.
</pre>

<h4 id="Limitação_ciclos">Limitação: ciclos</h4>

<p>Existe uma limitação no que se refere aos ciclos. No exemplo seguinte, dois objetos são criados e referenciam-se mutuamente, criando um ciclo. Estes sairão do âmbito depois de uma chamada a uma função e como tal, são efetivamente inúteis e podem ser libertados. No entanto, o algoritmo de contagem de referências considera que os dois objetos têm pelo menos uma referenciação; nenhum pode ser coletado para a coleção de lixo .</p>

<pre class="brush: js">function f() {
  var o = {};
  var o2 = {};
  o.a = o2; // o referencia o2
  o2.a = o; // o2 referencia o

  return 'azerty';
}

f();
</pre>

<h4 id="Exemplo_de_aplicação_real">Exemplo de aplicação real</h4>

<p>O Internet Explorer 6 e 7 são conhecidos por terem um coletor de contagem de referenciações de coleções de lixo para os objetos DOM. Os ciclos são um erro comum que podem gerar percas de memória:</p>

<pre class="brush: js">var div;
window.onload = function() {
  div = document.getElementById('myDivElement');
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join('*');
};
</pre>

<p>No exemplo acima, o elemento DOM "myDivElement" tem uma referência circular a si próprio na propriedade "circularReference". Se a propriedade não for explicitamente removida ou anulada, o coletor de contagem de referências da coleção de lixo, irá sempre ter uma referenciação intacta e irá manter o elemento DOM na memória mesmo quando for removido da árvore DOM. Se o elemento DOM contém bastante dados ( ilustrado no exemplo acima com a propriedade "lotsOfData" ), a memória consumida por estes dados nunca será libertada.</p>

<h3 id="Algoritmo_de_marcação_e_limpeza">Algoritmo de marcação e limpeza</h3>

<p>Este algoritmo reduz a definição de "um objeto já não é necessário" para "um objeto é inalcançável".</p>

<p>Este algoritmo assume o conhecimento de um conjunto de objetos chamados "roots" ( em JavaScript, a <em>root</em> - raiz - é um objeto global ). Periodicamente, a coleção de lixo começará pela raíz e procurará todos os objetos que são referenciados a partir desta; depois todos os que são referenciados a partir dos seguintes, etc.. Começando pela raiz, o coletor de lixo irá localizar objetos inalcançáveis; coletando-os.</p>

<p>Este algoritmo é melhor do que o anterior dado que "um objeto tem zero referenciações" leva a um objeto inalcançável. O contrário não é verdade como vimos nos ciclos.</p>

<p>Desde 2012, todos os modernos navegadores de Internet contêm um coletor de marcação e limpeza. Todos os melhoramentos feitos na área da coleção de lixo no JavaScript ( generalização / incrementação / concorrência / coleção de lixo paralela ) nos últimos anos são implemetações de melhoramento deste algoritmo, mas não melhoramentos do algoritmo de coleção de lixo em si nem a sua redução à definição de que quando "um objeto já não é necessário".</p>

<h4 id="Os_ciclos_já_não_são_um_problema.">Os ciclos já não são um problema.</h4>

<p>No primeiro exemplo acima, depois do retorno da chamada à função, 2 objetos já não são referenciados por algo alcançável no objeto global. Consequentemente, irão ser localizados pelo coletor da coleção de lixo.</p>

<h4 id="Limitações_os_objetos_necessitam_de_ser_explicitamente_inalcançáveis">Limitações: os objetos necessitam de ser explicitamente inalcançáveis</h4>

<p>Apesar disto estar marcado como uma limitação, é um caso que raramente ocorre na prática pelo que é por isto que ninguém se preocupa muito com a coleção de lixo.</p>

<h2 id="Ver_também">Ver também</h2>

<ul>
 <li><a class="external" href="http://www.ibm.com/developerworks/web/library/wa-memleak/">Aritgo da IBM sobre "Padrões de percas de memória no JavaScript" (2007)</a></li>
 <li><a class="external" href="http://msdn.microsoft.com/en-us/magazine/ff728624.aspx">Artigo da Kangax sobre como registar um apontador de evento e evitar percas de memória (2010)</a></li>
 <li><a href="https://developer.mozilla.org/pt-PT/docs/Mozilla/Performance" title="https://developer.mozilla.org/en-US/docs/Mozilla/Performance">Desempenho</a></li>
</ul>