JS变量具体是存放在哪里呢?
- 基本数据类型保存在栈内存中
- 引用数据类型保存在堆内存中
- 基本数据类型6种: Undefined、Null、Boolean、Number、String、Symbol, 由于他们在内存中分别占有固定大小的空间, 通过按值来访问.
- 引用数据类型: 也就是Object对象, 它的存储分为访问地址和实际存放的地方; 访问地址是存储在栈中的, 当查询引用类型变量的时候, 会先从栈中读取内存地址(也就是访问地址), 然后再通过地址找到堆中的值.因此, 这种我们也把它叫为引用访问.
栈数据结构
特点:后进先出(LIFO)的结构
这里所说的进栈和出栈不是指赋值算进, 使用算出. 而是指赋值算进, 被清理算出, 而且位于同一函数作用域下的变量, 应该是在栈的同一层.
所谓的变量存储于栈内存中的栈,传统意义上说指的是由内存自动创建分配的空间,例如函数的参数值与局部变量,只是其操作方式类似于栈操作,所以叫栈内存。
比如函数调用其实就相当于栈的形式:
scss
function fn1() {
console.log(1)
fn2()
}
function fn2() {
console.log(2)
fn3()
}
function fn3() {
console.log(3)
}
fn1()
如上, 声明的顺序是1, 2, 3 , 但是释放的顺序是为3, 2, 1 .
这里释放按照这个顺序是因为 3最先执行完, 所以最先被释放.
堆数据结构
一种树状结构。好比 JSON 格式中的数据,你有 key,我有对应的 value, 就立马返给你。
因为我们知道JSON格式的存储是无序的, 所以没有先后顺序, 所以它是一种绝对公平的数据结构.
队列数据结构
队列数据结构不同于堆, 队列是一种先进先出(FIFO)的数据结构.它也是事件循环(Event Loop)的基础结构。最先进入队列的任务最先出来, 类似于你排队买票, 排在前面的人先买.
内存管理
内存空间也是有属于自己的生命周期, 它主要分为三个阶段:
- 分配你所需的内存;
- 使用分配到的内存(读、写);
- 不需要的时候将其释放、归还.
JS有自动垃圾收集机制, 听着这个机制的名字我想大家就知道它是做什么的了, 没错就是字面意思, 它会找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。
在自动垃圾收集机制中, 最常用的就是通过标记清除的算法来找到哪些对象不再继续使用.
此外 在局部作用域中, 当函数执行完毕了之后, 局部变量就没有存在下去的必要了, 此时垃圾收集器知道这类变量是需要回收的, 所以很容易判断.但是全局变量什么时候需要释放内存空间则很难判断,因此在我们的开发中,原则上应该避免使用全局变量。
局部变量和局部变量如何销毁的
局部变量的销毁
全局变量的销毁
JavaScript 引擎
JavaScript 引擎将 JavaScript 编译成本地机器代码并执行。每个主要的浏览器都开发了自己的 JS 引擎。谷歌的 Chrome 使用 V8,Safari 使用 JavaScriptCore,Firefox 使用 SpiderMonkey。
垃圾回收
垃圾回收的思想很简单,有些数据被使用之后可能不再需要了,这就产生了垃圾数据,我们需要回收这些垃圾数据,释放内存空间。
通常情况下,垃圾回收策略分为
- 手动回收
- 自动回收
内存泄露
垃圾回收器的回收范围一般小于垃圾的范围。
因为有些东西,垃圾回收器不知道我们需要不需要
内存泄露是指一块内存空间不再被使用,但是没有被释放。在JavaScript中,内存泄露通常是因为一些对象被长时间引用,导致垃圾回收器无法回收这些对象。 可以通过chrome的Performance工具来检测内存泄露,查看内存的变化情况,找出内存泄露的原因。
内存泄露的本质
不可达的对象未释放:程序中存在一些不再使用的对象,但由于被引用(或误引用),垃圾回收器(GC)无法识别它们为"可回收对象",从而导致内存无法释放。(判断这个东西要不要用,用就保留,不用要清除或释放比如某个变量置空)
资源得不到回收:这些资源可能包括未清理的事件监听器、定时器、闭包中未释放的变量等。
清除策略:标记清除算法
常见的内存泄露
- 意外的全局变量
- 未声明的变量
- 使用this创建的变量
- 被遗忘的计时器或回调函数
- 闭包
内存泄露的识别方法
Chrome 浏览器查看内存占用,按照以下步骤操作。
- 在网页上右键, 点击"检查"打开控制台(Mac快捷键option+command+i);
- 选择Performance面板(很多教材中用的是Timeline面板, 不知道是不是版本的原因);
- 勾选Memory, 然后点击左上角的小黑点Record开始录制;
- 点击弹窗中的Stop结束录制, 面板上就会显示这段时间的内存占用情况。
根据这条蓝线可以初步判断是否存在内存泄漏
:如果这条蓝线持续上升,那很可能存在内存泄漏
。 然而,实际上这种说法有些偏颇。JS堆内存占用率的增长并不一定意味着内存泄漏
,只能表明有许多未释放的内存存在。要确定这些内存是否真正被使用,或者确实存在内存泄漏
,还需要进一步的排查和分析。
对比两次内存快照,查看内存中的对象和数据是否有明显的增加或者未释放的情况。特别要注意查看是否有多余的对象被引用、未释放的事件监听器、定时器等情况。
根据对比结果,定位可能存在内存泄漏的代码段,并进行优化和改进。