← Назад к вопросам

Какие знаешь способы решения утечки памяти?

1.0 Junior🔥 61 комментариев
#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы решения утечки памяти в JavaScript

Утечка памяти — это ситуация, когда приложение продолжает занимать память, хотя больше не использует определённые объекты. В браузере это приводит к замедлению страницы, высокому потреблению ОЗУ и потенциальному краху. Рассмотрю основные типы утечек и способы их решения.

Проблема 1: Неудаленные обработчики событий

Обработчики событий, которые не удаляются, остаются в памяти, даже если элемент удалён из DOM:

// Утечка: слушатель остаётся в памяти
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log('Нажата кнопка');
});

// Позже удаляем элемент, но слушатель остаётся
button.remove();

// Решение: удалить слушатель перед удалением элемента
button.removeEventListener('click', handleClick);
button.remove();

Правильный паттерн:

function handleClick() {
  console.log('Нажата кнопка');
}

const button = document.getElementById('myButton');
button.addEventListener('click', handleClick);

// При очистке
button.removeEventListener('click', handleClick);
button.remove();

// Или использовать AbortController (современный способ)
const controller = new AbortController();
button.addEventListener('click', handleClick, { signal: controller.signal });
// При очистке
controller.abort();

Проблема 2: Глобальные переменные и незакрытые замыкания

Переменные, установленные в глобальной области видимости, остаются в памяти вечно:

// Утечка: переменная в глобальной области
let bigArray = new Array(1000000); // Останется в памяти
window.cachedData = bigArray; // Точно останется

// Решение: использовать локальные переменные
(function() {
  let bigArray = new Array(1000000);
  // При выходе из функции будет очищена
})();

// Решение: явное удаление глобальных переменных
window.cachedData = null;
delete window.cachedData;

Проблема с замыканиями:

// Утечка: замыкание держит ссылку на данные
function createHandler() {
  const largeData = new Array(1000000);
  return function handler() {
    console.log(largeData.length);
  };
}

const handler = createHandler();
// largeData будет в памяти, пока существует handler

// Решение: очистить ссылку
handler = null;

Проблема 3: Интервалы и таймауты

setInterval и setTimeout, если не очищены, могут запускать неактуальный код:

// Утечка: интервал продолжает работать
let timerId = setInterval(() => {
  console.log('Каждую секунду');
}, 1000);

// При переходе на другую страницу в SPA интервал остаётся активным
// Решение: очистить таймер
clearInterval(timerId);

// Паттерн для React
import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    const timerId = setInterval(() => {
      // ...
    }, 1000);

    // Cleanup функция запустится при размонтировании
    return () => clearInterval(timerId);
  }, []);

  return <div>Component</div>;
}

Проблема 4: Циклические ссылки и неправильные ссылки на DOM

Объекты, содержащие ссылки на элементы DOM, после удаления элемента могут остаться в памяти:

// Утечка: объект содержит ссылку на удалённый элемент
const config = {
  element: document.getElementById('myDiv'),
  data: new Array(1000000)
};

document.getElementById('myDiv').remove();
// config всё ещё в памяти, держа ссылку на удалённый элемент

// Решение: очистить ссылки на DOM элементы
config.element = null;
config.data = null;

Проблема 5: Слушатели observer и вебхуков

MutationObserver, IntersectionObserver и другие наблюдатели должны быть очищены:

// Утечка: наблюдатель не отключен
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    console.log(entry.isIntersecting);
  });
});

observer.observe(document.getElementById('target'));

// Переход на новую страницу, наблюдатель остаётся активным

// Решение: отключить наблюдатель
observer.disconnect();

// Правильный паттерн в React
useEffect(() => {
  const observer = new IntersectionObserver(() => {});
  observer.observe(element);

  return () => observer.disconnect();
}, []);

Проблема 6: Кеширование без лимита

Бесконечный кеш может привести к утечке памяти:

// Утечка: кеш растёт бесконечно
const cache = {};

function getData(key) {
  if (cache[key]) return cache[key];
  const data = fetch(key).then(r => r.json());
  cache[key] = data;
  return data;
}

// После миллиона запросов кеш заполнит всю память

// Решение: использовать ограниченный кеш
class LimitedCache {
  constructor(maxSize = 100) {
    this.cache = new Map();
    this.maxSize = maxSize;
  }

  set(key, value) {
    if (this.cache.size >= this.maxSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }

  get(key) {
    return this.cache.get(key);
  }
}

// Или использовать WeakMap для автоматической очистки
const weakCache = new WeakMap();

Проблема 7: Третьесторонние библиотеки и плагины

Библиотеки могут оставлять слушатели или ссылки:

// Проверить наличие утечек в браузере
// Chrome DevTools -> Memory -> Heap Snapshot

// Решение: явная очистка перед выгрузкой страницы
window.addEventListener('beforeunload', () => {
  // Очистить все слушатели, кеши, таймеры
  // Вызвать cleanup методы библиотек
});

Практические инструменты для поиска утечек

// Chrome DevTools Memory tab
// 1. Снять снимок heap'а (baseline)
// 2. Выполнить операцию (открыть/закрыть компонент)
// 3. Снять второй снимок
// 4. Сравнить — найдутся утечки

// Использование performance API
performance.memory.usedJSHeapSize
performance.memory.totalJSHeapSize
performance.memory.jsHeapSizeLimit

setInterval(() => {
  console.log('Используется:', Math.round(performance.memory.usedJSHeapSize / 1048576) + ' MB');
}, 1000);

Типовые сценарии в фронтенд-приложениях

React компоненты:

  • Всегда очищать обработчики событий в cleanup функции useEffect
  • Отменять fetch запросы при размонтировании
  • Удалять slushobservers в cleanup функции

Single Page Applications:

  • Очищать слушатели при переходе между страницами
  • Отключать таймеры на неактивных страницах
  • Очищать кеши при выходе пользователя

Работа с большими данными:

  • Использовать пагинацию вместо загрузки всего
  • Очищать старые данные из памяти
  • Использовать streams для обработки больших файлов

Выводы

  • Всегда удаляй обработчики событий при удалении элементов
  • Очищай таймеры и интервалы в cleanup функциях
  • Избегай циклических ссылок на объекты
  • Используй WeakMap для кешей с автоматической очисткой
  • Регулярно проверяй память через Chrome DevTools
  • Помни о правиле: при создании ресурса — создай способ его очистки
Какие знаешь способы решения утечки памяти? | PrepBro