堆栈浏览器内存

堆栈内存

  • 什么是堆栈内存?

    • 浏览器在执行程序时形成两个虚拟的内存,用来存储数据,和提供代码的执行环境;
    • 浏览器每打开一个页面,都会开辟一个进程;一个进程包含了好几个线程;
    • JS 是单线程;同一个时间只能执行一行代码;
    • 堆栈内存: 用来存储数据;

栈内存分配

  • 基本类型的数据存储到栈内存(stack)中,函数作用域是消耗的栈内存;越小浏览器性能越高;

  • 引用数据类型存储到堆内存(heap)中;[对象、数组,函数],堆内存跟代码的执行环境没有关系;

  • 区别:

    • heap 堆内存数没有结构的;
    • stack 栈内存是有结构的,有次序的;
    let num = 100;
    num++;// 100
    let str = "hello";
    console.log(str);// 指针
    let  obj = {name:"zhufeng",age:10};
    // 对象的定义:
    // 1. 去堆内存开辟一个空间地址
    // 2. 把对象中的键值对放在堆内存中
    // 3. 把堆内存中的空间地址赋值给变量
    // 变量和对象的指针存储到了栈内存,当在栈内存中使用这个对象时,通过这个指针找到对应的堆内存地址;
    console.log(obj);// 指针
    
  • 内存生命周期

    • 内存的分配
    • 内存的读写
    • 不需要时将其释放(GC 垃圾回收机制)

堆内存回收

  • GC 垃圾回收器: GC 每隔一段时间执行一次;

  • 目前 IE 火狐 opera 谷歌大都采用标记清除的方式;

  • 垃圾回收方法

    • 标记清除
      1. 给每一个空间地址记上标记
      2. 筛选过滤环境的变量或者被引用的变量去除标记
      3. 有标记的就被视为被删除的变量
      4. 浏览器对有标记的进行回收,释放内存
    • 引用计数
      1. 对变量被引用一次就会在这个变量计数+1
      2. 如果不再引用,那么计数会默认-1;
      3. 当该变量计数为 0 时,浏览器要回收该块的内存;
      • 循环引用:创建两个对象并引用彼此,在函数调用之后,已经可以被释放,但引用计数算法认为,由于两个对象中的每一个至少被引用了一次,所以也不能被垃圾回收。
  • 内存泄漏的情况:

    1. 意外的全局变量,全局变量不会被回收
    • 可以通过严格模式"use strict"防止意外的全局变量
    //1.
    function foo(arg) {
      bar = "some text";
    }
    /* 等价于
    function foo(arg) {
        window.bar = "some text";
    }*/
    //2.
    function foo() {
      this.var1 = "potential accidental global";
    }
    foo();
    
    1. 闭包引发的内存泄漏
    • 闭包会造成对象引用的生命周期脱离当前函数的上下文,
    • 如果闭包如果使用不当,可以导致环形引用,类似于死锁,只能避免,无法发生之后解决,即使有垃圾回收也还是会内存泄露。
    function bar() {
      let a = 10; // 会放到当前函数执行的环境中;
      return function() {
        console.log(a); // 10
      };
    }
    let b = bar();
    b(); //不会被回收
    b = null; //释放,会被回收
    
    1. 对象中存在 DOM 的引用
    //1.没有清理的DOM元素引用
     var refA = document.getElementById('refA');
     document.body.removeChild(refA);
     //解决
     refA = null;
     //2.给DOM对象添加的属性是一个对象的引用
     var MyObject = {};
     document.getElementById('myDiv').myProp = MyObject;
     //解决
     window.onunload(){
         document.getElementById('myDiv').myProp = null;
     }
     //3.DOM对象与JS对象相互引用
     function Encapsulator(element) {
      this.elementReference = element;
      element.myProp = this;
      }
      new Encapsulator(document.getElementById('myDiv'));
      //解决
      window.onunload(){
          document.getElementById('myDiv').myProp = null;
      }
      //4.给DOM对象用attachEvent绑定事件
      function doClick() {}
      //解决
      element.attachEvent("onclick", doClick);
    
    1. 被遗忘的定时器和回调函数
    • 在 setInterval 没结束前回调函数变量及本身都无法被回收。如果回调函数内没有做什么事情,并且也没有被 clear 掉的话,就会造成内存泄漏,回调函数内依赖的变量也没法被回收。

    • 当不需要 interval 或者 timeout 时,最好调用 clearInterval 或者 clearTimeout

      let someResource = getData();
      setInterval(() => {
      const node = document.getElementById('Node');
      if(node) {
          node.innerHTML = JSON.stringify(someResource));
      }
      }, 1000);
      
    1. 循环引用
    function func() {
      let obj1 = {};
      let obj2 = {};
    
      obj1.a = obj2; // obj1 引用 obj2
      obj2.a = obj1; // obj2 引用 obj1
    }
    obj1 = null; //释放
    obj2 = null; //释放
    

函数编译 VOAO

  • 函数的执行 VOAO 过程

    • 函数的定义: 1.开辟一个堆内存,对应一个引用的空间地址
      2. 把函数体中的代码当做字符串存储到堆内存中
      3. 把函数体的空间地址赋值给函数名;

    • 函数调用执行,会引发 VOAO

    • 函数的 VO

      1. 形成一个上下文的执行环境(栈内存)
      2. 初始化作用域链
      3. 创建变量函数
      4. 初始化 arguments 对象和参数并赋值
      5. 对该上下文中的函数进行声明并且赋值
      6. 如果函数名已经出现,那么会把第一次初始的变量进行覆盖
      7. 对该上下文的变量进行声明,初始化值是 undefined;
      8. 如果变量名重复,直接跳过;
      9. 确定 this 的指向;(注意:先确定 this 指向再调用的)
      • 下面代码执行就是 AO:
      1. 代码从上到下开始执行
      // 函数执行AO
      
      function bar(a) {
        // console.log(arguments);
        fn();
        function fn() {
          console.log(100);
        }
        console.log(fn);
        function fn() {
          console.log(200);
        }
        var num = 100;
        var num = 1;
      }
      bar(1, 2, 3);
      console.dir(bar);
      
      function fn(num) {
        console.log(num);
        function num() {}
      }
      fn(1000);
      

函数的作用域链

  • 当获取变量对应的值时,首先先看自己的作用域有没有,如果没有会向上一级查找,上一级也没有,会继续向上查找,直到找到 window 为止,如果 window 也没有,那就会报错;这样一级一级形成就是作用域链;
let total = 100;
function fn() {
  // let total = 10;
  return function() {
    return function() {
      return function fn() {
        console.log(total);
      };
    };
  };
}
let f = fn();

f(); // 在全局作用域下执行的;f的上一级作用域是谁;
console.dir(f);
// 函数的上一级作用域跟函数在哪定义的有关,在哪定义那么上一级作用域就是谁;
// f 在fn执行的作用域中开辟的堆内存;f的上一级作用域就是fn执行形成的作用域

栈内存回收

// 函数执行会形成栈内存(执行上下文)
// 函数执行完成之后,会立即回收;栈内存中存储的值都会回收掉;
/*function fn(){
    let num =100;
  }
  fn();*/
// 这个栈内存是不回收;当关闭页面或浏览器时,回收站内存;
// 当函数体中返回一个引用的数据类型值时,并且这个引用的地址被外界占用,该栈内存不回收;
// 闭包: 函数执行中返回一个函数,里面的函数可以访问外界这个函数执行时存储的变量;
function fn() {
  let t = 10;
  return { name: 1 };
}
fn();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值