Автор этой статьи:ThornWu (клейкие рисовые шарики), фронтенд-инженер HIGO.
Автору есть что сказать
Серия статей «Как работает V8» представляет собой краткое изложение моего процесса изучения V8. С тех пор как я официально стал фронтенд-инженером год назад, я сознательно понял и изучил V8. Я также обнаружил, что в техническом сообществе мало свежих и оригинальных китайских материалов, поэтому я начал делиться кратким изложением своего процесса обучения.
Из-за загруженности работой я не обновлял свой блог уже полгода. Введение в эту серию — статья, написанная в апреле«Как работает V8 — представление объектов в V8», мы представили объектное представление в V8, проверив его с помощью Chrome DevTools.
Эта статья является первой в полном смысле этого слова. Позиционирование статьи является планом этой серии, которая будет следовать потоку выполнения JavaScript в V8, последовательно знакомить с работой каждого шага и разъяснять «ошибку», которая широко распространена в сообществе. В этой статье мы не будем слишком углубляться в детали (последующие главы будут расширяться), вы можете оставить ту часть, которую хотите узнать о двигателе V8, в комментариях, возможно, следующая тема будет принята и представлена первой.
Приятного чтения.
1. Почему V8
Any application that can be written in JavaScript, will eventually be written in JavaScript.
Я полагаю, что многие из моих друзей слышали об известном законе во внешнем мире, который называетсяAtwood’s Law
. В 2007 году Джефф Этвуд заявил, что «все приложения, которые можно написать на JavaScript, в конечном итоге будут написаны на JavaScript». Перенесемся на 12 лет вперед, и теперь мы действительно видим, что JavaScript играет роль в браузерах, серверах, настольных компьютерах, мобильных устройствах и IoT.
С другой стороны, на данный момент (2019-11-08) рыночная доля Chrome на всех платформах достигла64.92%(Источник данных: StatCounter). Как движок Chrome для JavaScript, V8 также играет ключевую роль в расширении доли Chrome на рынке.
Как один из самых мощных движков JavaScript, V8 также везде. Со стороны браузера он поддерживает Chrome и многие браузеры, работающие на ядре Chromium. На стороне сервера это среда выполнения для Node.js и фреймворка Deno. В области настольных компьютеров и IoT также есть место для V8.
2. Знание V8
V8 — это высокопроизводительная версия, написанная на C++.JavaScript
иWebAssembly
Движок поддерживает восемь процессорных архитектур, включая знакомые ia32, x64 и arm.
Цикл выпуска V8
- Примерно каждые шесть недель выходит новая версия V8.
- Версия V8 соответствует версии Chrome, например V8 v7.8 соответствует Chrome 78.
конкурент V8
- Chakra (бывший движок Edge JavaScript)
- Ядро JavaScript (Сафари)
- ПаукОбезьяна (Firefox)
Важные части V8
- Ignition (базовый компилятор)
- TurboFan (оптимизирующий компилятор)
- Ориноко (сборщик мусора)
- Liftoff (базовый компилятор WebAssembly)
Liftoff — это базовый компилятор для WebAssembly, доступный начиная с V8 6.8. Хотя версия 6.8 была запущена в августе 2018 года, в настоящее время в сообществе есть много статей, представляющих V8, опубликованных после этого времени, в которых не упоминается Liftoff. Включение Liftoff в статью также может быть использовано как признак устаревшего содержания.
Поскольку WebAssembly выходит за рамки этой статьи, введение в Liftoff ниже будет опущено.
3. Конвейер выполнения JavaScript в V8
Ранний конвейер выполнения V8 состоял из базового компилятора Full-Codegen и оптимизирующего компилятора CrankShaft. (Конвейер выполнения V8 много раз корректировался. В этой статье выбран только более важный этап в конвейере раннего выполнения. Учащиеся, интересующиеся эволюцией конвейера выполнения, могут пройтиРечи, связанные с V8понять).
Среди них базовый компилятор больше внимания уделяет скорости компиляции, тогда как оптимизирующий компилятор больше внимания уделяет скорости выполнения скомпилированного кода. Совместное использование базового компилятора и оптимизирующего компилятора позволяет коду JavaScript иметь более высокую скорость холодного запуска и более высокую скорость выполнения после оптимизации.
С этой архитектурой много проблем, например, Crankshaft может оптимизировать только подмножество JavaScript; отсутствие изоляции между слоями в конвейере компиляции, даже необходимость в некоторых случаях писать ассемблерный код для нескольких процессорных архитектур одновременно и т. д.
Чтобы решить проблемы архитектурной путаницы и трудности с расширением, после многих лет эволюции V8 в настоящее время формирует конвейер выполнения JavaScript, состоящий из синтаксического анализатора, базового компилятора Ignition и оптимизирующего компилятора TurboFan.
Синтаксический анализатор преобразует исходный код JavaScript в AST, а базовый компилятор компилирует AST в байт-код. Когда код соответствует определенным условиям, он будет перекомпилирован оптимизирующим компилятором для создания оптимизированного байт-кода.
Здесь мы должны упомянуть идею многослойности. В процессе улучшения конвейера за счет введения IR (промежуточное представление, промежуточное представление) масштабируемость системы эффективно улучшается, а степень связанности связанных модулей и сложность системы снижаются.
Например, есть три функции A, B и C, которые необходимо перенести на двухпроцессорные платформы. До введения IR требовалось 3 * 2 = 6 реализаций кода, а после введения IR требовалось 3 + 2 = 5 реализаций кода. Видно, что одно является отношением умножения, а другое — аддитивным отношением. Преимущества внедрения IR значительно увеличиваются, когда необходимо реализовать множество функций и адаптировать их к многопроцессорным архитектурам.
Ниже мы объединим фрагмент кода, чтобы проанализировать, как обрабатывается JavaScript в V8.
// example1.js
function addTwo(a, b) {
return a + b
}
4. Парсер и АСТ
Синтаксический анализ кода требует времени, поэтому движки JavaScript стараются полностью избегать разбора файлов с исходным кодом. С другой стороны, во время посещения пользователем на странице будет много кода, который не будет выполняться, например, действия, вызванные взаимодействием с пользователем.
Из-за этого все основные браузеры реализуют отложенный синтаксический анализ. Вместо создания AST (абстрактного синтаксического дерева) для каждой функции синтаксический анализатор может решить «предварительно проанализировать» или «полностью проанализировать» функции, с которыми он сталкивается.
При подготовке проверяется синтаксис исходного кода и выдаются синтаксические ошибки, но не разрешается область переменных в функциях и не создается AST. Полный синтаксический анализ проанализирует тело функции и создаст структуру данных AST, соответствующую исходному коду. Предварительный анализ в 2 раза быстрее, чем обычный анализ.
Генерация AST в основном проходит два этапа: сегментация слов и семантический анализ. АСТ Он предназначен для описания конкретной синтаксической композиции исходного кода с помощью структурированной древовидной структуры данных и часто используется для проверки синтаксиса (статический анализ кода), обфускации кода, оптимизации кода и т. д.
мы можем использоватьAST ExplorerИнструмент генерирует AST кода JavaScript.
// example1.js
function addTwo(a, b) {
return a + b
}
Следует отметить, что приведенный выше рисунок описывает только общую структуру AST. V8 имеет собственный наборпредставительство АСТ, результирующая структура AST отличается.
5. Базовое зажигание компилятора и байт-код
V8 представляет технологию JIT (Just In Time, своевременная компиляция) для быстрого создания байт-кода для выполнения с помощью базового компилятора Ignition.
Байт-код — это абстракция машинного кода. Байт-код легче компилировать в машинный код, если структура байт-кода такая же, как и вычислительная модель физического процессора. Вот почему интерпретаторы обычно представляют собой регистровые или стековые машины. Зажигание - это регистр с аккумулятором. («Понимание байт-кода V8»)
По сравнению с предыдущим базовым компилятором Full-Codegen, Ignition генерирует байт-код меньшего размера (Full-Codegen генерирует машинный код). Байт-код может напрямую использоваться оптимизирующим компилятором TurboFan для создания графиков (TurboFan оптимизирует код на основе графиков), что позволяет избежать повторного анализа исходного кода JavaScript оптимизирующим компилятором при оптимизации кода.
использоватьd8
Инструменты (оболочка разработчика V8, которую можно получить путем компиляции исходного кода V8, см. процесс компиляции«Создание V8 с GN»), чтобы просмотреть байт-код, сгенерированный компиляцией Ignition.
d8 --print-bytecode example1.js
[generated bytecode for function: (0x2d5c6af1efe9 <SharedFunctionInfo>)]
Parameter count 1
Register count 3
Frame size 24
0x2d5c6af1f0fe @ 0 : 12 00 LdaConstant [0]
0x2d5c6af1f100 @ 2 : 26 fb Star r0
0x2d5c6af1f102 @ 4 : 0b LdaZero
0x2d5c6af1f103 @ 5 : 26 fa Star r1
0x2d5c6af1f105 @ 7 : 27 fe f9 Mov <closure>, r2
0x2d5c6af1f108 @ 10 : 61 2c 01 fb 03 CallRuntime [DeclareGlobals], r0-r2
0x2d5c6af1f10d @ 15 : a7 StackCheck
0x2d5c6af1f10e @ 16 : 0d LdaUndefined
0x2d5c6af1f10f @ 17 : ab Return
Constant pool (size = 1)
0x2d5c6af1f0b1: [FixedArray] in OldSpace
- map: 0x2d5c38940729 <Map>
- length: 1
0: 0x2d5c6af1f021 <FixedArray[4]>
Handler Table (size = 0)
Все операторы байт-кода в Ignition можно найти вИсходный код V8Если вам интересно, вы можете проверить это самостоятельно.
6. Оптимизирующий компилятор TurboFan с оптимизацией и деоптимизацией
Чем меньше изменений в типе входных данных функции необходимо учитывать компилятору, тем меньше и быстрее сгенерированный код.
Как мы все знаем, JavaScript — язык со слабой типизацией. В стандарте ECMAScript много неясностей и оценок типов, поэтому код, сгенерированный базовым компилятором, выполняется неэффективно.
Например,+
Операндом оператора может быть целое число, число с плавающей запятой, строка, логическое значение и другие типы ссылок, не говоря уже о различных комбинациях между ними (вы можете почувствоватьВ стандарте ECMAScript для+
Определение).
function addTwo(a, b) {
return a + b;
}
addTwo(2, 3); // 3
addTwo(8.6, 2.2); // 10.8
addTwo("hello ", "world"); // "hello world"
addTwo("true or ", false); // "true or false"
// 还有很多组合...
Но это не значит, что код JavaScript нельзя оптимизировать. Для определенной программной логики параметры, которые она получает, часто имеют фиксированный тип. По этой причине в V8 реализована технология обратной связи по типу. V8 использует обратную связь типа для динамической проверки всех параметров по мере выполнения операции.
Проще говоря, для многократно выполняемого кода, если несколько исполнений передают параметры одного и того же типа, V8 будет считать, что типы параметров каждого последующего выполнения также одинаковы, и оптимизирует код. Базовая проверка типов сохраняется в оптимизированном коде. V8 всегда будет выполнять оптимизированный код, если тип параметра не меняется при каждом последующем выполнении. Когда тип параметра, переданный в более позднем выполнении, изменяется, V8 «отменяет» предыдущую операцию оптимизации, которая называется «деоптимизация».
Немного изменим приведенный выше код и проанализируем процесс его оптимизации в V8.
// example2.js
function addTwo (a, b) {
return a + b;
}
for (let j = 0; j < 100000; j++) {
if (j < 80000) {
addTwo(10, 10);
} else {
addTwo('hello', 'world');
}
}
d8 --trace-opt --trace-deopt example2.js
[marking 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> for optimized recompilation, reason: hot and stable]
[compiling method 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> using TurboFan OSR]
[optimizing 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> - took 5.268, 5.305, 0.023 ms]
[deoptimizing (DEOPT soft): begin 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> (opt #0) @2, FP to SP delta: 96, caller sp: 0x7ffee48218c8]
;;; deoptimize at <example2.js:10:5>, Insufficient type feedback for call
reading input frame => bytecode_offset=80, args=1, height=5, retval=0(#0); inputs:
0: 0x2ecfb2a5f229 ; [fp - 16] 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)>
1: 0x2ecfbcf815c1 ; [fp + 16] 0x2ecfbcf815c1 <JSGlobal Object>
2: 0x2ecfb2a418c9 ; [fp - 80] 0x2ecfb2a418c9 <NativeContext[253]>
3: 0x2ecf2a140d09 ; (literal 4) 0x2ecf2a140d09 <Odd Oddball: optimized_out>
4: 0x000000027100 ; rcx 80000
5: 0x2ecfb2a5f299 ; (literal 6) 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)>
6: 0x2ecfb2a5efd1 ; (literal 7) 0x2ecfb2a5efd1 <String[#5]: hello>
7: 0x2ecfb2a5efe9 ; (literal 8) 0x2ecfb2a5efe9 <String[#5]: world>
8: 0x2ecf2a140d09 ; (literal 4) 0x2ecf2a140d09 <Odd Oddball: optimized_out>
translating interpreted frame => bytecode_offset=80, variable_frame_size=48, frame_size=104
0x7ffee48218c0: [top + 96] <- 0x2ecfbcf815c1 <JSGlobal Object> ; stack parameter (input #1)
-------------------------
0x7ffee48218b8: [top + 88] <- 0x00010bd36b5a ; caller's pc
0x7ffee48218b0: [top + 80] <- 0x7ffee48218d8 ; caller's fp
0x7ffee48218a8: [top + 72] <- 0x2ecfb2a418c9 <NativeContext[253]> ; context (input #2)
0x7ffee48218a0: [top + 64] <- 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> ; function (input #0)
0x7ffee4821898: [top + 56] <- 0x2ecfb2a5f141 <BytecodeArray[99]> ; bytecode array
0x7ffee4821890: [top + 48] <- 0x00000000010a <Smi 133> ; bytecode offset
-------------------------
0x7ffee4821888: [top + 40] <- 0x2ecf2a140d09 <Odd Oddball: optimized_out> ; stack parameter (input #3)
0x7ffee4821880: [top + 32] <- 0x000000027100 <Smi 80000> ; stack parameter (input #4)
0x7ffee4821878: [top + 24] <- 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)> ; stack parameter (input #5)
0x7ffee4821870: [top + 16] <- 0x2ecfb2a5efd1 <String[#5]: hello> ; stack parameter (input #6)
0x7ffee4821868: [top + 8] <- 0x2ecfb2a5efe9 <String[#5]: world> ; stack parameter (input #7)
0x7ffee4821860: [top + 0] <- 0x2ecf2a140d09 <Odd Oddball: optimized_out> ; accumulator (input #8)
[deoptimizing (soft): end 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> @2 => node=80, pc=0x00010bd394e0, caller sp=0x7ffee48218c8, took 0.331 ms]
[marking 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> for optimized recompilation, reason: hot and stable]
[marking 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)> for optimized recompilation, reason: small function]
[compiling method 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)> using TurboFan]
[compiling method 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> using TurboFan OSR]
[optimizing 0x2ecfb2a5f229 <JSFunction (sfi = 0x2ecfb2a5f049)> - took 0.161, 0.441, 0.018 ms]
[optimizing 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)> - took 0.096, 0.231, 0.007 ms]
[completed optimizing 0x2ecfb2a5f299 <JSFunction addTwo (sfi = 0x2ecfb2a5f0b1)>]
В этом коде мы выполняем 100 000 раз+
операция, где первые 80 000 раз — это сложение двух целых чисел, а последние 20 000 раз — сложение двух строк.
Отслеживая запись оптимизации V8, мы видим, что операция деоптимизации запускается, когда тип параметра меняется с целого на строку в строке 10 кода (т. е. 80 001-е выполнение).
Следует отметить, что деоптимизация стоит дорого, поэтому старайтесь избегать запуска деоптимизации при написании функций.
7. Сбор мусора
Когда память больше не нужна, она освобождается периодически работающим сборщиком мусора.
У любого сборщика мусора есть несколько основных задач, которые необходимо выполнять периодически.
- Определить живые / мертвые объекты
- Восстановление / повторное использование памяти, занятую мертвыми объектами
- Сжатие/дефрагментация памяти (необязательно)
В V8 есть три основных этапа сборки мусора: маркировка, очистка и уборка.
Гипотеза поколения
Поколенческая гипотеза, также известная как слабая поколенческая гипотеза. Эта гипотеза предполагает, что большинство вновь созданных объектов умирают («сгорают») после распределения, в то время как более старые объекты обычно живут вечно.
Сборка мусора V8 основана на гипотезе поколений, которая делит память на молодое и старое поколения.
Источник:Блог V8Как показано, молодое поколение далее подразделяется на дошкольное и промежуточное подпоколения (деление вполне логично). Юные объекты закреплены за детским садом детей молодого поколения. Если объект переживет первую сборку мусора, его бит флага изменится, войдет в логическое промежуточное поколение и все еще будет существовать в новом поколении в физической памяти. Если объект переживает следующую сборку мусора, он переходит к старому поколению. Процесс перемещения объектов от молодого поколения к старому называется продвижением.
V8 использует разные стратегии сборки мусора в новом и старом поколении, чтобы сделать сборку мусора более целенаправленной и эффективной. В то же время V8 также ограничивает объем памяти нового поколения и старого поколения.
название | алгоритм | размер |
---|---|---|
Кайнозой | Алгоритм параллельной очистки | 32 МБ (64-разрядная версия) / 16 МБ (32-разрядная версия) |
старое поколение | Mark Sweep, Mark Collation Algorithms | 1400 МБ (64-разрядная версия) / 700 МБ (32-разрядная версия) |
Следует отметить, что по мере увеличения памяти количество сборок мусора будет уменьшаться, но время, требуемое каждый раз, также будет увеличиваться, что негативно скажется на производительности и отзывчивости приложения, поэтому чем больше память, тем лучше.
Кайнозой
V8 использует алгоритм Parallel Scavenge, аналогичный алгоритму Холстеда (алгоритм Чейни, использовавшийся до V8 v6.2), и его ядром является алгоритм репликации.
Алгоритм репликации — это способ обмена места на время.
V8 разделяет молодое поколение на два полупространства одинакового размера, называемые пространством Формы и пространством До. Во время сборки мусора V8 проверяет пространство From на живые объекты и копирует их в пространство To. После этого V8 сразу освободит пространство, соответствующее мертвому объекту. После того, как каждая копия будет завершена, позиции From и To будут заменены местами.
Когда объект переживает копию, он перемещается в старое поколение, этот процесс называется продвижением.
старое поколение
Согласно гипотезе поколения, объекты в старом поколении, как правило, бессмертны, т. е. их редко нужно собирать, а это означает, что использовать алгоритм репликации в старом поколении невозможно. V8 использует алгоритмы маркировки-очистки и маркировки-сортировки для сборки мусора в старом поколении.
Марк-развертка
Tag Sweep существует уже более полувека. Принцип его алгоритма очень прост. Сборщик мусора начинается с корневого узла, помечает объекты, на которые непосредственно ссылается корень, а затем рекурсивно помечает объекты, на которые непосредственно ссылаются эти объекты. Достижимость объекта используется как основа «выживания».
Время, затрачиваемое алгоритмом маркировки-развертки, пропорционально количеству живых объектов.
Марк-Компакт
Алгоритм сортировки меток является продуктом объединения алгоритма репликации и алгоритма очистки меток.
Когда мы выполняем очистку меток, может произойти фрагментация памяти, и эти фрагменты вредны для выделения памяти нашей программой.
В качестве крайнего примера на рисунке ниже синие объекты — это новые объекты, которые требуют от нас выделения памяти.До дефрагментации памяти все фрагментированные пространства не могут содержать полные объекты, а после дефрагментации памяти фрагментированные пространства объединяются в Большое пространство также может вместить новые объекты.
Преимущества и недостатки алгоритма сортировки тегов очевидны. Его преимущество в том, что он может сделать использование кучи более полным и эффективным. Его недостаток в том, что он требует дополнительного времени сканирования и времени перемещения объекта, а затрачиваемое время пропорционально размеру кучи.
Максимальное резервирование места - «ошибка», которая давно циркулирует в сообществе.
V8 будет резервировать место для нового и старого поколений в куче памяти, что расширяет концепцию максимального зарезервированного пространства (Max Reserved). Основными факторами, влияющими на размер максимального зарезервированного пространства, являются:max_old_generation_size_
(максимальное пространство старого поколения) иmax_semi_space_size_
(Максимальное полупространство нового поколения). Среди них первый можно передать в Node.--max-old-space-size
указано.
Метод расчета, который долгое время циркулировал в сообществе: «максимальное зарезервированное пространство = 4 * максимальное полупространство нового поколения + максимальное пространство старого поколения», источник которого должен исходить от г-на Пу Линга.«Введение в Node.js». Но с момента публикации книги (декабрь 2013 г.) и по настоящее время способ расчета максимального зарезервированного пространства фактически дважды корректировался.
5.1.277 и более ранние версии (версии, соответствующие «Введение в Node.js»)
// Returns the maximum amount of memory reserved for the heap. For
// the young generation, we reserve 4 times the amount needed for a
// semi space. The young generation consists of two semi spaces and
// we reserve twice the amount needed for those in order to ensure
// that new space can be aligned to its size.
intptr_t MaxReserved() {
return 4 * reserved_semispace_size_ + max_old_generation_size_;
}
Версия 5.1.278
// Returns the maximum amount of memory reserved for the heap.
intptr_t MaxReserved() {
return 2 * max_semi_space_size_ + max_old_generation_size_;
}
Версия 7.4.137
size_t Heap::MaxReserved() {
const size_t kMaxNewLargeObjectSpaceSize = max_semi_space_size_;
return static_cast<size_t>(2 * max_semi_space_size_ +
kMaxNewLargeObjectSpaceSize +
max_old_generation_size_);
}
Проще говоря, эти две корректировки численно изменяют коэффициент «наибольшего полупространства молодого поколения» с 4х на 2х и на 3х.
в соответствии сПримечания к выпуску для Node.js, соответствующая связь между указанной выше версией V8 и версией Node.js:
версия V8 | Версия Node.js |
---|---|
5.1.277 и ранее | 6.4.0 и ранее |
5.1.278 - 7.4.136 | После 6.4.0, до 12.0.0 |
7.4.137 и выше | 12.0.0 и выше |
Учитывая, что версия Node.js 6.4.0 была выпущена ранее, в августе 2016 года, а текущая LTS-версия больше не поддерживается, можно обоснованно предположить, что в настоящее время используются второй и третий методы расчета. Однако информация в сообществе редко упоминает эти два изменения (я нашел только одноЗнай колонкуВторой метод расчета упоминается здесь), в то же время, есть еще много недавно опубликованных статей, которые все еще используют первый метод расчета без указания версии Node.js, что легко заставить читателей думать, что метод расчета максимальной зарезервированное место не изменилось.Устаревшая информация явно создала "ошибки".
8. Кэш кода
В браузере Chrome есть много функций, которые в большей или меньшей степени влияют на выполнение JavaScript, одна из которых — кэширование кода.
Технология кэширования кода ускоряет загрузку и выполнение JavaScript, когда пользователь посещает одну и ту же страницу, а файл сценария, связанный с этой страницей, не изменился.
Источник:Блог V8
Кэш кода делится на три уровня: холодный, теплый и горячий.
-
В первый раз, когда пользователь запрашивает файл JS (т. е. холодный запуск), Chrome загружает файл и передает его в V8 для компиляции, а также кэширует файл на диск.
-
Когда пользователь запрашивает этот JS-файл во второй раз (т. е. при предварительном запуске), Chrome извлечет файл из кеша браузера и снова передаст его V8 для компиляции. После компиляции на этапе теплого запуска скомпилированный код десериализуется и добавляется к кэшированному файлу сценария в виде метаданных.
-
Когда пользователь запрашивает этот JS-файл в третий раз (т. е. горячий запуск), Chrome извлекает файл и метаданные из кеша и передает их в V8. V8 пропустит этап компиляции и напрямую десериализует метаданные.
Ссылки по теме
использованная литература
Карьера
HIGO — известный в Китае магазин модной одежды, которым руководит г-н Сюй Иронг, основатель Meilishuo. Мы мечтаем, чтобы в Китае была лучшая красота и дизайн в мире.
Мы чествуем людей, которые любят создавать новые вещи. Они полны энтузиазма, критичны и веселы. Мы верим, что они сделают мир лучше. Мы такая группа людей, если вы тоже, вы можете присоединиться!
Пожалуйста, отправьте свое резюме наwenchenghua@higohappy.com.