aboutsummaryrefslogtreecommitdiff
path: root/files/de/web/javascript/memory_management/index.html
blob: 25928eed266e13c1b9a104a7c6ac80e752ff979b (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
---
title: Speicherverwaltung
slug: Web/JavaScript/Memory_Management
tags:
  - JavaScript
translation_of: Web/JavaScript/Memory_Management
original_slug: Web/JavaScript/Speicherverwaltung
---
<div>{{JsSidebar("Advanced")}}</div>

<h2 id="Einleitung">Einleitung</h2>

<p>Bei Low-Level Programmiersprachen wie C, die zur systemnahen Programmierung eingesetzt werden, existieren Funktionen - wie z. B. <code>malloc()</code> und <code>free()</code> - womit explizit Speicher angefordert und wieder freigegeben werden kann. Bei JavaScript gibt es keine solchen Funktionen, sondern der benötigte Speicher wird bei der Erzeugung neuer Objekte (z. B. benutzerdefinierte Objekte oder vordefinierte Objekte wie <code>String</code>) zugewiesen. Die Speicherfreigabe erfolgt automatisch im Hintergrund durch die <em>Garbage Collection</em> (wörtlich: "Müllabfuhr"; automatische Speicherbereinigung). Dieser Automatismus führt oft zu Verwirrung und lässt so manchen Entwicklern irrtümlich glauben, man müsse sich über die Speicherverwaltung keine Gedanken machen.</p>

<h2 id="Zyklus_der_Speichernutzung">Zyklus der Speichernutzung</h2>

<p>Unabhängig von der eingesetzten Programmiersprache läuft der Zyklus für die Speichernutzung immer ungefähr gleich ab:</p>

<ol>
 <li>Alloziierung/Zuweisung des benötigten Speichers.</li>
 <li>Benutzung des Speichers (lesen, schreiben).</li>
 <li>Freigabe des alloziierten Speichers, wenn er nicht mehr benötigt wird.</li>
</ol>

<p>Der erste und zweite Schritt erfolgt bei allen Programmiersprachen explizit. Der letzte Schritt, die Freigabe des Speichers, wird bei Low-Level-Sprachen explizit und bei höheren Programmiersprachen wie JavaScript meist implizit vorgenommen.</p>

<h3 id="Speicherzuweisung_bei_JavaScript">Speicherzuweisung bei JavaScript</h3>

<h4 id="Initialisierung_von_Werten">Initialisierung von Werten</h4>

<p>Um dem Programmierer die Arbeit zu erleichtern, weist JavaScript bei der Zuweisung von Werten je nach Typ automatisch die benötigte Menge an Speicher zu:</p>

<div style="overflow: hidden;">
<pre class="brush: js">var n = 123; // alloziiert Speicher für eine Number
var s = "azerty"; // alloziiert Speicher für einen String

var o = {
  a: 1,
  b: null
}; // alloziiert Speicher für das Objekt und enthaltene Werte

var a = [1, null, "abra"]; // (wie bei Objekten) alloziiert Speicher für das Array und enthaltene Werte

function f(a){
  return a + 2;
} // alloziiert eine Function (die ein aufrufbares Objekt ist)

// Funktions-Ausdrücke alloziieren ebenfalls ein Objekt
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);
</pre>
</div>

<h4 id="Allokation_über_Funktionsaufrufe">Allokation über Funktionsaufrufe</h4>

<p>Einige Funktionen allozieren Objekte:</p>

<pre class="brush: js">var d = new Date();
var e = document.createElement('div'); // alloziiert ein DOM-Element
</pre>

<p>Andere Methoden allozieren neue Werte oder Objekte:</p>

<pre class="brush: js">var s = "azerty";
var s2 = s.substr(0, 3); // s2 ist ein neuer String
// Da Strings immutable sind, weist JavaScript evtl. hier
// keinen neuen Speicher zu, sondern merkt sich nur [0, 3],
// dass s2 die ersten beiden Elemente von s enthält

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // neues Array mit den 4 Elementen von a und a2
</pre>

<h3 id="Werte">Werte</h3>

<p>Bei der Zuweisung von Werten wird in den Speicher geschrieben und beim Abfragen von Werten der Speicher ausgelesen. Weist man also z. B. einer Objekteigenschaft einen Wert zu oder übergibt einen Wert als Argument an eine Funktion, wird dieser in den Speicher geschrieben und beim Abfragen des Werts der Speicher ausgelesen.</p>

