TGViewer
Настоящий JavaScript Настоящий JavaScript @true_js · 5.89K subscribers
Post #3589 370
⁣Node.js: WeakRef, FinalizationRegistry и GC-триггеры для long-running процессов

Long-running процессы в Node.js — зона риска утечек памяти. ES2021 даёт два API для активного контроля над GC: WeakRef и FinalizationRegistry. Многие разработчики полагаются на автоматическое управление памятью, не учитывая, что GC не может освободить ресурсы, удерживаемые сильными ссылками, а сами API недетерминированы.

WeakRef: слабые ссылки
WeakRef не удерживает объект в памяти. GC может удалить его, если нет сильных ссылок. Это полезно для кэшей, где не требуется гарантированное хранение данных.

const cache = new Map();
function getOrCreate(key) {
const ref = cache.get(key);
if (ref) {
const obj = ref.deref();
if (obj) return obj;
cache.delete(key);
}
const obj = new ExpensiveObject();
cache.set(key, new WeakRef(obj));
return obj;
}


deref() может вернуть undefined в любой момент — GC асинхронен. Если подряд дёргаешь deref(), не жди, что вернёт то же самое. Типичная ошибка — полагаться на deref() как на гарантированный доступ.

FinalizationRegistry: колбэк после GC
Вызывается после сборки объекта. Идеален для очистки нативных ресурсов, например, закрытия файловых дескрипторов.

const registry = new FinalizationRegistry((handle) => {
cleanupNativeHandle(handle);
});
function createResource() {
const handle = openNativeConnection();
registry.register({ handle }, handle);
return { handle };
}


Предостережения: колбэк не гарантирует порядок и может не выполниться при падении процесса. В продакшене я бы не вешал на него критичную логику вроде записи в БД. Только освобождение нативных хендлов или логгирование.

Паттерн: MemoryGuard
Триггер на основе FinalizationRegistry для мониторинга памяти. Помогает выявить растущие объекты, но не заменяет ручное управление.

class MemoryGuard {
constructor(thresholdMB) {
this.threshold = thresholdMB;
this.registry = new FinalizationRegistry(() => this.onGC());
}
track(obj) { this.registry.register(obj, () => {}); }
onGC() {
const usage = process.memoryUsage().heapUsed / 1024 / 1024;
if (usage > this.threshold) console.warn(Memory spike: ${usage.toFixed(2)} MB);
}
}


По опыту, этот паттерн хорош скорее для локальной отладки, чем для прода. В production лучше комбинировать с метриками в Prometheus и лимитами heap (--max-old-space-size=4096).

Production-паттерны
* LRU-кэш с WeakRef — автоочистка при нехватке памяти, но без гарантий времени.
* async_hooks для мониторинга асинхронных ресурсов: ловит утечки через незакрытые сокеты или таймеры.
* Регулярные snapshot console.profile() для поиска растущих объектов.
* Слушать process.on('warning') для уведомлений о превышении лимитов.

Я часто вижу, как разработчики забывают про async_hooks: он ловит утечки через незакрытые сокеты или таймеры. Это дешевле, чем дебажить heap snapshot.

Вывод: WeakRef и FinalizationRegistry — полезные инструменты для оптимизации памяти, но их недетерминированность требует ручного управления ссылками (WeakMap, cancellable promises) для production-надёжности.
  • ❤ 1
More from @true_js
  1. Oct 3, 2026Post #3963
  2. Oct 2, 2026😅 Айтишник отправил в одну компанию три одинаковых резюме и только одно дошло до финала О…
  3. Oct 2, 2026🤣 Правильно расставленные приоритеты в моей жизни би лайк: 💥 xCode Journal
  4. Oct 1, 2026🤯 Люди взбунтовались против «пыточной для ИИ» На GitHub заметили открытый проект AI Tortu…
  5. Oct 1, 2026День в Заонежье начинается ещё в дороге. Мы встретим вас в аэропорту или на вокзале. Дальш…
  6. Sep 30, 2026Книга из переписки без сервера: canvas, jsPDF и брошюра для печати прямо в браузере Пет-пр…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →