In-depth discussion of memory principles: Are variables stored in the heap or stack in JS?

In-depth discussion of memory principles: Are variables stored in the heap or stack in JS?

Are primitive types stored on the heap or the stack in JavaScript ?

---- Basic types of non-basic types

Seeing this question, I believe everyone feels that this question is so basic that it cannot be more basic. Just search Baidu and you will see many people saying: basic types are stored in the stack, and reference types are stored in the heap.

Is it really that simple?

1. The elephant that can’t fit in the refrigerator

Let's look at this code:

Here, we declare a 67MiB string, which would be hard to explain if the string actually existed on the stack. After all, the default stack size of v8 is 984KiB. It definitely can’t be saved.

Note: V8 has different string size limits in different operating systems at different times. The approximate range is 256MiB ~ 1GiB

node --v8-options | grep -B0 -A1 stack-size


At this point, are you starting to wonder? Is it possible that the answer from Baidu is wrong and I have to search on Google?

Let's see what's going on.

2. Shadow clone string

const BasicVarGen = function () {
    this.s1 = 'IAmString'
    this.s2 = 'IAmString'
}


let a = new BasicVarGen()
let b = new BasicVarGen()

Here, we declare two identical objects, each containing two identical strings.

Through the developer tools, we can see that although we declared four strings, their memory points to the same address.

Note: chrome cannot view the actual address, here is the abstract address

What does this mean? It shows that the four strings contain reference addresses.

So the elephant that cannot fit into the refrigerator in the above article is easy to explain. The string is not stored in the stack, but in another place, and then the address of this place is stored in the stack.

So, let's modify the contents of one of the strings.

const BasicVarGen = function () {
    this.s0 = 'IAmString'
    this.s1 = 'IAmString'
}


let a = new BasicVarGen()
let b = new BasicVarGen()
debugger
a.s0 = 'different string'
a.s2 = 'IAmString'

Memory snapshot before debugger

Memory snapshot after debugger

We can see that the initial content of a.s0 is ' IAmString' , and after we modify its content, the address changes.

The newly added a.s2 has the content of ' IAmString' and its address is consistent with other variables whose value is ' IAmString' .

When we declare a string:

  1. There is a hashmap called stringTable inside v8 that caches all strings. When V8 reads our code and converts the abstract syntax tree, every time it encounters a string, it will convert it into a hash value based on its characteristics and insert it into hashmap . If a string with the same hash value is encountered later, it will be taken out for comparison first. If it is consistent, no new string class will be generated.
  2. When caching strings, different hash methods are used depending on the string.

So let's sort it out. When we create a string, V8 will first search the memory (hash table) to see if there is an identical string that has already been created. If it exists, it will be reused directly. If it does not exist, a new memory space is opened to store the string, and then the address is assigned to the variable. This is why we cannot modify strings directly using subscripts: strings in V8 are immutable.

Take a basic type copy of js as an example to explain the implementation logic of v8 and the conventional logic that everyone understands (Yawen)

example:

var a = "刘潇洒"; // After V8 reads the string, it goes to stringTable to find out if it exists. If it does not exist, it inserts '刘潇洒' into hashTable and stores the reference of '刘潇洒' into a
var b = a; // Directly copy the reference of '刘潇洒' b = "谭雅文"; // Search for no entry in stringTable


Questions:

const BasicVarGen = function () {
    this.s0 = 'IAmString'
    this.s1 = 'IAmString'
}


let a = new BasicVarGen()
let b = new BasicVarGen()
debugger
a.s0 = 'different string'
a.s2 = 'IAmString'


a.s3 = a.s2+a.s0; // Question: What operations are performed on string concatenation?
a.s4 = a.s2+as

Apply for two concatenated strings at the same time, with the same content.

As you can see, the content is the same. But the addresses are not the same. Moreover, the Map description in front of the address has also changed.

If strings are concatenated in a traditional way (such as SeqString ), the time complexity of the concatenation operation is O(n). Using Rope Structure (that is, the data structure used by ConsString ) can reduce the time spent on concatenation.

If this is true for strings, is it also true for other primitive types?

3. The "Strange Ball" I See in Person

After talking about strings, let's look at another typical 'basic type' in V8: oddBall .

Extended from oddBall type

Let's do another little experiment:

We can see the basic types listed in the figure above, and the addresses are the same. When assigning values, they are also reused in place. (And the addresses of these basic types extended from oddBall are fixed, that is, when V8 runs for the first time, regardless of whether we declare these basic types or not, they have been created. When we declare objects, we assign their references. This also explains why we say that basic types are assigned to the stack: in V8, the value stored in @73 is always an empty string, so v8 can treat these addresses as the values ​​themselves.)