<h3 id="Freigabe_des_Speichers_wenn_dieser_nicht_mehr_benötigt_wird">Freigabe des Speichers, wenn dieser nicht mehr benötigt wird</h3>

<p>Bei diesem Vorgang tauchen am häufigsten Probleme mit der Speicherverwaltung auf, da es keine leichte Aufgabe ist, zu entscheiden, wann der benötigte Speicher tatsächlich nicht mehr gebraucht wird. Oftmals muss der Entwickler selbst bestimmen, an welcher Stelle die Freigabe eines Speicherbereichs erfolgen soll.</p>

<p>Höhere Programmiersprachen setzten häufig eine Softwarekomponente namens <em>Garbage Collector</em> ein, dessen Aufgabe darin besteht, die Speicherallokation zu überwachen und nicht mehr benötigten Speicher wieder freizugeben. Dieser Prozess basiert auf einer Abschätzung, da das zugrundeliegende Problem - zu entscheiden, wann Speicher nicht mehr benötigt wird - <a class="external" href="http://de.wikipedia.org/wiki/Entscheidbar">unentscheidbar ist</a> (nicht durch einen Algorithmus lösbar).</p>

<h2 id="Garbage_Collection">Garbage Collection</h2>

<p>Wie oben schon erwähnt, gibt es keine eindeutige Lösung für das zugrunde liegende Problem. Aus diesem Grund implementieren Garbage Collections einen beschränkten Algorithmus. Dieser Abschnitt liefert Informationen zum Verständnis der wichtigsten Algorithmen der Garbage Collection und deren Beschränkungen.</p>

<h3 id="Referenzen">Referenzen</h3>

<p>Der Grundgedanke auf dem Garbage Collection-Algorithmen hauptsächlich basieren, ist die Idee der <em>Referenz</em>. Im Zusammenhang mit der Speicherverwaltung spricht man davon, dass ein Objekt ein anderes Objekt referenziert, wenn ersteres (entweder implizit oder explizit) Zugriff auf letzteres hat. Zum Beispiel hat ein JavaScript-Objekt eine Referenz auf dessen Prototype (implizite Referenz) und dessen Eigenschaftswerte (explizite Referenz).</p>

<p>In diesem Kontext wird also der Begriff <em>Objekt</em> etwas breiter aufgefasst, als im Zusammenhang mit JavaScript-Objekten und beinhaltet außerdem auch Sichtbarkeitsbereiche von Funktionen (bzw. den globalen Sichtbarkeitsbereich).</p>

<h3 id="Referenzzählung_der_Garbage_Collection">Referenzzählung der Garbage Collection</h3>

<p>Dies ist der naivste Algorithmus der Garbage Collection. Er reduziert einfach "ein Objekt wird nicht mehr benötigt" auf "kein anderes Objekt referenziert auf das Objekt". Ein Objekt wird also dann von der Garbage Collection entsorgt, wenn keine Referenzen auf das Objekt existieren.</p>

<h4 id="Beispiel">Beispiel</h4>

<pre class="brush: js">var o = {
  a: {
    b:2
  }
}; // 2 Objekte wurden erstellt. Das eine referenziert auf das andere als dessen Eigenschaft.
// Das Objekt a wird referenziert, weil es der Variablen o zugewiesen wurde.
// Da auf alle Objekte irgendwie referenziert wird, kann keines entsogt werden

var o2 = o; // Die Variable 'o2' ist das zweite Objekt mit einer Referenz auf das Objekt
o = 1; // nun hat das Objekt, das sich ursprünglich in o befand eine einzige Referenz durch 'o2'

var oa = o2.a; // Referenz auf die Eigenschaft 'a' des Objekts.
// Nun hat das Objekt 2 Referenzen: Eine als Eigenschaft, die andere als Variable 'oa'

o2 = "yo"; // Das objekt, welches sich anfangs in 'o' befand hat nun keine Referenzen mehr
// und kann von der Garbage Collection entsorgt werden.
// Jedoch wird die Eigenschaft 'a' noch immer durch die Variable 'oa' referenziert und kann daher nicht freigegeben werden

oa = null; // Nun existieren keine Refrenzen mehr auf die Eigenschaft 'a'
// und das Objekt kann entsorgt werden.
</pre>

<h4 id="Beschränkung_gegenseitige_Referenzen">Beschränkung: gegenseitige Referenzen</h4>

<p>Dieser naive Algorithmus ist deshalb beschränkt, weil Objekte, die gegenseitig aufeinender referenzieren, gar nicht mehr benötigt werden könnten, aber nicht entsorgt werden dürfen, da noch auf sie referenziert wird.</p>

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

  return "azerty";
}

