3

I am trying to calculate the memory usage of a string in v8, and I know a single character would take 2 bytes. But when I check the shallow size and retained size in the devtools, I got confused of the result:

function Student() {
    this.name = 'lll';
}

var a = new Student();

var b = new String("ccccc");

enter image description here

The shallow size and retained size are both 16 bytes. Why?

As I imagine, the shallow size and the retained size would be equal, and the value would be 6 bytes. If I change the string from 'lll' to 'llll', the value would increase by 2 bytes. But it stays the same as below:

enter image description here

Can anybody explain that to me?

Jimmy
  • 43
  • 7
  • https://www.yourkit.com/docs/java/help/sizes.jsp May be this article could help you – Dpk Aug 15 '21 at 06:34
  • @Dpk ... java is NOT javascript – Bravo Aug 15 '21 at 06:41
  • Do some more tests with longer strings, perhaps a string is always stored in a multiple of 16 bytes – Bravo Aug 15 '21 at 06:43
  • @Bravo i know i just trying to help in getting understanding the terms shallow size and retained size – Dpk Aug 15 '21 at 06:43
  • @Dpk .. OK, are you sure this is relevant to javascript though? – Bravo Aug 15 '21 at 06:44
  • 1
    https://stackoverflow.com/questions/62049063/retained-size-in-chrome-memory-snapshot-what-exactly-is-being-retained – Dpk Aug 15 '21 at 06:45
  • that is better at the explanation of the difference between retained and shallow size - but that wasn't the question – Bravo Aug 15 '21 at 06:48

1 Answers1

15

(V8 developer here.)
General note up front: strings are very common on the web, so JavaScript engines go to great lengths to implement many different optimizations for many different things you can do with strings, so as a result, the string handling system in a modern JS engine tends to be very complicated. With that out of the way, we can focus on "simple" strings here.

a single character would take 2 bytes

There's a bit more to it: V8 internally distinguishes between one-byte and two-byte strings. When all characters in a given string can be represented with just one byte, then (usually) that's what V8 will do.

The shallow size and retained size are both 16 bytes. Why?

Commenters have already posted a link to a description of the difference between "shallow" and "retained" size, so I won't get into that. For simple strings it is indeed always the same value.

All objects on the heap start with a shape descriptor, which takes one pointer size (usually 4 bytes these days, thanks to "pointer compression" on 64-bit platforms).

Strings additionally have two more fields of 4 bytes each in their object header: the string's hash (which is needed a lot, so to avoid having to recompute it all the time, it is cached there), and the string's length.

After that, they store the actual characters. The size of any heap object must be a multiple of the pointer size, i.e. a multiple of 4, so the size of the string is rounded up to that; the last few bytes may be unused.

So, in summary, the size of a simple string with n ASCII characters is:

12 + 4 * Math.ceil(n/4)

(This may change over time, it'll be different if pointer compression was turned off at build time, it'll be different when there are two-byte characters in the string, it'll be different when the string is a "sliced" or "cons" string, it'll be different when the string is shared with Blink, and I'm probably forgetting some cases where it'll also be different.)

If you extend your experiment just a little, you'll see that:
"" takes 12 bytes
"1" through "1234" take 16 bytes
"12345" through "12345678" take 20 bytes
"123456789" takes 24 bytes, and so on.

jmrk
  • 34,271
  • 7
  • 59
  • 74