Estoy tratando de calcular el uso de memoria de una cadena en v8, y sé que un solo carácter tomaría 2 bytes. Pero cuando compruebo el tamaño superficial y el tamaño retenido en las herramientas de desarrollo, me confundí con el resultado:
function Student() { this.name = 'lll'; } var a = new Student(); var b = new String("ccccc");El tamaño superficial y el tamaño retenido son ambos de 16 bytes. ¿Por qué?
Como imagino, el tamaño superficial y el tamaño retenido serían iguales, y el valor sería de 6 bytes. Si cambio la cadena de 'lll' a 'llll', el valor aumentaría en 2 bytes. Pero queda igual que a continuación:
¿Alguien puede explicarme eso?
(Desarrollador V8 aquí.)
Nota general desde el principio: las cadenas son muy comunes en la web, por lo que los motores de JavaScript hacen todo lo posible para implementar muchas optimizaciones diferentes para muchas cosas diferentes que puede hacer con las cadenas, por lo que, como resultado, el sistema de manejo de cadenas en un motor JS moderno tiende ser muy complicado. Con eso fuera del camino, podemos centrarnos en cadenas "simples" aquí.
un solo carácter tomaría 2 bytes
Hay algo más: V8 distingue internamente entre cadenas de uno y dos bytes. Cuando todos los caracteres en una cadena determinada se pueden representar con solo un byte, entonces (generalmente) eso es lo que hará V8.
El tamaño superficial y el tamaño retenido son ambos de 16 bytes. ¿Por qué?
Los comentaristas ya han publicado un enlace a una descripción de la diferencia entre el tamaño "superficial" y "retenido", por lo que no entraré en eso. Para cadenas simples, de hecho, es siempre el mismo valor.
Todos los objetos en el montón comienzan con un descriptor de forma, que toma un tamaño de puntero (generalmente 4 bytes en estos días, gracias a la "compresión de puntero" en plataformas de 64 bits).
Las cadenas también tienen dos campos más de 4 bytes cada uno en su encabezado de objeto: el hash de la cadena (que se necesita mucho, así que para evitar tener que volver a calcularlo todo el tiempo, se almacena en caché allí) y la longitud de la cadena.
Después de eso, almacenan los caracteres reales. El tamaño de cualquier objeto del montón debe ser un múltiplo del tamaño del puntero, es decir, un múltiplo de 4, por lo que el tamaño de la cadena se redondea a ese valor; los últimos bytes pueden estar sin usar.
Entonces, en resumen, el tamaño de una cadena simple con n caracteres ASCII es:
12 + 4 * Math.ceil(n/4)(Esto puede cambiar con el tiempo, será diferente si la compresión del puntero se desactivó en el momento de la compilación, será diferente cuando haya caracteres de dos bytes en la cadena, será diferente cuando la cadena sea "cortada"). " o "contras", será diferente cuando la cadena se comparta con Blink, y probablemente me esté olvidando de algunos casos en los que también será diferente).
Si extiende su experimento solo un poco, verá que:
"" ocupa 12 bytes
"1" a "1234" ocupan 16 bytes
"12345" a "12345678" ocupan 20 bytes
"123456789" ocupa 24 bytes, y así sucesivamente.