内存(js)

栈和堆

内存空间:栈存放原始数据类型,堆存放引用数据类型

栈是一种数据结构。限定在表尾进行插入和删除操作的线性表

在js中,栈存放的数据类型有Boolean, Number, String ,Undefined ,Null,对象变量的指针

有一种说法是栈存储的东西是不会变的东西,就像null是一个对象,为什么会被放在栈内存而不是堆内存,因为null是一个不可变的对象

特点:后进先出

看一下这段代码:

var a = 10
var b = a

编译原理:
var a,编译器先判断当前作用域是否存在a变量,如果有则忽略,否则在当前的作用域新声明一个变量a

a = 10,引擎在执行这个赋值的时候先去找a变量是否在作用域链上是否存在,如果存在,则进行赋值操作,将10赋值给a,否则抛出异常

以上操作在栈上表现为下图:

实际就是在标识符中添加了一个变量a,在栈中开辟了一片内存,存放了一个值是数字10,a标识符对应的值是10
var b = a

var b,编译器先判断当前作用域是否存在b变量,如果有则忽略,否则在当前的作用域新声明一个变量b
b = a,引擎在执行这个赋值的时候先去找b变量是否在作用域链上是否存在,如果存在,则进行赋值操作,复制a的值,将值给b,否则抛出异常,

根据栈的后入先出,确定出栈顺序

假如1234是入栈顺序,那么出栈顺序可能有哪些?

4个元素的全排列共有24种,栈要求符合后进先出,按此衡量排除后即得:

1234√ 1243√ 1324√ 1342√ 1423× 1432√

2134√ 2143√ 2314√ 2341√ 2413× 2431√

3124× 3142× 3214√ 3241√ 3412× 3421√

4123× 4132× 4213× 4231× 4312× 4321√

14种可能,10种不可能,如上所示。

因为入栈顺序是确定的 但是出栈时机却是不确定的 例如1234这种出栈顺序就是1入栈 1出栈 2入栈 2出栈…

是存储引用类型的地方。跟调用堆栈主要的区别在于,堆可以存储无序的数据,这些数据可以动态地增长,非常适合数组和对象。在Javascript中我们无法直接操作堆,我们在操作对象时,实际是在操作对象的引用。

var c={
    name:'Bob',
    age:18
}

如图


实际就是在栈中存了一个引用指针,指向堆中的这个内存

垃圾回收

1、引用计数法

记录每个值被引用的次数,当引用数为0时,表示这个值不再使用了,判定可以进行释放。

var o = { 
  a: {
    b:2
  }
}; 
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集


var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1;      // 现在,“这个对象”只有一个o2变量的引用了,“这个对象”的原始引用o已经没有

var oa = o2.a; // 引用“这个对象”的a属性
               // 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
           // 但是它的属性a的对象还在被oa引用,所以还不能回收

oa = null; // a属性的那个对象现在也是零引用了
           // 它可以被垃圾回收了

但是这个引用计数法容易造成内存泄漏,看下一个例子

var a = {
    name: 'a'
}
var b = {
    name: 'b',
    a: a
}
a.b = b

这个时候,a被b引用,b被a引用,所以这两个都不会计数等于0,所以都不会被清除

2、标记清除法(2012年起,所有浏览器均使用了此机制)

大概是这样,先把所有的变量都加上标记,当这个变量在作用域链中还能被访问,那么清除掉这个标记,当遍历完所有的变量后,如果还有这个标记的变量,那么进行回收。

主要依赖与计算环境

执行环境:定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之相关联的变量对象(全局对象/局部对象),环境中定义的所有变量和函数都保存在这个对象中。

当变量进入执行环境时,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

简单理解为:当每个变量或函数在作用域链中无法访问,那么就该收集了。

内存大小

每一个浏览器窗口都有一个V8引擎负责运行js代码,由于js初始设计是作为脚本语言,为浏览器设计,不太可能遇到大量内存的场景,所以在设计的时候约定好了它占用的内存大小

一般和电脑配置有关:

64位操作系统的,运行内存大小是1.4G
32位操作系统的,运行内存大小是0.7GB

关于内存:


网易云课堂是这么说的:
新生代内存占总内存的25%,老生代内存占总内存的75%

但是我查了一下,更多的说法是:

64新生代内存32M,老生代内存1400M

32新生代内存16M,老生代内存700M

其中新生代中又划分了From和To两个区域,各占新生代各一半的内存

大致我们一个变量从新生代到老生代的过程大概是:

我们运行程序在不停的往内存里面增加变量消耗内存,当我们新增一个变量的时候,会在新生代的From里面去开辟一个空间放这个东西,当你不停的创建变量,到From的内存达到一个内存临界值的时候(有说是内存耗完,我觉得应该是一个阈值,待验证),扫From空间进行垃圾回收,未被回收的变量做一个标记,并将From空间的未被回收的变量复制一份到To中去,清空From空间,并将To空间与From空间对调,也就是老的To空间此时变为From,此时的To空间是一个空白的空间,然后此时From空间就存在了上一次未被回收的变量对不对?并且有一个未被回收的标记,当From空间再次进行垃圾回收时,如果这些标记了未被回收的变量再次无法被回收时,这些变量会进入老生代内存空间

新生代和老生代是有差别的,新生代是牺牲内存换取时间效率。

你看新生代真正能用于存储的空间其实只有一半,不停在做From和To的垃圾回收和切换。而老生代的垃圾回收和内存机制要复杂的多。在内存中存储的数据是类似于一个有序的数组的格式,例如10001地址存放a,10002地址存放b,10003地址存放c,在新生代中,假如b被回收,那么实际就是10002会被空出来,但是你又没办法往10002里面放东西,所以产生了10002磁盘碎片,但是这样快啊,我啥都不用管我就把东西复制一遍丢给To空间,而在老生代中它会把c放到10002,释放出来10003,也就是它会帮你整理磁盘碎片,所以新生代的垃圾回收要比老生代的垃圾回收要快,但是新生代的内存利用率没有老生代的高

注意:在垃圾回收的时候是会暂停当前线程的执行的,具体暂停的时间和垃圾回收的大小有关,据说是300M进行垃圾回收耗时是0.2ms,在这我也想到一个问题就是定时器不准的原因,大概也是和垃圾回收有关,因为一段程序不停再跑,那么内存越大的情况下时间就越长
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值