开辟的堆内存或者形成的上下文(进栈执行=>栈内存)是越多越好,还是越少越好?

  • 肯定是越少越好,因为计算机的内存是固定的,我们所有开辟的内存都在占用计算机的资源,当计算机内消耗存过多,性能也就越来越卡(直接导致我们的产品运行变慢)

思维导图

35.JS中堆栈内存的释放问题 - 图1

函数执行就会形成栈内存(从内存中分配的一块空间),如果内存都不销毁释放,很容易就会导致栈内存溢出(内存爆满,电脑就卡死了), => 所以JS中一个重要的性能优化点:减少内存的使用

  • =>释放堆内存
  • =>释放栈内存(也就是让进栈执行的上下文,尽可能出栈释放)

一、堆内存(HEAP)和 栈内存(STACK)

1、堆内存(HEAP)

  • 堆内存是用来存储引用数据类型值的

    • (例如:创建函数和创建对象,就是开辟一个堆内存,把代码字符串或者键值对存储到堆内存中的)

2、栈内存(STACK)

  • 栈内存是用来执行代码和存储基本类型值的(创建的变量也存栈里面了),

    • 不仅全局代码执行(EC(G)全局执行上下文),
    • 而且函数执行(EC(X)私有上下文),最后也都会进栈执行的
    • 基于ES6中的let/const形成的块作用域也是栈内存

二、浏览器常用的垃圾回收机制(内存释放机制):

1、 查找引用方式(weblit内核)

浏览器有自动回收垃圾的机制,定期间隔某段时间,把所有没有被占用的内存回收释放(这种垃圾回收机制,比其它语言要完善一些)

-1).不会销毁的情况:

  • 创建一个堆(16进制地址):

    • 如果有变量或者其它东西存储了堆内存的地址,则当前堆内存被视为占用,也就不能释放销毁
  • 上下文进栈执行;

    • 如果当前上下文中的某些内容(一般也是当前上下文中创建的堆)被上下文以外的变量或者其它事务所占用,那么当前上下文就不能出栈释放(但是一般情况下,上下文中代码执行完,上下文自己就出栈释放了)

-2).堆内存释放

如果堆内存用完后,我们想去手动释放它,则取消所有的占用:赋值为NULL(NULL是空对象指针,也就是不指向任何的堆内存)

  1. //=>创建一个引用类型值,就会产生一个堆内存
  2. //如果当前创建的堆内存不被其它东西所占用了(浏览器会在空闲的时候,查找每一个内存的引用状况,不被占用的都会给回收释放掉),则会释放
  3. let obj = {
  4. name : 'xiaozhima'
  5. };
  6. let oop = obj;
  7. //此时obj和oop都占用着对象的堆内存,想要释放堆内存,需要手动解除变量和值的关联(null:空对象指针)
  8. obj = null;
  9. oop = null;
  10. 复制代码

-3).栈内存释放

栈内存:

  • 打开浏览器形成的全局作用域是栈内存
  • 手动执行函数形成的私有作用域是栈内存
  • 基于ES6中的let/const形成的块作用域也是栈内存
  • ……

栈内存销毁:

  • 全局栈内存:关掉页面的时候才会销毁

  • 私有栈内存:

    • 1.一般情况下,函数只要执行完成,形成的私有栈内存就会被销毁释放掉(排除出现无限极递归、出现死循环的模式)
    • 2.但是一旦栈内存中的某个东西(一般都是堆地址)被私有作用域以外的事物给占用了,则当前私有栈内存不能立即被释放销毁(特点:私有作用域中的私有变量等信息也保留下来了=>这种函数执行形成不能被释放的私有栈内存,也叫做闭包)
function fn(){
    //...
}
fn(); //=>函数执行形成栈内存,执行完成栈内存销毁

function X(){
    return function(){
        //...
    }
}
let f=X(); //=>f占用了X执行形成的栈内存中的一个东西(返回小函数对应的堆),则X执行形成的栈内存不能被释放了
复制代码

2、内存计数器方式(Trident内核)

当前内存被其它东西引用了,则给堆计数1(累加计数),取消占用后,则减1,当减到零之后,浏览器就可以把它释放了

三、练习题

1.输出结果

var i = 5;
function fn(i) {
    return function (n) {
        console.log(n + (++i));
    }
}
var f = fn(1);
f(2);
fn(3)(4);
fn(5)(6);
f(7);
console.log(i);
复制代码

35.JS中堆栈内存的释放问题 - 图2

2.输出结果

let x = 5;
function fn(x) {
    return function(y) {
        console.log(y + (++x));
    }
}
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);
复制代码

35.JS中堆栈内存的释放问题 - 图3

3.输出结果

let a=0,
    b=0;
function A(a){
    A=function(b){
        alert(a+b++);
    };
    alert(a++);
}
A(1);
A(2);
复制代码

35.JS中堆栈内存的释放问题 - 图4

4.输出结果

var n=0; 
function a(){
    var n=10; 
    function b(){
        n++; 
        console.log(n); 
    }
    b();
    return b; 
}
var c=a();
c(); 
console.log(n);
复制代码

35.JS中堆栈内存的释放问题 - 图5

5.输出结果

var test = (function(i){
    return function(){
        alert(i*=2);
    }
})(2);
test(5);
复制代码

35.JS中堆栈内存的释放问题 - 图6