← Назад к вопросам
Как происходит взаимодействие с оперативной памятью в JS?
2.0 Middle🔥 162 комментариев
#JavaScript Core
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Управление памятью в JavaScript
JavaScript управляет памятью автоматически через сборщик мусора (garbage collector). Понимание того, как это работает, критично для написания производительного кода без утечек памяти.
1. Память: Stack vs Heap
Вся память в JavaScript делится на две части:
// STACK - быстрая, маленькая память для примитивов
// Хранит: числа, строки, булевы значения, ссылки на объекты
// Размер: ограничен, обычно 1-8 MB
// Очистка: автоматически когда переменная выходит из scope
function example() {
let a = 5; // stack: a = 5
let b = "hello"; // stack: b -> ссылка на строку в heap
let c = true; // stack: c = true
// При выходе из функции: a, b, c удаляются из stack
}
// HEAP - медленная, большая память для объектов
// Хранит: объекты, массивы, функции
// Размер: большой, может быть гигабайты
// Очистка: через garbage collector (GC) когда нет ссылок
function createObject() {
let obj = { // stack: obj -> ссылка на heap
name: 'John', // heap: объект с данными
age: 30
};
return obj; // объект остаётся в heap
}
let user = createObject(); // stack: user -> ссылка на heap
// Объект в heap не удаляется, потому что на него есть ссылка
2. Garbage Collection (автоматическое управление памятью)
Когда нет ссылок на объект в памяти, он удаляется:
// Пример 1: Объект с одной ссылкой
let user = { name: 'John' }; // объект создан в heap
user = null; // ссылка удалена, объект может быть удален GC
// Пример 2: Массив с объектами
let users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 }
];
// В heap находятся 3 объекта: массив + 2 объекта внутри
users = []; // массив теперь пуст
// Но объект всё ещё существует! GC должен удалить его
users = null; // теперь GC может удалить массив и всё содержимое
// Пример 3: Циклические ссылки (раньше были проблемы)
let obj1 = {};
let obj2 = {};
obj1.ref = obj2; // obj1 -> obj2
obj2.ref = obj1; // obj2 -> obj1 (циклическая ссылка)
obj1 = null;
obj2 = null;
// Современные GC справляются с этим, удалят оба объекта
// Но в старом IE было утечки памяти из-за циклических ссылок
3. Утечки памяти: как они происходят
// Утечка 1: Забытые таймеры
function setupTimer() {
let data = new Array(1000000); // большой массив
setInterval(() => {
console.log(data.length); // используем data
}, 1000);
// УТЕЧКА: setInterval остаётся в памяти, data не может быть удалена
// Решение:
// const intervalId = setInterval(...);
// clearInterval(intervalId); когда нужно очистить
}
// Утечка 2: Event listeners не удалены
const button = document.querySelector('button');
button.addEventListener('click', function handler() {
// УТЕЧКА: если handler содержит большие данные
// и addEventListener не очищен, это утечка
});
// Решение:
// button.removeEventListener('click', handler);
// Или использовать once: { once: true }
// Утечка 3: Замыкания с большими данными
function createComponent() {
let bigData = new Array(1000000);
return {
getData: () => bigData // замыкание держит bigData в памяти
};
}
let comp = createComponent();
// bigData остаётся в памяти даже если не используется
comp = null; // только тогда bigData может быть удалена
// Утечка 4: Глобальные переменные (window)
window.largeArray = new Array(1000000); // это в памяти ВЕЧНО
// Решение: избегай глобальных переменных
// Утечка 5: Detached DOM nodes
let element = document.querySelector('#myDiv');
element.parentNode.removeChild(element); // элемент удалён из DOM
// Но если element всё ещё в памяти (переменная), это утечка
element = null; // освобождаем
4. Как GC работает: Mark and Sweep
// Упрощённое объяснение алгоритма Mark and Sweep
// ШАГ 1: MARK (отметить живые объекты)
// GC начинает с корневых объектов (stack переменные, глобальные)
// И рекурсивно идёт по всем ссылкам
let root = { name: 'root' };
root.child = { name: 'child' };
root.child.grandchild = { name: 'grandchild' };
// В памяти GC создаёт граф:
// root -> child -> grandchild
// Все эти объекты помечены как "живые"
// ШАГ 2: SWEEP (удалить мёртвые объекты)
let orphan = { name: 'orphan' };
orphan = null; // ссылка удалена
// orphan объект остался в памяти, но его никто не использует
// При следующем GC он не будет помечен как живой
// И будет удалён из памяти
// Пример временной шкалы:
// t=0: orphan создан
// t=1: orphan = null (ссылка удалена)
// t=2: GC запускается -> не находит orphan в графе живых объектов
// t=3: orphan удаляется из памяти
5. Профилирование памяти в Chrome DevTools
// В Console можно проверить использование памяти
// performance.memory API
if (performance.memory) {
console.log('Used JS heap: ', Math.round(performance.memory.usedJSHeapSize / 1024 / 1024), 'MB');
console.log('Total JS heap: ', Math.round(performance.memory.totalJSHeapSize / 1024 / 1024), 'MB');
console.log('Heap limit: ', Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024), 'MB');
}
// Пример:
// Used JS heap: 45 MB
// Total JS heap: 62 MB
// Heap limit: 2048 MB
6. Оптимизация использования памяти
// Проблема: создание большого числа объектов
for (let i = 0; i < 1000000; i++) {
let obj = { x: i, y: i * 2 }; // 1 миллион объектов!
// ПЛОХО: много операций allocation/deallocation
}
// Решение: переиспользуй объекты или используй типизированные массивы
const results = new Array(1000000);
for (let i = 0; i < 1000000; i++) {
results[i] = { x: i, y: i * 2 };
}
// Или ещё лучше - используй Float64Array
const xValues = new Float64Array(1000000);
const yValues = new Float64Array(1000000);
for (let i = 0; i < 1000000; i++) {
xValues[i] = i;
yValues[i] = i * 2;
}
// TypedArrays занимают меньше памяти
// Проблема: хранение больших строк
let data = '';
for (let i = 0; i < 100000; i++) {
data += 'some string'; // ПЛОХО: каждый раз создаётся новая строка
}
// Решение: используй array join
const parts = [];
for (let i = 0; i < 100000; i++) {
parts.push('some string');
}
const data = parts.join(''); // ХОРОШО: один раз объединяем
7. WeakMap и WeakSet для временных ссылок
// Обычный Map держит объекты в памяти
const cache = new Map();
const key = { id: 1 };
cache.set(key, 'value');
key = null; // объект всё ещё в памяти, потому что Map его держит!
// WeakMap не держит ссылки - объект может быть удалён
const weakCache = new WeakMap();
const key = { id: 1 };
weakCache.set(key, 'value');
key = null; // объект может быть удалён GC, WeakMap не помешает
// Практический пример: кэш для DOM элементов
const elementsData = new WeakMap();
const button = document.querySelector('button');
elementsData.set(button, { clicks: 0 });
// Когда button удалён из DOM, данные тоже удалятся
8. Асинхронный код и утечки
// УТЕЧКА: Promise, которые никогда не resolve
function problematicAsync() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let bigData = new Array(1000000);
// забыли resolve/reject
// Promise остаётся в памяти, bigData тоже
}, 10000);
});
}
// РЕШЕНИЕ: всегда resolve/reject Promise
async function goodAsync() {
try {
const result = await fetch('/api');
return await result.json();
} catch (error) {
console.error(error);
throw error; // важно!
}
}
// УТЕЧКА: Observer, которые не unsubscribe
const observer = new IntersectionObserver((entries) => {
let data = new Array(100000);
});
observer.observe(element);
// УТЕЧКА: observer никогда не удалён
// РЕШЕНИЕ:
// observer.unobserve(element);
// observer.disconnect();
Практические советы
- Всегда очищай таймеры и слушатели событий
const unsubscribe = addEventListener('click', handler);
// Когда не нужно:
unsubscribe(); // или removeEventListener
- Избегай циклических ссылок с большими данными
// ПЛОХО
obj.self = obj; // циклическая ссылка
// ХОРОШО
// не держи циклические ссылки на себя
- Профилируй регулярно
// Chrome DevTools > Memory tab
// Бери snapshots и смотри на утечки
- Используй WeakMap для временных данных
const metadata = new WeakMap();
metadata.set(obj, { info: 'data' });
Понимание управления памятью в JavaScript - это ключ к написанию производительных приложений без утечек памяти, особенно для долгоживущих приложений (SPA, Node.js серверы).