1、前言
本来我是用js编程一道题,浏览器报错RangeError: Maximum call stack size exceeded
,百度了很多js内存、栈的知识,找了几十个页面的答案,只能说是不如人意。每个文章几乎相同,上来就吧啦吧啦解释一堆堆栈
的概念,然后就讲基本变量保存在栈内,引用类型保存在堆内,说的我迷迷糊糊。ok,堆栈
是基本的数据结构,我懂,内存分布讲的也挺有道理,但就是感觉不对劲。那些文章光是语言上的阐述,有图的也就画个图,让我有些许疑惑,比如函数调用的溢出情况,是怎么回事?谷歌了几篇文章,还是找到了我的答案。
每个浏览器对各自对js实现方式不一样,但大多大同小异。以Chrome为例子,其分内存分布形式为call stack
和memory heap
,栈和堆不了解的同学就自己百度,我不再赘述。
2、Call stack(调用栈)
调用栈的基本概念
call stack
调用栈在浏览器中的资源是有限的,是一开始就被分配好的,一般没有多大,几M乃至更小。
该栈可以保存
- 基本类型的变量(相同的变量的值,如
a=2,b=2
,它们是独立的,并不是共享一个2) - 引用类型指针(对象、函数)
- 函数调用情况(非定义的指针),即一个函数及其参数情况(称之为一个栈帧
call frame
),通俗来说就是执行函数的时候
这里着重讲栈帧,一个函数中调用其他函数(或者递归调用自己),会导致内部函数入栈形成一个函数帧,直到该函数内部执行完毕,才会出栈。
注意到,即使是return一个栈帧也不会让该函数释放(很多编程语言实现了tail call 尾调用优化,ES6规定了尾调优化,但仍然许多浏览器不见优化,Chrome也是如此)。
递归容易引起一系列的栈帧入栈,将会导致调用栈溢出,如这个例子
function test(flag){
retrun flag && test(--flag)
}
test(100000);//控制台报错:Uncaught RangeError: Maximum call stack size exceeded
3、Memory heap(内存堆)
这个,由需要定义的引用类型的大小决定,类似C语言的申请内存。
函数、对象等引用类型都是保存在此处,栈中保存的是指向内存堆中的指针。
这个没啥好讲的,不清楚是否有最大限制,貌似都是动态申请的,不然几十个动态页面那还得了?
4、内存优化
栈优化
- 避免持续入栈,方法五花八门,百度的避免递归堆栈内存溢出的方案,大多都是这样
- 尾调优化,慎用!原因之前讲了
堆优化
内存越来越大,电脑就会卡顿,不用的变量及时设置为null,让垃圾回收机制回收。