一、小故事
炎热的夏天,走在大马路上,手里拿着雪糕(雪糕有包装盒),吃得津津有味,大马路的旁边有个垃圾桶,每天都有勤劳的环卫工叔叔阿姨来清理垃圾桶里面的垃圾。刚好今天吃雪糕是碰到环卫工阿姨正在清理垃圾:
1、在我没吃完雪糕前,雪糕包装盒对我来说还有价值,环卫工阿姨并不会觉得我手上的雪糕包装盒是垃圾,不会向我说赶紧把雪糕包装盒扔了,我好回收。
2、即使我吃完雪糕了,但是我正在看手机,就是不扔掉雪糕包装盒,环卫工阿姨也无法回收,即使她知道雪糕包装盒已经是垃圾了。
环卫工人 ==> 垃圾回收
我 ==> 运行的程序
雪糕 ==> 有价值的内存变量
雪糕包装盒 ==> 没有价值的内存变量
为了我能够更好的发展,我必须及时扔掉雪糕包装盒让环卫工人 回收,腾出手来做更加有意义的事。
为了程序更好的运行,没有价值的内存变量必须及时清理掉,腾出内存空间做别的事情。
二、内存回收机制
JavaScript具有自动的垃圾回收机制,能够回收没有价值的的内存变量。但是不规范的程序往往让没有价值的内存变量引用着,不释放,导致程序的执行效率低下。对应写程序的我们,所谓的内存管理就是要即使释放没有价值的内存变量,让垃圾回收机制回收,让程序更好的运行。因此,如何分辨哪些内存变量是没有价值了才是我们的重中之重。
三、内存基本概念
内存的生命周期:
1、分配所需的内存
2、内存的读与写
3、不需要时将其释放
所有语言的内存生命周期都基本一致,不同的是最后一步在低级语言中很清晰,但是在像JavaScript 等高级语言中,这一步是隐藏的、透明的。
js的内存生命周期:
1、定义变量时就完成了内存分配
2、使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。
3、而内存的释放而依赖GC机制(高级语言解释器嵌入的“垃圾回收器”)。
程序运行的时候,需要内存空间存放数据。一般来说,系统会划分出两种不同的内存空间:一种叫做栈(stack),另一种叫做堆(heap)。
四、堆(heap)与栈(stack)
heap是没有结构的,数据可以任意存放。heap用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象。
stack是有结构的,每个区块按照一定次序存放(后进先出),stack中主要存放一些基本类型的变量和对象的引用,存在栈中的数据大小与生存期必须是确定的。可以明确知道每个区块的大小,因此,stack的寻址速度要快于heap。
五、垃圾回收算法
引用计数算法(已被抛弃)
将引用视为一个对象访问另一个对象的路径。(这里的对象是一个宽泛的概念,泛指JS环境中的实体)。
引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需了。
标记清除算法
标记清除算法将“不再使用的对象”定义为“无法达到的对象”。简单来说,就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
如:栈内存中没有指向堆内存的寻址空间,那么堆内存的空间将被清除。
参考链接
1、【JavaScript 运行机制详解:再谈Event Loop】http://www.ruanyifeng.com/blog/2014/10/event-loop.html
2、【javascript内存管理(堆和栈)和javascript运行机制】https://www.cnblogs.com/web-easy/p/7889184.html