How to enable flash plug-in in browser, how to enable camera in browser

This article mainly introduces how to enable JavaScript on mobile phones in the browser. It has certain reference value and friends in need can refer to it. I hope you will gain a lot after reading this article. Let the editor take you to understand it together.

This lesson will take you to explore the working mechanism of the JavaScript engine from two aspects: compilation process and memory management.

Compilation process

The basic workflow of the compiler was mentioned in “Snack 1: Handwritten CSS Preprocessor”, which generally includes 3 steps: Parsing, Transformation and Code Generation, with the JavaScript engine Compared with GPT rewriting, which generally follows this process, it can be divided into three steps: parsing, interpretation and optimization. Let’s take the V8 engine as an example to explain.

Analysis

The parsing step can be split into 2 small steps:

  • Lexical analysis, parsing JavaScript code into individual tokens;

  • Syntax analysis assembles tokens into an abstract syntax tree (AST).

Below is a simple code that declares a string variable and calls the function console.log for printing.

<code>var name = 'web'
console.log(name)
</code>

Through lexical analysis, this code will be parsed character by character to generate tokens similar to the following structure. These token types are different, including keywords, identifiers, symbols, and strings. .

<code>Keyword(var)
Identifier(name)
Punctuator(=)
String('web')
Identifier(console)
Punctuator(.)
Identifier(log)
Punctuator(()
Identifier(name)
Punctuator())
</code>

The syntax analysis phase will use tokens to generate an abstract syntax tree similar to the following structure. The process of generating the tree is not simply to add all tokens to the tree, but to remove unnecessary symbol tokens and follow the grammar rules. generate.

1.png

abstract syntax tree

Explanation

In Addition 1, we convert the AST into a new AST, and the JavaScript engine converts the AST into bytecode through the interpreter Ignition. Bytecode is an abstract description of machine code. Compared with machine code, its code size is smaller, which can reduce memory consumption.

The following code is an excerpt from the bytecode generated by the sample code. Its syntax is very close to assembly language, with many operators, such as StackCheck, Star, and Return. Considering that these operators are too low-level and involve the processor’s accumulator and register operations, which are beyond the scope of the front-end, they will not be introduced in detail here.

<code>[generated bytecode for function: log (0x1e680d83fc59 <SharedFunctionInfo log>)]
Parameter count 1
Register count 6
Frame size 48
 9646 E> 0x376a94a60ea6 @ 0 : a7 StackCheck
         ...
         0x376a94a60ec9 @ 35 : 26 f6 Star r5
 9683 E> 0x376a94a60ecb @ 37 : 5a f9 02 f7 f6 06 CallProperty2 r2, <this>, r4, r5, [6]
         0x376a94a60ed1 @ 43 : 0d LdaUndefined
 9729 S> 0x376a94a60ed2 @ 44 : ab Return
Constant pool (size = 3)
Handler Table (size = 0)
Source Position Table (size = 24)
</code>
Optimization

After getting the AST, the interpreter will interpret and execute it on demand. That is to say, if a function has not been called, it will not be interpreted and executed.

In this process, the interpreter will collect some repeated optimizable operations (such as type judgment) to generate analysis data, and then pass the generated bytecode and analysis data to the compiler TurboFan, and the compiler will generate height based on the analysis data. Optimized machine code.

The function of optimized machine code is very similar to caching. When the interpreter encounters the same content again, it can directly execute the optimized machine code. Of course, the optimized code may sometimes fail to run (for example, the function parameter type changes), then it will be de-optimized again into bytecode and handed over to the interpreter.

The entire process is shown in the flow chart below:

3.png

JavaScript compilation process

Memory management

The memory space of the JavaScript engine is divided into Heap and Stack. The heap and the stack are two different data structures. The heap is an array with a tree structure, and the stack is also an array, but follows the “first in, last out” rule.

Stack

The stack is a temporary storage space that mainly stores local variables and function calls (for global expressions, anonymous functions are created and called).

For local variables of basic data types (String, Undefined, Null, Boolean, Number, BigInt, Symbol), they will be created directly on the stack, while local variables of object data types will be stored in the heap, and only its reference address will be stored in the stack. , which is what we often call shallow copy. Global variables and closure variables also only store reference addresses. All in all, the data stored in the stack is lightweight.

For functions, the interpreter creates a “Call Stack” to record the function’s calling process. Every time a function is called, the interpreter will add the function to the call stack. The interpreter will create a stack frame for the added function (this stack frame is used to save the local variables and execution statements of the function) and immediately implement. If the executing function also calls other functions, the new function will also be added to the call stack and executed. Once the execution of this function ends, the corresponding stack frame will be destroyed immediately.

There are two ways to view the call stack:

  • Call the function console.trace() to print to the console;

  • Use browser developer tools for breakpoint debugging.

Example

The following code is a function that calculates the Fibonacci sequence. Its call stack information is obtained by calling the console.trace() function and breakpoints.

<code>function fib(n) {
  if (n < 3) return 1
  console.trace();
  return fib(n-1) + fib(n-2)
}
fib(4)
</code>

image (31).png

Example renderings

image (32).png

Example renderings

Although the stack is very lightweight and is only created when used and destroyed when used, it cannot grow infinitely. When the allocated call stack space is full, a “stack overflow” error will occur.

The following is a code fragment that causes a stack overflow error caused by a recursive function:

