# 01-内存管理

# 内存的数据存储:堆和栈

  • 栈内存(Stack):存储普通类型的变量,如:Sting,Number,Boolean,null,undefined,Symbol
  • 堆内存(Heap):存储方法,数组,函数,对象,等引用类型,如:Array,Function,Object

栈是一种特殊的列表,栈内的元素只能通过列表的一端访问,这一端称为栈顶。 栈被称为是一种后入先出(LIFO,last-in-first-out)的数据结构。

堆是一种经过排序的树形数据结构,每个结点都有一个值。 通常我们所说的堆的数据结构,是指二叉堆。

引用数据类型存储在堆内存中,因为引用数据类型占据空间大、大小不固定。 如果存储在栈中,将会影响程序运行的性能; 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

# 栈内存和堆内存的优缺点

1.在JS中,基本数据类型变量大小固定,并且操作简单容易,所以把它们放入栈中存储。 引用类型变量大小不固定,所以把它们分配给堆中,让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。

2.栈内存由于它的特点,所以它的系统效率较高。 堆内存需要分配空间和地址,还要把地址存到栈中,所以效率低于栈。

# 栈内存和堆内存的垃圾回收

栈内存中变量一般在它的当前执行环境结束就会被销毁被垃圾回收制回收, 而堆内存中的变量则不会,因为不确定其他的地方是不是还有一些对它的引用。 堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。

# V8有多大

  1. 64位下1.4G
  2. 32位下0.7G
  3. 根据浏览器不同会有些许扩容。Node情况下会有一些C++内存扩容

# 新生代和老生代

v8的内存空间分为新生代和老生代:
新生代:新产生的对象
老生代:经历过新生代垃圾回收后还“存活”下来的对象

  • 64位下新生代的空间为32MB,老生代为1400MB
  • 32位下新生代的空间为16MB,老生代为700MB

# 新生代转化为老生代的条件

  1. 新生代发现本次复制后,会占用25%的to空间
  2. 这个对象已经经历过一次回收

# 什么时候触发回收

  1. 执行完一次代码
  2. 内存不够的时候

# 变量什么时候被回收

  • 全局变量不会被回收
  • 局部变量会被回收,也就是函数一旦运行完以后,函数内部的东西就会被销毁
  • 只要被另外一个作用域所引用就不会被回收
var i = 1;   // 全局变量不会被回收
var i = 2;   // 这里重复声明变量i,因此var声明被忽略,只是把i赋值为2
var add = function () {  // 全局变量不会被回收
  var i = 0;  // 局部变量
  return function () {
      i++;
      console.log(i); // 被另一个作用域引用导致不会被回收
  }
}();
add(); // 1
1
2
3
4
5
6
7
8
9
10

# 如何检查内存

# 浏览器端(以字节为单位)

window.performance.memory
console.log(window.performance.memory);
// 打印:
// {
//   jsHeapSizeLimit: 4294705152
//   totalJSHeapSize: 9957865
//   usedJSHeapSize: 7467161
// }
// 查看当前使用内存
function testMemory() {
    var usedJSHeapSize = window.performance.memory.usedJSHeapSize;
    console.log(usedJSHeapSize / 1024 / 1024 + 'mb');
    console.log(window.performance.memory);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • jsHeapSizeLimit该属性代表的含义是:上下文内可用堆的最大体积

  • totalJSHeapSize表示 已分配的堆体积

  • usedJSHeapSize表示当前 JS 堆活跃段(segment)的体积

# Node端(以字节为单位)

node.js中文网 (opens new window)

process.memoryUsage();

console.log(memoryUsage());
// 打印:
// {
//  rss: 4935680,
//  heapTotal: 1826816,
//  heapUsed: 650472,
//  external: 49879,
//  arrayBuffers: 9386
// }

// 查看当前使用内存
function testMemory() {
    var memory = process.memoryUsage().heapUsed;
    console.log(memory / 1024 /1024 + 'mb');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • rss,常驻集大小,是进程在主内存设备(即总分配内存的子集)中占用的空间量,包括所有 C++ 和 JavaScript 对象和代码。
  • heapTotalheapUsed 指的是 V8 的内存使用量。
  • external 指的是绑定到 V8 管理的 JavaScript 对象的 C++ 对象的内存使用量。
  • arrayBuffers 是指为 ArrayBuffer 和 SharedArrayBuffer 分配的内存,包括所有 Node.js Buffer。 这也包含在 external 值中。 当 Node.js 被用作嵌入式库时,此值可能为 0,因为在这种情况下可能不会跟踪 ArrayBuffer 的分配。

# 内存优化

  • 尽量不要定义全局,定义了及时手动释放
  • 注意闭包

# Node端的一些特殊点

  1. Node可以手动触发垃圾回收: global.gc
  2. Node端可以设置内存:
    • node --max-old-space-size=1700 test.js (单位为MB)
    • node --max-new-space-size=1024 test.js (单位为KB)