JavaScript内存管理与垃圾回收机制
字和字节
- 位(bit): 一个二进制数码0或1, 是计算机存储处理信息最基本和最小的数据单位.
- 字节(byte): 一个字节由8个位组成, 是计算机存储信息的基本单位, 也是计算机存储空间大小的最基本单位.
- 1TB = 2^10GB = 2^20MB = 2^30KB = 2^40B = 2^50b
- 字(word): 若干个字节组成字, 16个位为一个字, 32位为一个双字, 64位是两个双字, 它代表计算机处理指令或数据的二进制数位数, 是计算机进行数据存储和数据处理的运算单位.
- 字长: CPU内每个字可以包含的二进制的长度称为字长(word size), 如32位(四个字节)表示CPU一次能处理四个字节.
内存
内存是一种用半导体技术做成的电子设备, 用于存储数据, 电子电路的数据是以二进制的方式存储, 存储器的每一个存储单元称为记忆元. 内存分为易失性的和非易失性的存储器, 但是一般我们说的内存指的是易失性的, 而非易失性的存储器我们称为磁盘. 内存主要是进程,文件加载,系统运行等高速缓存的临时运行存储空间. 内存速度快, 但是存储空间小, 而其中的缓存部分速度更加快. 每个进程在运行的时候都需要被分配内存.
内存的分配主要有静态分配和动态分配两种. 静态分配需要在编译阶段知道内存可用空间的大小, 是在编译时分配, 从栈空间中分配, 以后进先出的顺序移除这些调用. 动态分配不用知道内存可用空间的大小, 在运行时动态分配,从堆空间中分配, 没有特定的执行顺序.
JavaScript中的内存分配以及垃圾回收的缺陷
JavasSript在其变量创建的时候分配内存, 然后他们不再使用的时候'自动'释放, 这被称为垃圾回收. 但是这个过程是一个近似的过程, 大部分垃圾回收器会收集不再被访问的内存, 例如指向它的所有变量都在作用域之外, 但是在任何时候, 一个内存地址可能还有一个在作用域里的变量指向它, 但是它将不会被再次访问.在了解垃圾回收算法前需要理解一个概念, 引用, 在内存管理的环境中, 一个对象如果有访问另一个对象的权限(隐式或显示), 叫一个对象引用另一个对象, 如一个对象对它的原型的引用(隐式)和对它属性的引用(显示).
现在主要有以下几种垃圾回收算法:
引用计数垃圾收集
此算法把'对象是否不再需要'简化为'对象有没有被其他对象引用到它', 如果没有引用指向该对象, 对象将被垃圾回收机制回收. 但是这种算法无法处理循环引用.
标记-清除算法
此算法把'对象是否不再需要'简化为'对象是否可以获得'. 这个算法设定一个根(root)对象(在js中是全局对象). 定期的, 垃圾回收器从根开始, 找到所有从根开始引用的对象, 然后找到这些对象引用的对象...这样垃圾回收器将找到所有可以获得的对象和所有不能获得的对象. 这个算法解决了循环引用的问题, 但是那些无法从根对象查询到的对象将被清除.
内存泄漏
内存泄漏就是一些不再被应用需要的内存由于某种原因, 没有被归还给操作系统或进入可用内存池. 这里需要指出,内存溢出是指程序在申请内存时, 没有足够的内存空间供其使用.
JavaScript中的内存泄漏
全局变量
function foo(arg) {
bar = "some text";
this.baz = "some text";
//用'use strict'可以阻止这种意外的全局变量
}
除了这些无意的全局变量, 明确创建的全局变量也有很多, 这些不能被回收除非被指定为null或者重新分配. 如果使用了全局变量存储大量的数据, 确保在使用完成之后为其赋值null或者重新赋其他值.
被遗忘的定时器或者回调
例如setInterval
和addEventListener
等, 在使用完成后最好能显示的remove掉.
闭包
DOM外引用
有的时候在数据结构里存储DOM节点是非常有用的,比如你想要快速更新一个表格几行的内容。此时存储每一行的DOM节点的引用在一个字典或者数组里是有意义的。此时一个DOM节点有两个引用:一个在dom树中,另外一个在字典中。如果在未来的某个时候你想要去移除这些排,你需要确保两个引用都不可到达。