Перечислите основные случаи, приводящие к утечке памяти и как с этим бороться?nodejs-26

Основные механизмы обработки

1. process.on

Глобальный обработчик непойманных исключений:

process.on('uncaughtException', (err, origin) => {
  console.error('Uncaught Exception:', err);
  // Обязательно завершаем процесс в продакшене
  process.exit(1);
});

Особенности:

  • Перехватывает исключения, не пойманные try/catch
  • Последний барьер перед падением процесса
  • Не должен использоваться для продолжения работы

2. process.on

Обработка необработанных Promise rejections:

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

Ключевые особенности Node.js

1. Поведение по умолчанию

  • До Node.js 15: Предупреждение в консоль, продолжение работы
  • После Node.js 15: Завершение процесса (аналогично uncaughtException)

2. Отличия от браузерного JavaScript

  • В браузере ошибки попадают в window.onerror
  • В Node.js нет глобального window объекта, только process

3. Домены

Ранее использовались domains для обработки:

const domain = require('domain');
const d = domain.create();
d.on('error', (err) => {
  console.error('Domain caught:', err);
});
d.run(() => {
  throw new Error('Failed!');
});

Сейчас: Рекомендуется использовать async_hooks

Практические рекомендации

1. Логирование и мониторинг

process.on('uncaughtException', (err) => {
  logger.fatal(err, 'Uncaught Exception');
  metrics.increment('server.errors.uncaught');
  process.exit(1);
});

2. Graceful shutdown

process.on('uncaughtException', async (err) => {
  await closeDatabaseConnections();
  await sendAlertToOpsTeam(err);
  process.exit(1);
});

3. Опасные антипаттерны

Неправильно:

process.on('uncaughtException', () => {
  // Продолжение работы после ошибки
});
  • Может привести к неопределенному состоянию
  • Возможны утечки памяти и ресурсов

Особые случаи

1. Child processes

const { fork } = require('child_process');
const child = fork('server.js');

child.on('error', (err) => {
  // Ошибки в дочернем процессе
});

child.on('exit', (code) => {
  // Завершение дочернего процесса
});

2. Async/await ошибки

async function main() {
  throw new Error('Async error');
}

main(); // Попадет в unhandledRejection

3. EventEmitters

const EventEmitter = require('events');
const emitter = new EventEmitter();

emitter.on('error', (err) => {
  // Обработка ошибок конкретного emitter
});

Резюмируем

  1. Глобальные обработчики:

    • uncaughtException для синхронных ошибок
    • unhandledRejection для Promise
  2. Критические правила:

    • Всегда завершайте процесс после uncaughtException
    • Не полагайтесь на обработчики для продолжения работы
  3. Лучшие практики:

    • Комплексное логирование всех необработанных ошибок
    • Реализация graceful shutdown
    • Мониторинг и алертинг
  4. Эволюция API:

    • Домены устарели (используйте async_hooks)
    • Поведение unhandledRejection изменилось в Node.js 15+
  5. Особенности Node.js:

    • Более строгий подход к ошибкам, чем в браузере
    • Важность правильного завершения процессов

Правильная обработка непойманных исключений критична для надежности Node.js приложений в production.