Let's look at the source code to verify:

Generate various oddBall types of methods, you can see that the return is an address

When undefined is assigned to a variable, it actually assigns the address

getRoot Method

Where the offset is defined

4. Confusing Numbers

The reason why it is called a confusing number is that the mechanism of memory allocation when it is allocated and changed has not yet been figured out. (Its memory is dynamic)

Numbers are divided into smi and heapNumber in V8.

smi directly stores integers in the range of -2³¹ to 2³¹-1 (2³¹≈2*10⁹)

heapNumber is similar to string and is immutable. Its scope is: all non-smi numbers

The lowest bit is used to indicate whether it is a pointer. If the lowest bit is 1, it is a pointer.

const o = {
  x: 42, // Smi
  y: 4.2, // HeapNumber
};


The 42 in ox will be treated as Smi and stored directly in the object itself, while the 4.2 in oy needs to be stored in an additional memory entity and the object pointer of oy is pointed to the memory entity.

If it is a 32-bit operating system, it is understandable to use 32 bits to represent smi, but in a 64-bit operating system, why is the smi range also -2³¹ to 2³¹-1 (2³¹≈2*10⁹)?

ECMAScript standard stipulates that number numbers need to be treated as 64-bit double-precision floating-point numbers, but in fact, it is very inefficient to always use 64 bits to store any number (space inefficiency, computational time inefficiency smi uses a lot of bit operations), so the JavaScript engine does not always use 64 bits to store numbers. The engine can use other memory representations (such as 32 bits) internally, as long as all the external features of the number that can be monitored are aligned with the 64-bit representation.

const cycleLimit = 50000
console.time('heapNumber')
const foo = { x: 1.1 };
for (let i = 0; i < cycleLimit; ++i) {
// Creates an extra heapNumber instance foo.x += 1; 
}
console.timeEnd('heapNumber') // slow   


console.time('smi')
const bar = { x: 1.0 };
for (let i = 0; i < cycleLimit; ++i) {
  bar.x += 1;
}
console.timeEnd('smi') // fast

Questions:

const BasicVarGen = function () {


    this.smi1 = 1
    this.smi2 = 2
    this.heapNumber1 = 1.1
    this.heapNumber2 = 2.1
}
    let foo = new BasicVarGen()
    let bar = new BasicVarGen()
    
    debugger
    
    baz.obj1.heapNumber1++

In the numbers, the value of a single number is not modified, but the addresses of other numbers are changed.

5. Summary: Where do basic types exist?

String: It exists in the heap and is a reference address in the stack. If the same string exists, the reference address is the same.

Numbers: Small integers are stored on the stack, and other types are stored on the heap.

Other types: A unique address is assigned when the engine is initialized, and the variables in the stack store unique references.

Here we can only roughly explain where the basic types exist.

This concludes this article on in-depth memory principles and whether variables in JS are stored in the heap or the stack. For more information on whether variables in JS are stored in the heap or the stack, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed explanation of JS variable storage deep copy and shallow copy
  • JS variable promotion and function promotion example analysis
  • Detailed explanation of js variables, scope and memory
  • Detailed explanation of js variables and their scope
  • A brief talk about JavaScript variable promotion
  • All properties of JavaScript variable Dom object
  • Basic usage of JavaScript variables
  • JavaScript variable declaration example analysis
  • Detailed explanation of Javascript variable scope and scope chain

<<:  How to run Linux commands in the background

>>:  Common solutions for Mysql read-write separation expiration

Recommend

Let's talk about my understanding and application of React Context

Table of contents Preface First look at React Con...

About the IE label LI text wrapping problem

I struggled with this for a long time, and after s...

How to solve the element movement caused by hover-generated border

Preface Sometimes when hover pseudo-class adds a ...

Summary of Commonly Used MySQL Commands in Linux Operating System

Here are some common MySQL commands for you: -- S...

Master-slave synchronization configuration of Mysql database

Table of contents Mysql master-slave synchronizat...

Solution to 1067 when Mysql starts in Windows

I just started working a few days ago and install...

Have you carefully understood Tags How it is defined How to use

Preface : Today I was asked, "Have you carefu...

idea combines docker to realize image packaging and one-click deployment

1. Install Docker on the server yum install docke...

Detailed process of installing nginx1.9.1 on centos8

1.17.9 More delicious, really Nginx download addr...

Detailed explanation of using javascript to handle common events

Table of contents 1. Form events 2. Mouse events ...

How to set the page you are viewing to not allow Baidu to save its snapshot

Today, when I searched for a page on Baidu, becaus...

18 killer JavaScript one-liners

Preface JavaScript continues to grow and prosper ...