f();
// die Objekte werden erzeugt und referenzieren gegenseitig aufeinander.
// Sie werden außerhalb des Sichtbarkeitsbereichs der Funktion nicht verwendet
// und könnten daher nach der Funktionsausführung freigegeben werden
// Der Referenzzählungsalgorithmus erkennt jedoch nur, dass auf beide Objekte
// noch referenziert wird und entsorgt daher keines der beiden
</pre>

<h4 id="Praxisbeispiel">Praxisbeispiel</h4>

<p>Bei Internet Explorer 6, 7 ist bekannt, dass sie einen Garbage Collector mit Referenzzählung für DOM-Objekte einsetzen. Es gibt daher ein bekanntes Muster für die systematische Erzeugung von Speicherlecks:</p>

<pre class="brush: js">var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // Das div hat eine Referenz über die Event-Handler-Eigenschaft 'onclick'.
// Der Handler hat auch eine Referenz auf das div, da die div-Variable
// innerhalb Sichtbarkeitsbereich der Funktion ansprechbar ist.
// Dieser Kreislauf sorgt dafür, dass beide Objekte nicht entsorgt werden
// und ein Speicherleck entsteht
</pre>

<h3 id="Mark-and-Sweep-Algorithmus_2"><span class="mw-headline" id="Mark-and-Sweep-Algorithmus">Mark-and-Sweep-Algorithmus</span></h3>

<p>Dieser Algorithmus reduziert die Definition "ein Objekt wird nicht mehr gebraucht" auf "ein Objekt ist nicht erreichbar".</p>

<p>Der Algorithmus nimmt an, dass ein Satz von <em>root</em>-Objekten (bei JavaScript das globale Objekt) existiert. Der Algorithmus sucht dann von diesen Anfangsobjekten aus alle referenzierten Objekte, dann von den referenzierten Objekten alle Objekte, die auf andere Objekte referenzieren und so weiter. Auf diese Weise erkennt der Algorithmus alle <em>erreichbaren</em> Objekte und sammelt alle unerreichbaren Objekte.</p>

<p>Dieser Algorithmus ist besser als der vorherige, da Objekte, die nicht referenziert sind als unerreichbar gelten, jedoch Objekte, die unerreichbar sind, nicht unbedingt keine Referenzen haben (gegenseitige Referenz).</p>

<p>Im Jahr 2012 arbeiten alle modernen Browser mit einem Mark-and-Sweep-Garbage Collector. Alle Verbesserungen der letzen Jahre im Bereich der Garbage Collection bei JavaScript (generational/incremental/concurrent/parallel garbage collection) sind Verbesserungen der Implementierung des Algorithmus, jedoch keine Änderungen des Algorithmus selbst oder der Definition von "ein Objekt wird nicht mehr gebraucht".</p>

<h4 id="Gegenseitige_Verweise_stellen_kein_Problem_mehr_dar">Gegenseitige Verweise stellen kein Problem mehr dar</h4>

<p>Bei dem Beispiel oben zu gegenseitigen Verweisen wird auf die beiden Objekte nicht mehr von einem Objekt aus verwiesen, welches über das globale Objekt erreichbar ist. Daher werden sie vom Garbage Collector als unerreichbar angesehen.</p>

<p>Dasselbe gilt für das nächste Beispiel. Sobald das div und der Handler unerreichbar geworden sind, werden sie vom Gabage Collector entsorgt, obwohl sie noch gegenseitig aufeinander verweisen.</p>

<h4 id="Einschränkung_Objekte_müssen_explizit_unerreichbar_gemacht_werden">Einschränkung: Objekte müssen explizit unerreichbar gemacht werden</h4>

<p>Obwohl dies als Einschränkung gilt, gibt es in der Praxis kaum solche Situationen und deshalb denkt kaum jemand über die Garbage Collection nach.</p>

<h2 id="Weitere_Informationen">Weitere Informationen</h2>

<ul>
 <li><a class="external" href="http://www.ibm.com/developerworks/web/library/wa-memleak/">IBM-Artikel "Memory leak patterns in JavaScript" (2007)</a></li>
 <li><a class="external" href="http://msdn.microsoft.com/en-us/magazine/ff728624.aspx">Kangax-Artikel zu Event Handlern und Vermeidung von Speicherlecks (2010)</a></li>
 <li><a href="https://wiki.mozilla.org/Performance:Leak_Tools" title="https://wiki.mozilla.org/Performance:Leak_Tools">Performance: Leak Tools</a></li>
</ul>