当前位置: 欣欣网 > 码农

「字符串」存在「栈内存」?那我可要杠你了哦!

2024-03-27码农


前言

大家好,我是林三心, 用最通俗易懂的话讲最难的知识点 是我的座右铭, 基础是进阶的前提 是我的初心。

在我们的认知里:基础类型存栈内存,引用数据类型存堆内存。

const a = '林三心'
const b = {
age18,
height180
}

超长字符串

大家都知道, 字符串 属于 基础类型 ,所以大家会觉得 字符串 是存在 栈内存 中的,但是大家要知道,V8默认栈内存是 984Kib ,那如果一个 超长字符串 > 984Kib 能装的进 栈内存 吗?这也就是一个比较经典的问题—— 大象装箱问题 ,试问:一头大象能装进一个小箱子里吗?

一探究竟

堆快照

先来看一段代码

const func = function() {
this.str1 = '林三心'
this.str2 = 'Sunshine_Lin'
}
const a = new func()
const b = new func()

然后咱们来看看 堆快照 的详情

上面的结果可以看出:

  • a 和 b的 str1 都指向同一个地址

  • a 和 b的 str2 都指向同一个地址

  • 那我们可不可以猜测出一个结论: 字符串的内容存于堆内存中,指针存于栈内存中,且相同的字符串指向同一个堆内存地址

    修改和新增字符串

    我们稍微修改下代码

    const func = function() {
    this.str1 = '林三心'
    this.str2 = 'Sunshine_Lin'
    }
    const a = new func()
    const b = new func()
    // 修改str1
    a.str1 = '哈哈哈哈哈哈哈哈哈哈'
    // 新增str3,跟str2一样
    a.str3 = 'Sunshine_Lin'

    再来看看现阶段的 堆快照 的详情

    上面的结果可以看出:

  • str1 修改成一个新的字符串后,重新开辟了一个内存空间(新地址)

  • str3 新增之后,指针指向已有的 Sunshine_Lin 的内存空间

  • 那我们可不可以猜测出一个结论: 新增或者修改字符串后,如果是一个之前不存在的字符串,则新开辟内存空间,如果是已有的,则直接使用已有的内存空间

    源码分析

    当我们声明一个字符串时:

  • 1、v8内部有一个名为 stringTable hashmap 缓存了所有字符串,在V8阅读我们的代码,转换抽象语法树时,每遇到一个字符串,会根据其特征换算为一个 hash值 ,插入到 hashmap 中。在之后如果遇到了 hash值 一致的字符串,会优先从里面取出来进行比对,一致的话就不会生成新字符串类。

  • 2、缓存字符串时,根据字符串不同采取不同 hash 方式。

  • 源码

    image.png

    image.png

    通俗易懂总结

    字符串的数据存于 堆内存 中,栈内存 中只是存其 地址指针

    当我们新建一个字符串时,V8会从内存中查找一下是否已经有存在的一样的字符串,找到的话直接复用。如果找不到的话,则开辟一块新的内存空间来存这个字符串,并把地址赋给变量。

    大家有没有想过,为什么字符串不能通过下标索引来进行修改呢?因为字符串的修改本质上只能是通过整个的修改,而不能局部修改。

    结语

    喜欢林三心的朋友,可以关注公众号【前端之神】,进入【千人学习大群】哦,或者可以给三心点个赞,点个在看哦,谢谢你