3

How does V8 store integers in memory?

For example the integer 5?

I know it stores it the heap, but how exactly does it store it? Things like metadata and the actual value itself. Is there a constant added to the int before storing it?

  • 1
    All numbers in JavaScript are 8-byte floating point values. – Pointy Aug 04 '19 at 17:30
  • Is there some kind of constant added to the var in memory? – Спасимир Павлов Aug 04 '19 at 17:33
  • Why do you care? High level languages like JavaScript abstract all that, so for most (almost all) purposes it completely does not matter. – Pointy Aug 04 '19 at 18:01
  • @Pointy Perhaps because knowing the representable range of numbers could help avoid overflow bugs. Or because you want to know whether truncation occurs during integer division. Or because knowing details like that might affect optimization decisions, or aid in debugging. Or maybe you're designing your own scripting language and want to know how other languages did things. Or simply because there's nothing wrong with acquiring knowledge for its own sake. – Ray Aug 04 '19 at 21:45
  • @Ray, whatever the implementation choses must not be semantically observable, so no overflow bugs or truncation errors. It can only affect performance. – Andreas Rossberg Aug 05 '19 at 20:19
  • @AndreasRossberg The fact that `var n = 1, m=5; for (i = 0; i < 32; i++) n += 1 << i; alert(n + ", " + m/2);` shows `0, 2.5` instead of `4294967296, 2` suggests that overflow and truncation both happen. And truncation isn't an *error*; it's just a consequence of storing variables as ints instead of floats. But it's worth knowing whether it will happen. The ECMAscript spec seems to require that numbers all use 64-bit IEEE754 representations, which definitely have physical limitations of this sort. But I'm admittedly not very familiar with the JS spec, so I may be overlooking something. – Ray Aug 05 '19 at 20:46
  • 1
    @Ray, `m/2` is `2.5` because everything is a float, so no surprise there. The other value is 0 because `<<` is defined by the language spec to truncate to int32 in 2's complement, not because of representation choices by the engine (for i=30, n becomes 2^31, for i=31, `1< – Andreas Rossberg Aug 05 '19 at 21:24
  • The answer from @Superfly and the source code linked to indicate why it matters how "small" integers are represented: It affects performance and storage allocation. For example consider the code to be generated for a loop that iterates through integers from 1 to a million. Clever representation of integers can make this code run faster and also potentially reduce the stress on memory allocation and garbage collection. – Cris P Jan 10 '23 at 20:24

2 Answers2

11

V8 uses a pointer tagging scheme to distinguish small integers and heap object pointers. 5 would be stored as a Smi type, which is not heap allocated in V8.

You can check out the source code for the Smi class to learn more.

On 32-bit platforms, Smis are a 31 bit signed int with a 0 set for the bottom bit. On 64-bit platforms, Smis are a 32 bit signed int, 31 bits of 0 padding and a 0 for the bottom bit. Pointers to heap objects have a 1 set for the bottom bit so that V8 can tell the difference between pointers and Smis without extra metadata.

Superfly
  • 571
  • 3
  • 13
  • 3
    Because of [pointer compression](https://v8.dev/blog/pointer-compression), contemporary versions of V8 store SMIs as 31-bit ints even on 64-bit platforms now. This allows to store pointers as 4 bytes and considerably decrease memory consumption. – shitpoet Feb 05 '22 at 16:35
-4

In Javascript, all numbers are stored as 64bit floating point values. C and C++ call this type double. There is no distinct "integer" type.

To some degree, you can use integer values naivly and get the result you expect, without having to fear rounding errors. These integers are so called "safe" integers.

All integers in the range [-(2^53 - 1), +(2^53 - 1)] are "safe" integers, as described here. This means that if you add, subtract or multiply integers in that range, and the result is within that range too, then the calculation is without rounding errors.

Of course, all values in Javascript/V8 are somehow "boxed", because a variable doesn't have a type (except small integers which use tagged pointers). If you have a variable x that is 5.25, it has to know that it is a "number" and that that number is 5.25. So it will take more than 8 bytes of space. You will have to look up the source code of v8 to find out more.

Michael
  • 6,451
  • 5
  • 31
  • 53
  • 4
    This is incorrect. V8, like most other language runtimes, will unbox small integers (abbreviated "smi" in V8) into single (tagged) words. – Andreas Rossberg Aug 04 '19 at 18:43
  • @AndreasRossberg that may be true, but the language spec is clear: numbers are 64-bit double-precision floating point values. Internal optimization is opaque to the programmer or else the implementation is broken. – Pointy Aug 04 '19 at 19:52
  • 7
    @Pointy, correct, but irrelevant, since the OP's question explicitly was about the implementation, not the language spec. – Andreas Rossberg Aug 04 '19 at 22:06