Как дебажить приложение и находить утечки памяти
Общий процесс дебага
1
Определить проблему
- Приложение зависает, тормозит или потребляет слишком много памяти?
- Используйте инструменты браузера:
DevTools
(Performance, Memory),Console
,React Profiler
.
2
Анализ логов и ошибок
- Добавляйте
console.log
,console.trace
,console.error
для отслеживания. - Используйте внешние инструменты: Sentry, LogRocket, Datadog для логирования ошибок.
3
Профилирование производительности
Performance
вкладка в Chrome DevTools — анализ FPS, времени загрузки и рендера.React Profiler
— выявление лишних ререндеров.Lighthouse
— рекомендации по оптимизации.
4
Анализ утечек памяти
- Используйте вкладку
Memory
и снимки Heap Snapshot. - Ищите объекты, которые не удаляются после размонтирования компонента.
- Следите за
setInterval
,setTimeout
,WebSocket
,EventListeners
.
Причины утечек памяти и решения
Забытые таймеры и интервалы
useEffect(() => {
const interval = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(interval); // обязательно очищаем!
}, []);
Неудалённые обработчики событий
useEffect(() => {
const handleScroll = () => console.log("scrolling...");
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
Замыкания, удерживающие данные
Замыкания могут "запомнить" ссылки на объекты, и те не будут удалены. Решение: использовать
useRef
,useCallback
или вовремя очищать.
Глобальные переменные
window.myCache = largeObject; // плохая практика
// лучше явно очищать
window.myCache = null;
Ссылки в useRef и DOM
const ref = useRef(null);
useEffect(() => {
ref.current = document.getElementById("my-element");
return () => {
ref.current = null; // освобождаем ссылку
};
}, []);
WebSocket или подписки, не закрытые вовремя
useEffect(() => {
const socket = new WebSocket("wss://example.com");
return () => socket.close(); // обязательно закрываем
}, []);
Отсутствие виртуализации на больших списках
Рендеринг 1000+ элементов без виртуализации замедляет приложение и "забивает" память.
Решение: использовать библиотеки:
Как найти утечки памяти
Вкладка Memory → Heap Snapshot
- Перейдите в DevTools → Memory → Take Snapshot
- Взаимодействуйте с компонентом (например, откройте/закройте модалку)
- Снимите ещё один Snapshot
- Сравните: должны исчезнуть временные объекты
Вкладка Performance → Record Allocations
- Нажмите «Start recording allocations»
- Используйте компонент
- Нажмите «Stop» и ищите утечки, которые остаются после удаления компонента
Инструменты мониторинга
- Chrome DevTools — встроенный анализатор памяти и профайлер
- Sentry, Datadog, LogRocket — обнаружение и логирование утечек и ошибок в проде
- why-did-you-render — помогает найти лишние ререндеры компонентов
Важно:
Утечки памяти особенно опасны в SPA (Single Page Application), т.к. приложение живёт долго. Даже небольшие утечки могут привести к деградации производительности со временем.
Вывод
- Следите за очисткой side-effects (
setInterval
,event listeners
,sockets
) - Используйте DevTools → Memory → Snapshot для диагностики
- Не храните глобальные объекты или замыкания, которые не очищаются
- Для больших списков — применяйте виртуализацию
- Используйте React Profiler и профилирование в браузере, чтобы находить узкие места
// Пример шаблона безопасного useEffect
useEffect(() => {
const res = startSomething();
return () => {
cleanupSomething(res);
};
}, []);