<code>(function recursive() {
  recursive()
})()
</code>

image (33).png

stack overflow error

Therefore, when we write a recursive function, we must pay attention to the function execution boundary, which is the condition for exiting the recursion.

Extension: tail call

Recursive calls are usually more memory-consuming operations because they are called more often and stack frames need to be saved for each layer of function calls. There are generally two ideas for optimizing recursion, reduce the number of recursions and use tail calls.

Tail call refers to the last step of a function returning a call to another function. For example, in the following code, function a() returns a call to function b().

<code>function a(x){
  return b(x);
}
</code>

As in the example below, returning cached function call results or returning multiple function calls are not “tail calls”.

<code>function a(x){
  let c = b(x);
  return c;
}
function a(x){
  return b(x) + c(x);
}
function a() {
  b(x)
}
</code>

Since the tail call is in the return statement and is the last operation of the function, local variables and other information do not need to be used anymore, so that the memory space can be saved immediately by releasing it.

The following sample code implements the function of finding the nth number in the Fibonacci sequence through recursion. Compared with function fib(), function fibTail() uses two optimization methods: tail calling and reducing the number of calls.

<code>function fib(n) {
  if (n < 3) return 1
  return fib(n-1) + fib(n-2)
}
function fibTail(n, a = 0, b = 1) {
  if (n === 0) return a
  return fibTail(n - 1, b, a + b)
}
</code>

However, because tail calls also have some hidden dangers, such as loss of error information and inconvenience in debugging, browsers and the Node.js environment do not support this optimization method by default.

Heap

The data stored in the heap space is relatively complex and can be roughly divided into the following five areas: Code Space, Map Space, Large Object Space, New Space, and Old Generation ( Old Space). This lesson focuses on the memory recycling algorithms of the new generation and the old generation.

New Generation

Most objects will initially be allocated in the new generation. This storage space is relatively small, only a few dozen MB, and is divided into two spaces: from space and to space.

The objects declared in the program will first be allocated to the from space. When garbage collection is performed, the surviving objects in the from space (surviving objects can be understood as referenced objects) will be copied to the to space for storage. The object space is recycled. When the copy is completed, the from space and the to space are exchanged, the to space will become the new from space, and the original from space will become the to space. This algorithm is called “Scavenge”.

The memory recycling frequency of the new generation is very high and the speed is fast, but the space utilization is low because half of the memory space is left in an “idle” state.

1.png

Scanvage recycling process

Old generation

Objects in the young generation that are still alive after multiple recycling will be transferred to the old generation with larger space. Because the old generation space is large, if the recycling method still uses the Scanvage algorithm to frequently copy objects, the performance overhead will be too high.

Therefore, the old generation uses another method of “Mark-Sweep” to reclaim unlived object space.

This method is mainly divided into two stages: marking and clearing. The marking phase will traverse all objects in the heap and mark surviving objects; the clearing phase will reclaim the space of unmarked objects.

5.png

Mark-and-sweep recycling process

Since mark-and-sweep does not split memory in half, no space is wasted. However, the memory space after mark clearing will produce a lot of discontinuous fragmented space. In this discontinuous fragmented space, when larger objects are encountered, it may not be possible to store them due to insufficient space.

In order to solve the problem of memory fragmentation and improve memory utilization, it is also necessary to use the Mark-Compact algorithm. Compared with the mark and clear algorithm, the marking and sorting algorithm has been improved in the recycling phase. Mark and sorting does not immediately recycle unmarked objects, but moves the surviving objects to the side and then cleans them up. Of course, this operation of moving objects is relatively time-consuming, so the execution speed is slower than mark clearing.

6.png

Marking the sorting and recycling process

Summary

The content of this class is low-level and abstract, with the emphasis on understanding and memory.

First, the execution process of the JavaScript engine is explained, including parsing, interpretation, and optimization. This part can be understood in conjunction with the compiler knowledge mentioned in Part 1.

Then it was mentioned that the memory of the JavaScript engine is divided into two parts: the stack and the heap. The stack can save function call information and local variables, and is characterized by “first in, last out” and “destroyed immediately after use”. The data objects stored in the heap area are usually relatively large and need to be processed by certain recycling algorithms, including the Scanvage algorithm for the new generation, and the mark clearing and mark sorting algorithms for the old generation.

Finally, here is a question: What other memory recycling algorithms have you learned about, and what are their advantages and disadvantages?

Featured Comments
**Tao:

I feel that the more I analyze the bottom layer, the more I understand the importance of the algorithm.

**Ling:

The teacher spoke very well

**Tong:

After reading this, I don’t quite understand. Is this the compilation process of js or the execution mechanism? Is this step of parsing done by the v8 engine? Isn’t the v8 engine an interpreter? But there are other interpreters later…Thank you for clarifying.

Lecturer’s reply:

Taking the V8 engine as an example to explain the JavaScript compilation process, V8 can compile and execute JavaScript code.

ezra.xu:

Reference counting, mark cleaning, mark sorting, generational recycling…

**Super:

Teacher, regarding obtaining the camera, microphone, and desktop, in which process are these implemented?

Lecturer’s reply:

When the camera/microphone is enabled, the browser starts the Video Capture process.

**Accompanying:

So is the garbage collection method in v8 the same as that in Java?

Lecturer’s reply:

Basically the same