Галоўная / Артыкулы / Буфер каштовых картадзеў Node ёсць тыхі вылів памяці ў режыме разработкі.

Буфер каштовых картадзеў Node ёсць тыхі вылів памяці ў режыме разработкі.

Дазвольце даклэ нарадзіцца, чаму актываўка параметраў --enable-source-maps або NODE_V8_COVERAGE можа спрычыніць неактуальны рост кашы з-за павтаральных вызоў eval, і як сёньня діагноставаць та зменшыць гэты проблема.

1073 слоў

Процес Node пасуючы пасля запуску выглядае абсалютна здаровым, але паступова заполняе памяц, калі вы продавальваеце код. Якщо гэты процес быў запускаць з параметрам --enable-source-maps (альбо з установленым параметрам NODE_V8_COVERAGE), і ў вашай стак-структуре постаўляецца вызыв eval з новым значэнням //# sourceURL ў кожны раз, тады вы, верагодна, знайшлі прычыну: сильны кэш Source Map, які накаплівае створаныя элементы і ніколи іх не вывалюе. Об’ём памяці продавальваецца. Вы перазапускаеце процес — і ён зноў падрастае.

Вы прыменяеце прыемы збірання сметлівых дадзенняў. Вы ачышчаеце всі можлівыя звязкі. Але об’ём памяці продавальваецца ўсё равно.

Фармат росту

Уявіце сервер для розробкі, який працюе працюе ўсё больш час, або будь-які іншы процес Node з довгай трываласцю, які генеруе штучныя трагі з інформацыяй працы або „фрэмы-власнікі“, пазначаныя унікальнымі URL-адрэсамі. Значэння RSS і heapUsed постаўна зростаюць. Перзапуск процесу скасоўвае гэты трэнд, але выкананне таго ж завадзяўкі без пазначкі source-map заставляе колькасць памяці не змінювацца.

