JavaScript / Memory / Performance

    JS Memory Management

    Garbage Collection, WeakMap/WeakSet (słabe referencje), WeakRef, FinalizationRegistry, memory leaks w React i V8 optymalizacje.

    Mark+Sweep
    GC
    WeakMap
    Słabe ref
    WeakRef
    Nullable
    FinalizReg.
    Cleanup

    6 konceptów JS Memory Management

    GC, WeakMap, WeakSet, WeakRef, FinalizationRegistry i Hidden Classes — opis i zastosowanie.

    Koncept Opis Zastosowanie
    Mark and Sweep GC Oznacz żywe obiekty od roots, zbierz resztę Automatyczne — V8, SpiderMonkey, JSCore
    WeakMap Map z słabymi kluczami — GC może zebrać Cache bez wycieków, private fields, DOM data
    WeakSet Set z słabymi referencjami — brak iteracji Tracking przetworzonych obiektów, circular refs
    WeakRef Słaba referencja — deref() lub undefined Cache który może być GC, obserwatory
    FinalizationRegistry Callback gdy obiekt zbierany przez GC Cleanup native resources, auto-evicting cache
    Hidden Classes (V8) Obiekty z tym samym shape = szybkie Inicjalizuj wszystkie pola w constructor

    Często zadawane pytania

    JavaScript Garbage Collection — jak działa Mark and Sweep?

    Garbage Collection: automatyczne zarządzanie pamięcią w JS. Deweloper nie zwalnia ręcznie. GC engine: V8 (Chrome/Node.js), SpiderMonkey (Firefox), JavaScriptCore (Safari). Mark and Sweep: 1. Zacznij od roots (globalny obiekt, stack). 2. Odwiedź wszystkie osiągalne obiekty. 3. Oznacz jako 'żywe'. 4. Zmieć nieoznaczone (garbage). Generational GC: Young generation (Scavenger/Minor GC): małe, szybkie kolekcje. Obiekty które przeżyły kilka GC -> Old generation. Old generation (Major GC): rzadsze, droższe. Mark-Sweep-Compact. Incremental: podział na małe kroki. Brak pauz > 5ms. Concurrent: GC w osobnym wątku. Równolegle z JS. V8 Orinoco. Write barriers: śledzenie zmian wskaźników. Ważne dla concurrent GC. Hidden classes: V8 optymalizuje obiekty. Stwórz raz shape. Nie dodawaj pól po init. Szybszy dostęp do properties. Inline caches: zapamiętaj typ obiektu. Szybsze wywołania. Polymorphic — wolniejsze. GC triggery: alokacja przekroczyła próg. Czasowo (minor GC). Explicit: nie ma w JavaScript! global.gc() w Node.js z --expose-gc. Monitoring: Node.js --inspect. process.memoryUsage() — heapUsed, heapTotal, rss. V8 heap profiler w Chrome DevTools. console.memory (Chrome). GC events: PerformanceObserver. entryTypes: ['measure'].

    WeakMap i WeakSet — słabe referencje bez memory leaks?

    WeakMap: Map ale słabe klucze. Klucz musi być obiektem. GC może zebrać klucz jeśli brak innych referencji. WeakMap jest zbierany razem z kluczem. Brak iteracji — nie można listować. const wm = new WeakMap(). wm.set(domNode, {clicks: 0}). wm.get(domNode) — pobierz. wm.has(domNode) — sprawdź. Gdy domNode usunięty z DOM i brak referencji -> GC zbiera domNode -> wm entry znika automatycznie. Zastosowania WeakMap: Cache per obiekt: const cache = new WeakMap(). function getExpensiveResult(obj) { if (cache.has(obj)) return cache.get(obj). const result = expensive(obj). cache.set(obj, result). return result }. Private fields (pre-#): const privateData = new WeakMap(). class MyClass { constructor() { privateData.set(this, {secret: 42}) } getSecret() { return privateData.get(this).secret } }. Event listener tracking: bez wycieku gdy element usunięty. WeakSet: zbiór słabych referencji. Brak iteracji. const ws = new WeakSet(). ws.add(object). ws.has(object). Zastosowania WeakSet: Śledzenie przetworzonych obiektów: const processed = new WeakSet(). function process(item) { if (processed.has(item)) return. processed.add(item). doWork(item) }. Circular reference detection. Brak wyczerpania pamięci. vs Map/Set: Map/Set — silne referencje, enumerable. WeakMap/Set — słabe, brak enumeracji. Używaj WeakMap gdy chcesz powiązać dane z obiektem bez wycieków.

    WeakRef i FinalizationRegistry — zaawansowane zarządzanie pamięcią?

    WeakRef: słaba referencja do obiektu. GC może zebrać obiekt. deref() zwraca obiekt lub undefined. const wr = new WeakRef(heavyObject). const obj = wr.deref(). if (obj) { useObj(obj) } else { // GC collected }. Nie gwarantuje żywości. Sprawdzaj zawsze. Zastosowania WeakRef: Cache z możliwością GC: class Cache { cache = new Map(). get(key) { const ref = this.cache.get(key). if (ref) { const val = ref.deref(). if (val !== undefined) return val } }. set(key, value) { this.cache.set(key, new WeakRef(value)) } }. Obserwator bez wycieków: const weakCallback = new WeakRef(callback). emitter.on('event', () => { const cb = weakCallback.deref(). if (cb) cb() }). Nie trzyma callback przy życiu. FinalizationRegistry: callback gdy obiekt zbierany. Nie gwarantuje wywołania. Nie natychmiastowy. const registry = new FinalizationRegistry((heldValue) => { console.log('Cleaned up', heldValue). cleanupResource(heldValue) }). const obj = createExpensiveObject(). registry.register(obj, 'cleanup-key', obj). Trzeci argument (unregisterToken): registry.unregister(obj) — anuluj rejestrację. Zastosowania: cleanup native resources. Close file handles. Cancel subscriptions. Kiedy NIE używać: normalne cleanup — useEffect return. Deterministyczny cleanup — finally. WeakRef + FinalizationRegistry razem: const cache = new Map(). const cleanup = new FinalizationRegistry(key => cache.delete(key)). function addToCache(key, value) { cache.set(key, new WeakRef(value)). cleanup.register(value, key) }. Auto-evicting cache.

    Memory leaks w JavaScript i React — jak znajdować i naprawiać?

    Common memory leaks: 1. Event listeners bez cleanup: element.addEventListener('click', handler). Brak removeEventListener. Handler trzyma closure z dużym obiektem. Napraw: useEffect return () => element.removeEventListener. 2. Timers: setInterval bez clearInterval. Callback trzyma referencję. Napraw: clearInterval w cleanup. 3. Closures: function outer() { const bigArray = [1, 2, ...]. return function inner() { return bigArray.length } }. bigArray trzymana przez inner. Napraw: nie trzymaj niepotrzebnych referencji. 4. DOM references: const el = document.querySelector('#app'). el.remove(). Ale referencja el nadal istnieje. Napraw: el = null po usunięciu. 5. React: nieoczyszczone subscriptions. setState po unmount (przestarzałe ostrzeżenie). Zamknięty component trzymany przez timer. 6. Global variables: window.myData = hugeObject. Nigdy nie zerowane. Napraw: null gdy skończone. Lub WeakRef. React useEffect cleanup: useEffect(() => { const subscription = api.subscribe(handler). return () => subscription.unsubscribe(). }, []). Zawsze zwróć cleanup! Zewnętrzne biblioteki: chart = new Chart(canvas, options). return () => chart.destroy(). AbortController: const controller = new AbortController(). fetch(url, {signal: controller.signal}). return () => controller.abort(). Narzędzia do wykrywania: Chrome DevTools Memory tab. Heap snapshot. Allocation timeline. Profiler. node --inspect. clinic.js (Node.js). memwatch-next. process.memoryUsage() monitoring. Wzrost heapUsed = potential leak.

    JavaScript performance — V8 optymalizacje i jak pisać szybki kod?

    V8 optymalizacje: JIT compilation — hot functions kompilowane do native code. Turbofan — optymalizujący kompilator. Maglev — nowy kompilator (2023). Sparkplug — szybki baseline. Hidden classes (shapes): obiekty z tym samym kształtem dzielą shapes. Szybki dostęp do pól. Nie dodawaj pól po konstruktorze. Inicjalizuj wszystkie pola w constructor. Monomorphic functions: jedna shape = szybko. Dwa shapes = polimorficzny (wolniejszy). Trzy+ shapes = megamorphic (najwolniejszy). Inline caching: zapamiętaj wynik type check. Szybsze kolejne wywołania. Array performance: typowany array = najszybszy. Array z jednym typem (SMI = small integer). Nie mieszaj typów w jednej tablicy. Int32Array, Float64Array dla numeryki. Object pool: reużywaj obiekty. Mniej GC pressure. Gry, animacje. const pool = []. function getObject() { return pool.pop() || {} }. function release(obj) { pool.push(obj) }. String interning: niezmienne strings. Silnik cache'uje. Template literals: wolniejsze od concatenation w hot path. Immutability: Object.freeze — kopiowanie zamiast mutacji. Spread operator tworzy nowy obiekt. GC pressure. Moduły: import * as ns — tree-shaking trudne. Named imports. Side effects w modules. JSON.stringify/parse: wolne dla dużych obiektów. msgpack lub protobuf dla performance. Profiling: Chrome Performance tab. Node.js --cpu-prof. clinic.js flame. 0x (flamegraph).

    Czytaj dalej

    Powiązane artykuły

    Kontakt

    Skontaktuj się z nami

    Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.

    Wyślij zapytanie

    Bezpłatna wycena w 24h
    Bez zobowiązań
    Indywidualne podejście
    Ekspresowa realizacja

    Telefon

    +48 790 814 814

    Pon-Pt: 9:00 - 18:00

    Email

    adam@fotz.pl

    Odpowiadamy w ciągu 24h

    Adres

    Plac Wolności 16

    61-739 Poznań

    Godziny pracy

    Pon - Pt9:00 - 18:00
    Sob - NdzZamknięte

    Wolisz porozmawiać?

    Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.

    Zadzwoń teraz