📜 ⬆️ ⬇️

Nine questions about working with memory in V8

As you know, the JavaScript engine V8 is very popular. It is used in the Google Chrome browser, the Node.js platform is based on it. The material prepared by Matt Zeinert, the translation of which we publish today, gives nine questions on the peculiarities of how the V8 works with memory. Each question contains a piece of code that needs to be analyzed and found the answer that most accurately describes the memory consumption of this code or the data structures represented in it. Answers to questions are provided with comments.

image

Having dealt with this material, you will learn about some interesting features of how the V8 handles memory. Perhaps an understanding of these features will not be useful to you when looking for performance problems of those programs that you write all the time, but we believe this will help you better understand the internal mechanisms of the V8 engine, which means it may serve a good service in analyzing some unusual situation.

1. How much memory does each element of the array use?


To answer this question (and other similar questions), you need to divide the total memory consumed by the program by the length of the array. Here we will use, to indicate the length of the array, the number represented by the variable MAGIC_ARRAY_LENGTH , equal to 1304209. Later, in the comments to one of the questions, we will focus on why this value is used here. In the meantime, we note that such a large array length allows one to abstract from memory consumption by other parts of programs.

So, here is the code that you are invited to analyze.
')
 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push(Math.random()) } 

Answer Options


  1. 1 byte
  2. 4 bytes
  3. 8 bytes
  4. 16 bytes
  5. 24 bytes
  6. 35 bytes

Select the answer that you think is correct, expand the text of the explanations and check yourself.
Note: there will be “The correct answer ...” everywhere as the title of a collapsible block.

Correct answer…
The correct answer to this question is 8 bytes. The point here is that numbers in JavaScript are represented by 64-bit floating point values. In a byte of 8 bits, as a result, each number takes 64/8 = 8 bytes.

2. How much memory does each element of the array use?


 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push(Math.random()) } a.push("this is a string") 

Answer Options


  1. 2 bytes
  2. 4 bytes
  3. 8 bytes
  4. 16 bytes
  5. 24 bytes
  6. 35 bytes

Correct answer…
In this case, the correct answer is 24 bytes. In this example, we put the JS engine in a difficult position. The fact is that the array contains 2 different data types - numbers and strings.

The array contains a string reference. A link is simply an indication of where the data is stored in memory (in our case, the characters that make up the string). The address in memory is a number. System memory can be perceived as a huge array, and addresses in memory can be considered indices of this array.

As a result, it turns out that the string reference is a number, the other elements of the array are also numbers. How to distinguish them? How does a reference number differ from a regular number?

The answer to this question is given by the term “boxed value”. The system packs each number into an object and stores in the array a reference to this object. Now each element of the array can be a link.

In order to store a number in the array, we need to store the following data in memory:

  • Object link (8 bytes)
  • The object itself, in which the number is packed (16 bytes)

Why does an object representing a number take 16 bytes? To begin with, it must contain the number itself (64-bit floating point value). Each JS object has an internal property called a “hidden class”, which is another reference.

Why do you need 8 bytes to store the link? Remember that system memory is like an array? If a 32-bit addressing system is used, then it can be used to express array indices up to 2 ^ 32. If you store one byte for each array index, it means that you can operate 2 ^ 32 / (1024 * 1024 * 1024) = 4 GB of memory. Since most computers today have more than 4 GB of memory, you have to use 64-bit addresses to work with it (it takes 8 bytes to store the address). This is a rather simplified explanation of what is happening, however, it gives an idea of ​​how the addressing system works.

3. How much memory does each element of the array use?


 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push({}) } 

Answer Options


  1. 8 bytes
  2. 16 bytes
  3. 32 bytes
  4. 64 bytes
  5. 128 bytes
  6. 156 bytes

Correct answer…
In this case, the correct answer is 64 bytes. How much memory should be allocated to the V8 engine for storing an empty object? This is a difficult question. In particular, taking into account the fact that it is assumed that the object will not always be empty.

Here is a list of what V8 will store for each empty object:

  • Link to hidden class (8 bytes).
  • 4 empty cells to store the values ​​of the future properties of the object (32 bytes).
  • Empty cell for storing a reference to an additional object that will be used if more than 4 properties (8 bytes) are added to the original object.
  • Empty cell for an object that stores values ​​for indexes of numeric properties (8 bytes).

If you are interested in details on how V8 allocates memory for objects, take a look at this material .

As a result, we come to the fact that for one element of an array consisting of empty objects, we need 64 bytes, which include 56 bytes, which are required to store an object in memory, and 8 bytes, which are needed to store an object reference in an array.

4. How much memory does each element of the array use?


 var Obj = function(){} var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push(new Obj()) } 

