Једном сам возио Ауди са В8 твин-турбо мотором, а његове перформансе су биле невероватне. Возио сам се око 140 км / х аутопутем ИЛ-80 у близини Чикага у 3 сата ујутро када на путу није било никога. Од тада, термин „В8“ ми постаје повезан са високим перформансама.
Ноде.јс је платформа изграђена на Цхромеовом В8 ЈаваСцрипт механизму за једноставну изградњу брзих и скалабилних мрежних апликација.Иако је Аудијев В8 веома моћан, и даље сте ограничени капацитетом резервоара за бензин. Исто важи и за Гоогле-ов В8 - ЈаваСцрипт механизам који стоји иза Ноде.јс. Његова изведба је невероватна и постоји много разлога због којих Ноде.јс добро функционише у многим случајевима коришћења , али увек сте ограничени величином гомиле. Када требате да обрадите више захтева у својој апликацији Ноде.јс, имате два избора: вертикално или хоризонтално. Хоризонтално скалирање значи да морате покретати истовремене инстанце апликација. Када завршите исправно, на крају ћете моћи да услужите више захтева. Вертикално скалирање значи да морате да побољшате употребу меморије и перформансе апликације или да повећате ресурсе доступне за инстанцу апликације.
Недавно су ме замолили да радим на апликацији Ноде.јс за једног од мојих АпееСцапе клијената како бих решио проблем цурења меморије. Апликација, АПИ сервер, требало је да може да обради стотине хиљада захтева сваког минута. Првобитна апликација заузимала је скоро 600 МБ РАМ-а и зато смо одлучили да узмемо вруће АПИ крајње тачке и поново их применимо. Режијски трошкови постају врло скупи када треба да се удовољи многим захтевима.
За нови АПИ изабрали смо рестифи са изворним МонгоДБ управљачким програмом и Куе-ом за позадинске послове. Звучи као врло лагана гомила, зар не? Не сасвим. Током вршног учитавања нова инстанца апликације могла би да потроши до 270 МБ РАМ-а. Стога је мој сан о два примера пријаве по 1Кс Хероку Дино нестао.
Ако тражите „како пронаћи цурење у чвору“, први алат који бисте вероватно пронашли је мемватцх . Оригинални пакет је давно напуштен и више се не одржава. Међутим, његове нове верзије можете лако пронаћи у ГитХуб-у рачваста листа за спремиште . Овај модул је користан јер може емитовати цурење догађаја ако види како гомила расте током 5 узастопних збирки смећа.
Одличан алат који омогућава Ноде.јс програмери да бисте направили снимак гомиле и прегледали их касније помоћу Цхроме Девелопер Тоолс.
Чак и кориснија алтернатива хеапдумп-у, јер вам омогућава да се повежете са активном апликацијом, направите думп хеап-а, па чак и отклоните грешке и поново је компајлирате у лету.
Нажалост, нећете се моћи повезати са продукцијским апликацијама које су покренуте на Херокуу, јер не дозвољава слање сигнала текућим процесима. Међутим, Хероку није једина платформа за хостинг.
Да бисмо искусили ноде-инспецтор у акцији, написаћемо једноставну Ноде.јс апликацију користећи рестифи и у њу ставити мали извор цурења меморије. Сви експерименти овде су направљени са Ноде.јс в0.12.7, који је компајлиран против В8 в3.28.71.19.
var restify = require('restify'); var server = restify.createServer(); var tasks = []; server.pre(function(req, res, next) { tasks.push(function() { return req.headers; }); // Synchronously get user from session, maybe jwt token req.user = { id: 1, username: 'Leaky Master', }; return next(); }); server.get('/', function(req, res, next) { res.send('Hi ' + req.user.username); return next(); }); server.listen(3000, function() { console.log('%s listening at %s', server.name, server.url); });
Овде је апликација врло једноставна и има врло очигледно цурење. Низ задаци би растао током животног века апликације, услед чега би се успорио и на крају срушио. Проблем је у томе што не пропуштамо само затварање већ и читаве објекте захтева.
ГЦ у В8 користи стратегију заустављања света, стога то значи да имате више предмета у меморији што дуже траје сакупљању смећа. На доњем дневнику можете јасно видети да би на почетку века примене смећа требало просечно 20 мс, али неколико стотина хиљада захтева касније траје око 230 мс. Људи који покушавају да приступе нашој апликацији морали би да сачекају 230мс сада дуже због ГЦ. Такође можете видети да се ГЦ позива на сваких неколико секунди, што значи да би на сваких неколико секунди корисници имали проблема са приступом нашој апликацији. А кашњење ће расти док се апликација не сруши.
[28093] 7644 ms: Mark-sweep 10.9 (48.5) -> 10.9 (48.5) MB, 25.0 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 7717 ms: Mark-sweep 10.9 (48.5) -> 10.9 (48.5) MB, 18.0 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 7866 ms: Mark-sweep 11.0 (48.5) -> 10.9 (48.5) MB, 23.2 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 8001 ms: Mark-sweep 11.0 (48.5) -> 10.9 (48.5) MB, 18.4 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. ... [28093] 633891 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 357.3 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 635672 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 331.5 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested]. [28093] 637508 ms: Mark-sweep 235.7 (290.5) -> 235.7 (290.5) MB, 357.2 ms [HeapObjectsMap::UpdateHeapObjectsMap] [GC in old space requested].
Ови редови дневника се штампају када се Ноде.јс апликација покрене са –Траце_гц застава:
node --trace_gc app.js
Претпоставимо да смо нашу апликацију Ноде.јс већ покренули са овом заставицом. Пре него што повежемо апликацију са ноде-инспецтор-ом, морамо да јој пошаљемо сигнал СИГУСР1 текућем процесу. Ако покренете Ноде.јс у кластеру, обавезно се повежите са једним од помоћних процеса.
kill -SIGUSR1 $pid # Replace $pid with the actual process ID
Радећи ово, ми терамо апликацију Ноде.јс (тачније В8) да уђе у режим отклањања грешака. У овом режиму апликација аутоматски отвара порт 5858 помоћу В8 протокол за отклањање грешака .
Наш следећи корак је покретање ноде-инспектора који ће се повезати са интерфејсом за отклањање грешака покренуте апликације и отворити други веб интерфејс на порту 8080.
$ node-inspector Node Inspector v0.12.2 Visit http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858 to start debugging.
У случају да апликација ради у производњи и имате постављен заштитни зид, можемо тунелирати удаљени порт 8080 на лоцалхост:
ssh -L 8080:localhost:8080 [email protected]
Сада бисте могли да отворите Цхроме веб прегледач и добијете пуни приступ Цхроме развојним алатима у прилогу ваше удаљене производне апликације. Нажалост, Цхроме Девелопер Тоолс неће радити у другим прегледачима.
Цурење меморије у В8 није право цурење меморије какву познајемо из Ц / Ц ++ апликација. У ЈаваСцрипт променљиве не нестају у празнини, већ се само „забораве“. Циљ нам је да пронађемо ове заборављене променљиве и подсетимо их да је Добби слободан.
Унутар Цхроме Девелопер Тоолс имамо приступ више профила. Нас посебно занима Снимите распоред гомиле која се током времена покреће и прави вишеструке снимке гомиле. Ово нам даје јасан завир у то који предмети цуре.
Почните да снимате расподелу гомиле и симулирајте 50 истовремених корисника на нашој почетној страници користећи Апацхе Бенцхмарк.
ab -c 50 -n 1000000 -k http://example.com/
Пре снимања нових снимака, В8 би извршио сакупљање смећа са померањем, тако да дефинитивно знамо да на снимку нема старог смећа.
Након прикупљања снимака алокације гомиле током периода од 3 минута на крају имамо нешто попут следећег:
Јасно можемо видети да у гомили има и гигантских низова, пуно објеката ИнцомингМессаге, РеадаблеСтате, СерверРеспонсе и Домаин. Покушајмо да анализирамо извор цурења.
Након одабира разлике гомиле на графикону од 20-их до 40-их, видећемо само објекте који су додати после 20-их од када сте покренули профил. На овај начин бисте могли изузети све уобичајене податке.
Имајући на уму колико је објеката сваке врсте у систему, проширујемо филтер са 20 на 1 минут. Видимо да низови, који су већ прилично гигантски, непрестано расту. Под „(низ)“ можемо видети да постоји пуно објеката „(својства објекта)“ са једнаком удаљеностом. Ти објекти су извор нашег цурења меморије.
Такође можемо видети да и објекти „(затварање)“ брзо расту.
Можда би било згодно погледати и жице. Испод листе жица налази се пуно фраза „Хи Леаки Мастер“. То би нам такође могло дати неки траг.
У нашем случају знамо да се низ „Хи Леаки Мастер“ могао саставити само на путу „ГЕТ /“.
Ако отворите пут задржача, видећете да се на овај низ некако упућује рек , онда је створен контекст и све ово додато неком огромном низу затварања.
У овом тренутку знамо да имамо некакав гигантски низ затварања. Идемо заправо и дајмо име свим нашим затварањима у реалном времену на картици извора.
Након што завршимо са уређивањем кода, можемо притиснути ЦТРЛ + С да бисмо спремали и поново компајлирали код у лету!
Сада снимимо још један Снимак додељивања гомиле и погледајте која затварача заузимају памћење.
Јасно је то СомеКиндОфЦлојуре () је наш негативац. Сад то можемо видети СомеКиндОфЦлојуре () затварања се додају неком низу именованих задаци у глобалном простору.
Лако је видети да је овај низ бескористан. Можемо то коментарисати. Али како да ослободимо меморију већ заузете меморије? Врло једноставно, само додељујемо празан низ задаци а са следећим захтевом биће поништено и меморија ће бити ослобођена након следећег ГЦ догађаја.
Добби је бесплатан!
В8 гомила је подељена на неколико различитих простора:
mmap
ед едон у меморијиCell
с, PropertyCell
с и Map
с. Ово се користи за поједностављивање сакупљања смећа.Сваки простор је састављен од страница. Страница је регион меморије додељен из оперативног система са ммап. Свака страница је увек велика 1 МБ, осим страница у великом објектном простору.
В8 има два уграђена механизма за сакупљање смећа: Сцавенге, Марк-Свееп и Марк-Цомпацт.
Сцавенге је врло брза техника сакупљања смећа и оперише са предметима у Нови простор . Сцавенге је примена Цхенеи’с Алгоритхм . Идеја је врло једноставна, Нови простор је подељен у два једнака полупростора: То-Спаце и Фром-Спаце. Сцавенге ГЦ се јавља када је То-Спаце пун. Једноставно замењује размаке из и из простора и копира све живе објекте у свемир или их промовише у један од старих простора ако су преживели две чистаче, а затим се у потпуности брише из свемира. Отпади су врло брзи, али имају за последицу задржавање двоструке величине и стално копирање објеката у меморији. Разлог за употребу чистача је тај што већина предмета умире млада.
Марк-Свееп & Марк-Цомпацт је друга врста сакупљача смећа која се користи у В8. Друго име је пуни сакупљач смећа. Означава све живе чворове, затим брише све мртве чворове и дефрагментира меморију.
Иако за веб апликације високе перформансе можда не представљају тако велики проблем, и даље ћете желети да избегнете цурење по сваку цену. Током фазе означавања у пуном ГЦ, апликација се заправо зауставља док се сакупљање смећа не заврши. То значи да што више објеката имате у гомили, то ће дуже требати да се изврши ГЦ и дуже ће корисници морати да чекају.
Много је лакше прегледати трагове и гомиле стека када сва ваша затварања и функције имају имена.
db.query('GIVE THEM ALL', function GiveThemAllAName(error, data) { ... })
У идеалном случају желите да избегнете велике објекте унутар врућих функција тако да сви подаци буду уклопљени Нови простор . Све операције везане за процесор и меморију треба да се извршавају у позадини. Такође избегавајте окидаче за оптимизацију за вруће функције, оптимизована врућа функција користи мање меморије од неоптимизираних.
Вруће функције које раде брже, али такође троше мање меморије, узрокују да ГЦ ради ређе. В8 нуди неке корисне алате за отклањање грешака за уочавање неоптимизираних функција или деоптимизираних функција.
Уграђени кешеви (ИЦ) користе се за убрзавање извршавања неких делова кода било кеширањем приступа својству објекта obj.key
или неке једноставне функције.
function x(a, b) { return a + b; } x(1, 2); // monomorphic x(1, “string”); // polymorphic, level 2 x(3.14, 1); // polymorphic, level 3
Када к (а, б) покреће се први пут, В8 ствара мономорфни ИЦ. Када позовете x
други пут, В8 брише стари ИЦ и ствара нови полиморфни ИЦ који подржава обе врсте операнда цео број и низ. Када трећи пут позовете ИЦ, В8 понавља исти поступак и креира још један полиморфни ИЦ нивоа 3.
Међутим, постоји ограничење. Након што ниво ИЦ достигне 5 (може се променити помоћу –Мак_инлининг_левелс застава) функција постаје мегаморфна и више се не сматра оптимизираном.
Интуитивно је разумљиво да се мономорфне функције покрећу најбрже и имају мањи меморијски отисак.
Овај је очигледан и добро познат. Ако имате велике датотеке за обраду, на пример велику ЦСВ датотеку, прочитајте је ред по ред и обрадите на мале делове, уместо да читаву датотеку учитате у меморију. Постоје прилично ретки случајеви када би појединачна линија цсв била већа од 1мб, што вам омогућава да је уклопите Нови простор .
Ако имате неки врући АПИ којем је потребно неко време за обраду, попут АПИ-ја за промену величине слика, преместите га у засебну нит или претворите у позадински посао. ЦПУ интензивне операције би блокирале главну нит приморавајући све остале купце да чекају и наставе слати захтеве. Необрађени подаци захтева слагали би се у меморију, што би приморало пуни ГЦ да потраје дуже време да заврши.
Једном сам имао чудно искуство са рестифи-ом. Ако пошаљете неколико стотина хиљада захтева на неважећи УРЛ, тада ће меморија апликације брзо нарасти до стотину мегабајта док пуни ГЦ не крене за неколико секунди касније, када ће се све вратити у нормалу. Испоставило се да за сваку неважећу УРЛ адресу рестифи генерише нови објекат грешке који укључује дуге трагове стека. Ово је натерало новоразграђене објекте да се доделе у Простор великих објеката него у Нови простор .
Приступ таквим подацима може бити од велике помоћи током развоја, али очигледно није потребан у производњи. Стога је правило једноставно - не генеришите податке ако вам то сигурно нису потребни.
На крају, али свакако не најмање важно, треба знати своје алате. Постоје разни програм за отклањање грешака, цурење катера и генератори графикона употребе. Сви ти алати могу вам помоћи да свој софтвер учините бржим и ефикаснијим.
Разумевање начина на који В8 ради прикупљање смећа и оптимизатор кода је кључ за перформансе апликације. В8 компајлира ЈаваСцрипт у нативни склоп и у неким случајевима добро написан код може постићи перформансе упоредиве са ГЦЦ компајлираним апликацијама.
И у случају да се питате, нова АПИ апликација за мој АпееСцапе клијент, иако има простора за побољшање, функционише врло добро!
Јоиент је недавно објавио нову верзију Ноде.јс која користи једну од најновијих верзија В8. Неке апликације написане за Ноде.јс в0.12.к можда нису компатибилне са новом верзијом в4.к. Међутим, апликације ће доживети огромне перформансе и побољшање употребе меморије у новој верзији Ноде.јс.