У публічным рэўюжеры проблем Node.js (nodejs/node#65760) для аналізу гэтага паведання прыклад выкарыстоўвае приблізна 90 байт коду, які перакладаецца за дапамогою аднаго зовнішняга source-map, пры чым у кожной ітерацыі зменяецца толькі sourceURL. Пасля выкарання прымусовай сортаванні мусора:

Evals | with --enable-source-maps | no flag
0     | 5 MB                       | 5 MB
400   | 170 MB                     | 5 MB
800   | 334 MB                     | 6 MB
1200  | 499 MB                     | 6 MB

Гэта дае прыблізна 415 КБ памяці, якая застаецца пасля кожнага вызову eval, пры чым не відзначаецца жадны верхнія межа.

Якщо вы працюеце з Next.js App Router, гэта частая проблема: кожны раз, калі вы правяце які-небудзь файл, next dev запрашвае дзесяткі мегабайтаў памяці. Механізм owner-stack у React Server Components запускае eval аднажды на кожны фрэйм стака, пазначаючы кожны вызов чымось у дзеўянстве //# sourceURL=about://React/…?<counter++> разам з вялікай вбудованай картою выкарыстоўвання коду. Паколькі гэты лячык збіраецца на кожны вызов, кожны ключ кэша є унікальным, таму нічога не вяртаецца да практычнага выкарыстоўвання. Адпаведны тэматычны разгортак у Next.js — vercel/next.js#98221; спачатку трэба вважаць гэта месца, дзе выклікаецца гэты симптам, а не незалежной прычынай.

Чаму кэш не прабачае

Флаг --enable-source-maps паведамляе Node аб кэшаванні Source Maps, ў результате чаго трэс-трэйсы падчас выканання можна пераклацать назад у вашы первоначальныя файлы-выкананні (дакледзе документацыю CLI). Канстантныя кантыненты модуляў праходзяць через кэш з слабымі ключамі, таму колектар сметлівасці можа ўзяць іх пад контроль, калі да яных больш няма апыленаў. Аднак генераваныя кантыненты — тые, якімі керуе галоўка isGeneratedSource — атрымліваюць месца ў generatedSourceMapCache, яным являецца звычайны, са сильнымі апыленнямі Map, заданы ў lib/internal/source_map/source_map_cache.js і які ніколі не адчысляецца.

Каментары даўжынай коду, якія знаходзяцца ў аблаке кэша, прыпускаюць, што за весь час роботы процэса будзе толькі калькuluема некалькі ісходных дасягненняў. Функцыі Hot Module Replacement і регенерацыя власнага стаку цэлкам разбиваюць гэты прыпуск. Кожны адзінаковы sourceURL стае постоянным ключам, старыя елементы ніколі не вычысляюцца, а цэлы распакаваны карточны спіс для кожнага з іх застаецца ў памяці.

Змена значэння NODE_V8_COVERAGE ведае працю чераз той самы шлях кэша, таму вы будете бачыць той самы необмежаны рост, калі ключі, якія ствараюцца пасля eval, продовжуюць зміняцца.

Уж існуе ачыты рэквэст прытягання з верхней лініі (#65761), які абмежвае кэш generated-sources за дапамою стратэгіі LRU з выкарыстоўваннем байтовага бюджэту — 32 МБ у няўяшчэнай версіі — і апавершвае элементы пад час чытання, каб карты для яшчэ актыўных генераваных функцый не былі выключаны працягом часу. На дзесяньдзе той рэквэст заставаецца ачытым і мае пазначку needs-ci. Ён не быў з’едынены і не ўходзіць у склад жадной выпусканай версіі Node, таму не трэба прыпускать, што ваша ныяшчэя LTS версія вже мае гэты фікс.

Падтверджэнне дыягназу

  1. Пераканайцеся, што ваш процес, які працуе дзяўно, быў запускаўся з параметрам --enable-source-maps або ў яму задана значэнне NODE_V8_COVERAGE, і што якаясь частка пастойчыва выкананае код з новым параметрам //# sourceURL кожны раз — гэта можа быть HMR, стекі власніка RSC або спецыяльны кодгенератор.
  • Прынцыпавайце process.memoryUsage().heapUsed пасля запуску ціклу прымусовага адзбору мэмарыя, будзь то ў самастоятнай репродукцыі, якая запускаецца з node --expose-gc, або праз фіксацыю стану хепа через інспектар у вашам жывом процэсе.
  • Запускайце той самы завод з выключаным флагам. Адзначальны сігнал з #65760 — гэта тое, што мэмарыя застаюцца стабільнымі без флага, а з яго ўвёнам іх колькасць постаўна зрастае.
  • Па выбору можна зафіксаваць стан хепа і абшукаць у ям generatedSourceMapCache або context:generatedSourceMapCache. Автары звестак пра гэтыя проблемы знайшлі тыячы захаваных элементаў, якія зберагалі падсумкова болей што гігабайт дакументаў sourcesContent і mappings пад час рэальнай сесіі next dev.
  • Збяльшэнне значэння --max-old-space-size не ўсуне проблему — яно толькі адкладае момент збою з-за нехваткі мэмарыя.

    Што робіць зараз

    Выберыце падход, які пасуе вашай сэтапаванню:

    1. Для проектаў Next.js, дзеяце негайна: запускіце next dev --disable-source-maps. Автары з паведамленняў у #65760 зафіксавалі, што спадзянанне росту значна зменшылася — пра +6 MB за кожную зміну, у працоўнасупраце з акываванымі карточкамі выхадных данных — пра +89 MB, за ўсё тое жа час. Вы трапляецеся пазбавіцца часткі чытаемасці трэс-трэйсаў, але ваш комп’ютер больш не будзе трапляцца без памяці.
    2. Для іншых інструментаў Node, якія працуюць дзеўна: адключыце --enable-source-maps або не задаўце значэнне NODE_V8_COVERAGE на будзь-ям сервере гарячага перазавантажэння, пакуль карточкі выхадных данных сапраўды не стануць неабходнымі. У такім случае выкорыстоўвайце карточкі выхадных данных толькі пад час короткакалендарных сесыяў дэбагавання.
  • Стежыце за парадкамі выправлення: стараннаўце следзіце за праектам nodejs/node#65760 і запрошэнням да падключэння коду #65761. Калі ў новай версіі Node будзе прыметна прамова пра обмежаны кэш генераваных выкарыстоўваных кодаў, можна без абавясці перайсці на новую версію. Даколы гэтага не адбудзецца, любыя цифры, прадастыя іншымі корыстнікамі, трэба спрыяваць як показнікі для ўсунення проблем у ўсабліжным сераўеры, а не як гаранцыю таго, што ваша прыкладна програма будзе работаць аднакова.
  • Не следзіце за саветам «проста падняць ліміт памяці». Адпаведны кэш є моцным і необмежаным пад гэтым самым сукупніцам флагаў і спосабам выкарыстоўвання. Большы ліміт памяці дае толькі некалькі дадатковых секунд прычыну краху.
  • Вывад

    Яны ваш сервер развітку не спрацоўвае без прычыны і не з’ядае памяць. Аднаўленне параметра --enable-source-maps разам з паўтаральным выкананням коду, які викорыстоўвае унікальныя значэння sourceURL, прыводзіць да заполнення кэша generatedSourceMapCache, які ніколі не вывалюе своіх елементаў. Кэшы звычайных модуляў можна вываліць, але кэшы, створаныя автаматычна, — ні, прынеймні пакуль не будзе выдана версія з номерам #65761. Закрыйце функцыю створэння кэша з мапамі джерела пад процесами гарячага перзавантажэння ўжо сьогодні, і планаваце апдэйт, калі будзе выпусканы кэш з обмежаннем.

    Таму, калі наступны раз вы пазначыце, што значэння RSS падыходзіць да вялікіх значэнняў, калі вы проста рэдагуеце файлы, а прыводзімае да вывалення кэша не дапамагае цьому запобiec, пераканайцеся, чы не ёсьць прычыной самэй гэтай настройкі.

    Спаднёе чытанне