Answer Options


  1. 8 bytes
  2. 16 bytes
  3. 32 bytes
  4. 64 bytes
  5. 128 bytes
  6. 156 bytes

Correct answer…
The correct answer to this question is 32 bytes. Here, again, empty objects are stored in the array, but this time we use the constructor function to create objects. V8 can study the program code, and understand that the objects created by the Obj() function contain nothing.

Therefore, the system may not create empty cells for some of the expected properties, placing in the new object only three 8-byte properties. Here they are:

  • Link to hidden class.
  • A cell for storing an object reference with additional properties.
  • A cell for referencing an object used to store indexes of numeric properties.

Do not forget that to store links to these empty objects requires another 8 bytes. The result is 32 bytes.

5. How much memory does each element of the array use?


 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push("Hello") } 

Answer Options


  1. 8 bytes
  2. 16 bytes
  3. 32 bytes
  4. 64 bytes
  5. 128 bytes
  6. 156 bytes

Correct answer…
The correct answer is 8 bytes. Each element of the array should store a 64-bit reference to the text value stored in memory. V8 will create only one line storing the text “Hello”, and all elements of the array will refer to it. Therefore, if we consider that we have a sufficiently large array, the size of the string can be neglected, and we will come to the conclusion that we need 8 bytes to store one element of the V8 array.

6. How much memory does each element of the array use?


 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH; i++) {   a.push(true) } 

Answer Options


  1. 1 byte
  2. 2 bytes
  3. 4 bytes
  4. 8 bytes
  5. 16 bytes
  6. 32 bytes

Correct answer…
To store one element of such an array requires 8 bytes. The value true stored in the array as an object reference, just as it does with strings. In the results, we again need to write 64-bit addresses to the elements of the array. Values ​​of false , undefined and null handled in a similar way.

7. What is the total amount of memory that this program consumes?


 var a = [] for (var i=0; i<1024 * 1024; i++) {   a.push(Math.random()) } 

Answer Options


  1. 2 MB
  2. 4 MB
  3. 8 MB
  4. 10 MB
  5. 16 MB
  6. 24 MB

Correct answer…
This program consumes 10 MB of memory. Here we store in the array a little more than a million numbers, each of which occupies 8 bytes. As a result, we can assume that the array will take about 8 MB of memory. However, in fact it is not. Elements in the JavaScript arrays can be added at any time, but V8 will not change the size of the array each time you add a new element to it. To do this, allocating memory for the array, the engine leaves some amount of free space at the end of the array.

In the previous examples, we used the number represented by the variable MAGIC_ARRAY_LENGTH . This number is on the border of the “spare” memory, which the system allocates to arrays. The value of MAGIC_ARRAY_LENGTH is 1304209, while 1024 * 1024 is 1048576. However, in that and in the other case, the amount of memory used by the array will be the same.

8. What is the total amount of memory that this program consumes?


 var a = new Array(1024 * 1024) for (var i=0; i<1024 * 1024; i++) {   a[i] = Math.random() } 

Answer Options


  1. 2 MB
  2. 4 MB
  3. 8 MB
  4. 10 MB
  5. 16 MB
  6. 24 MB

Correct answer…
In this case, the array will be allocated 8 MB of memory. Since the V8 knows in advance the size of the array, the system can allocate exactly as much memory as needed.

9. What is the total amount of memory that this program consumes?


 var a = new Int16Array(1024 * 1024) for (var i=0; i<1024 * 1024; i++) {   a[i] = 1 } 

Answer Options


  1. 2 MB
  2. 4 MB
  3. 8 MB
  4. 10 MB
  5. 16 MB
  6. 24 MB

Correct answer…
This program will need 2 MB of memory, since the array contains only 16-bit integers, each of which occupies 2 bytes. There are slightly more than a million such numbers, which means the need for 2 megabytes of memory.

Results


If you want to experiment on your own with everything that the questions from this material are devoted to, using the Chrome developer tools, you can use the following design:

 function Holder() {} var holder = new Holder() var MAGIC_ARRAY_LENGTH = 1304209 var a = [] for (var i=0; i<MAGIC_ARRAY_LENGTH;i++) {   a.push(null) } holder.a = a 

Here we are interested in the value placed in the class Holder , which simplifies its search.


Exploring Memory with Chrome Developer Tools

By the way, the author of this material says that, conducting experiments, he could not yet fully understand how the V8 works in memory with strings. If you manage to figure it out - sure, it will be interesting for many to find out about it.

Dear readers! Which of the questions presented in this material seemed to you the most difficult?

Source: https://habr.com/ru/post/350240/


All Articles