Similar presentations:
Браузерная реальность. Garbage. Collect
1.
2. garbage.collect()
Роенко АндрейРазработчик API Яндекс.Карт
3.
│ Если бы в JavaScript действительноработала сборка мусора,
большинство npm-модулей
удаляли бы сами себя сразу после
установки
(наверное, так бы сказал) Роберт Сьюэл
4.
5.
План доклада1
Теория
2
Суровая реальность
3
Браузерная реальность
4
Повседневность
6. Зачем мне все это знать?
› Хорошо иметь представление о том как работает ваш инструмент› Вы поймете где можно оптимизировать ваши приложения
› Не будете совершать ошибки
› Перестанете делать «оптимизации»
6
7.
│ Все нетривиальныеабстракции дырявы
Джоэл Спольски
7
8. Теория
9.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
9
10.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
10
11.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
11
12.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
12
13.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
13
14.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
};
14
15.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
15
16.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
16
17.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
17
18.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
18
19.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
19
20.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
20
21.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
21
22.
window.Foo = class Foo {constructor() {
this.x = { y: 'y’ };
}
work(name) {
let z = 'z';
return function () {
console.log(name, this.x.y, z);
this.x = null;
}.bind(this);
}
var foo = new Foo();
};
window.worker = foo.work('Brendan Eich');
window.foo = null;
window.Foo = null;
window.worker();
window.worker = null;
22
23.
24.
class Foo25.
class Foo25
26.
class Foo {constructor()
}
26
27.
class Foo {constructor()
}
27
28.
class Foo {constructor()
work()
}
28
29.
class Foo {constructor()
work()
}
29
30.
3031.
window.Foo = Foo31
32.
foo = new Foo()32
33.
foo = new Foo()33
34.
worker = foo.work()34
35.
fn(){}.bind()35
36.
замыкание36
37.
замыкание37
38.
3839.
window.foo = null39
40.
window.foo = null40
41.
4142. Типичная ошибка: забытая подписка
externalElement.addEventListener('click', () => {if (this.shouldDoSomethingOnClick) {
this.doSomething();
}
})
› Отписывайтесь
› Продумайте время жизни подписки и кто ей владеет
› Зануляйте ссылки (whatever = null)
› Используйте WeakMap
42
43.
4344.
window.Foo = null44
45.
window.Foo = null45
46.
4647. Типичная ошибка
destroy() {this._x = null;
this._y = null;
// еще 10 this._foobar = null
}
› Если объект состоит из ссылок на другие объекты, то никакой
destroy() вам, скорее всего, не нужен
› Зануляйте когда надо, а когда не надо – не зануляйте
47
48. Типичная ошибка бесполезная работа
destroy() {this._x = null;
this._y = null;
// еще 10 this._foobar = null
}
› Если объект состоит из ссылок на другие объекты, то никакой
destroy() вам, скорее всего, не нужен
› Зануляйте когда надо, а когда не надо – не зануляйте
48
49.
4950.
window.worker()// this.x = null
50
51.
window.worker()// this.x = null
51
52.
window.worker = null52
53. Мусор, который не собирается
▌ Объекты с парными функциями create/delete:› createObjectURL(), revokeObjectURL()
› WebGL: create/delete Program/Shader/Buffer/Texture/etc
› ImageBitmap.close()
› indexDb.close()
› …
53
54.
window.worker = null54
55.
5556.
5657.
5758.
window.worker = null58
59.
window.worker = null59
60.
6061.
6162.
6263.
6364.
6465. Что значит мусор?
› Мусор – всё, что не является живым объектом65
66. Что значит живой объект?
› Живой объект – такой объект, до которого можно дойти поссылкам от корневого объекта
66
67. Что значит дойти по ссылкам?
6768. Что значит дойти по ссылкам?
6869. Что значит дойти по ссылкам?
6970. Что значит дойти по ссылкам?
7071. Что значит дойти по ссылкам?
7172. Что значит дойти по ссылкам?
7273. Что значит дойти по ссылкам?
7374. Что значит дойти по ссылкам?
7475. Что значит дойти по ссылкам?
7576. Что значит дойти по ссылкам?
7677. Что значит дойти по ссылкам?
7778. Что значит дойти по ссылкам?
7879. Что значит корневой объект?
7980. Что значит корневой объект?
DOMMicrotask
Macrotask
RAF
Idle
80
81. Что значит корневой объект?
function foo (a, b, c) {function bar (x, y, z) {
const x = {}; // nomem, run gc D:
// ...
DOM
Microtask
Macrotask
RAF
Idle
}
while (whatever()) bar();
}
81
82. Что значит корневой объект?
function foo (a, b, c) {function bar (x, y, z) {
const x = {}; // nomem, run gc D:
// ...
DOM
Microtask
Macrotask
RAF
Idle
}
while (whatever()) bar();
}
82
83. Суровая реальность
84.
85.
86.
new Uint32Array(16 * 2 ** 30);87.
8788.
8889.
8990.
9091.
9192.
9293.
9394.
9495.
9596.
97. No-op collector
▌ Плюсы› Очень простой
› У вас просто нет сборки мусора
▌ Минусы
› Ваше приложение падает
97
98.
9899.
99100.
100101. Mark and Sweep
▌ Плюсы› Простой
› Работает пропорционально количеству мусора
› Хорошо работает, когда у вас мало мусора
▌ Минусы
› Требует сложной логики поиска свободного места
› Фрагментирует память
101
102.
102103.
103104.
104105. Mark and Compact
▌ Плюсы› Дефрагментирует память
▌ Минусы
› Сложный
› Перемещает объекты
› Работает пропорционально количеству живых объектов
› Требует 2-3 прохода по всей памяти
› Медленный
105
106. Сборка мусора не бесплатна
▌ WebGL / WebAudio / WebGPU› Инициализация объектов
› pull() вместо Promise
106
107.
107108.
108109.
109110.
110111.
111112.
112113.
113114. Semispace aka Lisp 2
▌ Плюсы› Дефрагментирует память
› Простой
› Можно совместить с фазой обхода
› Работает пропорционально количеству живых объектов
› Хорошо работает, когда у вас много мусора
▌ Минусы
› Двойной расход памяти
› Перемещает объекты
114
115. На заметку
› Сборщики мусора могут перемещать объекты› Расширения используют двойные ссылки aka «хендлы»
› Например, v8::Local<v8::String>
115
116. Разные алгоритмы
Mark andCompact
Сложность
Сборка
Создание
Фрагментация
Минус
Mark and
Sweep
Semispace
Eden
~ все
~ мусор
~ живые
~ живые
Мееедленно
Медленно
Быстро
Быстро
Быстро
Медленно
Быстро
Быстро
нет
есть
нет
нет
Сложная
реализация
Сложная
аллокация
Память x2
Сбрасывается
116
117. Разные алгоритмы
Mark andCompact
Сложность
Сборка
Создание
Фрагментация
Минус
Mark and
Sweep
Semispace
Eden
~ все
~ мусор
~ живые
~ живые
Мееедленно
Быстро
Быстро
Быстро
Быстро
Медленно
Быстро
Быстро
нет
есть
нет
нет
Сложная
реализация
Сложная
аллокация
Память x2
Сбрасывается
117
118. Лучшее из всех миров
› Можно использовать несколько алгоритмов› Но как?
118
119. Гипотеза поколений?
│ Большинство объектовумирают молодыми
Слабая гипотеза о поколениях
119
120. Сборка поколениями
121.
121122.
122123.
123124.
124125.
125126.
126127.
127128.
128129.
129130.
130131. Ошибка: обман сборщика мусора
› Не создавайте долгоживущий мусор› Сборщик начнет считать, что его не надо собирать
› Типичный пример обмана: LRU-cache
131
132. Когда собирать?
133. Stop the World сборщик
133134. Parallel сборщик
134135. Concurrent сборщик
135136. Что почитать
wikipedia.org/Garbage_collectionmemorymanagement.org
shipilev.net
136
137. Браузерная реальность
(ну, и не только браузерная)Браузерная реальность
138. IoT-движки
› Mark’n’sweep› Stop the world
› …
› На микроконтроллерах
138
139. IoT-движки
› Медленный язык› Секундные фризы
› Фрагментация
› Теперь и в вашем чайнике
139
140. Вот эти ребята
140141. В основном, конечно, вот этот
› Почти весь серверный JavaScript› ~80% клиентского JavaScript’а
› Больше всего информации
› Проще всего читать исходники
141
142. Сборка поколениями
EdenSemispace
Mark and sweep
142
143.
143144.
Major GCMajor GC
144
145. Ошибка: боязнь создания мусора
› Мусор можно создавать, если это действительно мусор› Переиспользование объектов хуже, чем мусор
145
146. Параллельный mark
146147. Сколько стоит все это удовольствие?
│ 1-3%147
148. Сколько стоит все это удовольствие?
│ 1/100 – 1/33148
149. Это много
149150. Это много (для геймдева)
150151. Это много (для геймдева)
const pool = [new Bullet(), new Bullet(), /* ... */];function getFromPool() {
const bullet = pool.find(x => !x.inUse);
bullet.isUse = true;
return bullet;
}
function returnToPool(bullet) { bullet.inUse = false; }
// Frame
const bullet = getFromPool();
// ...
returnToPool(bullet);
151
152. Это много (для геймдева)
const pool = [new Bullet(), new Bullet(), /* ... */];function getFromPool() {
const bullet = pool.find(x => !x.inUse);
bullet.isUse = true;
return bullet;
}
function returnToPool(bullet) { bullet.inUse = false; }
// Frame
const bullet = getFromPool();
// ...
returnToPool(bullet);
152
153. Статистика сборщика мусора: Chromium
> performance.memoryMemoryInfo {
totalJSHeapSize: 10000000,
usedJSHeapSize: 10000000,
jsHeapSizeLimit: 2330000000
}
153
154. Статистика сборщика мусора: Node
> process.memoryUsage(){ rss: 22839296,
heapTotal: 10207232,
heapUsed: 5967968,
external: 12829 }
154
155. Будущее: WeakRef
let cached = new WeakRef(myJson);Ссылки, которые могут быть собраны
в случае нехватки памяти
github.com/tc39/proposal-weakrefs
npmjs.com/package/weak
// 2 часа спустя
let json = cached.deref();
if (!json) {
json = await fetchAgain();
}
155
156. Будущее: GC в WebAssembly
https://github.com/WebAssembly/gc156
157. Что почитать
› v8.dev▌ Исходники
› github.com/v8/v8/tree/7.0.237/src/heap
› github.com/servo/mozjs/blob/master/mozjs/js/src/gc/
› github.com/WebKit/webkit/…/JavaScriptCore/heap/MarkedSpace.cpp
› github.com/Microsoft/ChakraCore/…/HeapAllocator.cpp
› github.com/svaarala/duktape/…/duk_heap_markandsweep.c
› github.com/jerryscript-project/jerryscript/…/ecma-gc.c
157
158. Повседневность
159.
159160.
160161.
161162.
162163.
163164.
164165.
165166.
166167.
167168.
React168
169.
169170.
170171.
171172.
172173.
173174.
React174
175. Если этого вдруг не хватило
› Chromium: about:tracing› Firefox: about:memory, about:performance
› Node: --trace-gc, --expose-gc, require('trace_events')
175
176. Итого
177. Итого
› Сборщик мусора умный› Никто вам не мешает выстрелить себе в ногу
› Не бойтесь создавать мусор
› Следите за перформансом
› Если у вас не SPA, то вы можете забить и ничего не делать
› Большая часть ошибок и сомнительных мест растет из незнания
того, как работает ваш инструмент
› Вы теперь знаете
177
178. Команда JavaScript API Яндекс.Карт ищет разработчиков
› Возможность поучаствовать в работе над одним из крупнейшихAPI рунета
› Нестандартные задачи для фронтенда
› Отсутствие верстки
› yandex.ru/jobs/vacancies/dev/intdev_api_maps
› yandex.ru/jobs/vacancies/dev/dev_graphics_api_maps
178
179.
Слайды: flapenguin.me/talks/gcВопросы?
Роенко Андрей
flapenguin@yandex.ru
flapenguin
@flapenguin
flapenguin.me
@fla_penguin
179
programming