Node进阶 - 内存管理和垃圾回收

  Node内存管理和垃圾回收

  

  node.js 是基于 V8 引擎的 javascript 运行环境。

  V8 引擎

  javascript 代码运行的时候提供编译优化、内存管理、垃圾回收等功能

  代码编译优化:

    1、通过 parser 将 javascript 源码转为 ast 抽象语法树

    2、将 ast 抽象语法树转为字节码,

    3、然后转为机器可运行的汇编代码

  内存管理:管理内存分配、内存划分

  垃圾回收:通过垃圾回收机制对无用代码释放内存

 

 

  内存管理

 

  类型

  常驻内存:运行 node 进程时候的所有内存( 代码占用内存、栈内存、堆内存、堆外内存 )

  栈内存:用于存放变量( javascript 中基本类型 )

  堆内存:用于存放对象、闭包引用上下文( 引用类型 )

  堆外内存:不是通过 V8 分配,不受V8管理,不占用V8内存。一些用于存放 buffer 数据的 ( buffer 对象依然在堆内存中 )

  

  限制

  默认给堆内存分配的大小:64位系统为 1.4G、32位系统为 0.7G

  可以通过 node 启动命令修改内存大小。

  不过分配的内存越大,垃圾回收一次的时间越长

 

  内存信息获取

  node 提供了 process.memoryUsage() 获取当前进程的内存信息

  rss :常驻内存大小( 进程分配的内存 )

  heapTotal:给 V8 分配的内存大小

  heapUsed:已使用的堆内存大小

  external:堆外使用的内存( buffer )

 

  1、内存溢出和堆外内存

const fs = require('fs')

const format = function (item, type = 0) {
    let ext = 'byte'
    switch (type) {
        case 1:
            ext = 'KB'
            break;
        case 2:
            ext = 'M'
            break;
        case 3:
            ext = 'G'
            break;
        case 3:
            ext = 'T'
            break;
    }
    if (item < 1024) return item.toFixed(2) + ext
    return format(item / 1024, type + 1)
}

function showMemory() {
    const memoryUsage = process.memoryUsage()
    console.log(`---------------------`)
    for (const item in memoryUsage) {
        console.log(`${item}:${format(memoryUsage[item])}`)
    }
}

showMemory()
// 使用 40M 堆外内存
const buffer = new ArrayBuffer(40 * 1024 * 1024)
const total = []
setInterval(() => {
    // 20M 内存的添加
    total.push(new Array(20 * 1024 * 1024))
    showMemory()
},2000)

  

 

  垃圾回收

 

  释放一些在应用程序中不在被引用,或者空指针的一些变量的内存。

  1、变量为空的时候释放内存栈

  2、对象结束没引用或者引用的对象被清除的时候释放对应内存( 对象无法被根节点访问时候 )

 

  堆内存中的分类

  新生代:内存比较小,未经历垃圾回收的对象存放位置

    from 区:回收检查( 算法回收 ) 的时候,检查区

    to 区:回收检查( 算法回收 ) 的时候,from检查到对象被引用存活了

  老生代:内存是新生代20倍,from 区收检查( 算法回收 ) 的时候,已经经历过的对象或者to区满了时候送到老生代

 

  

  新生代垃圾回收

  新生代垃圾回收是通过交换 from 区和 to 区来实现的

  1、新建的对象被存放在 from 区,满了之后执行新生代垃圾回收检查

  2、检查 from 区对象,如果已经经历过一次检查且依然被引用的对象送到老生代区

  3、检查 from 区对象,如果被引用则总到 to 区,to 区满了25%则送到老生代区

  4、结束 from 检查后,交换 from 区和 to 区身份( from 区变 to区,反之 )

  5、再次检查,回到 1。

 

  老生代垃圾回收

  老生代垃圾回收没有新生代频繁

  主要通过判断是否被引用进行标记清除,然后对存活的对象整理

  1、检查老生代区,是否被引用,未被引用,释放该内存块的内存

  2、将依旧存活的对象,整理到老生代区的一端

  3、清除存活一端以外部分的内存块

  

  堆内存里面的对象主要是靠引用计数来判断是否释放内存(每引用一次增加1)

 

 

  内存泄漏场景

  

  1、未释放无用的全局变量/对象

  2、闭包的上下文

function add() {
    const x = 1;
    const y = 2;
    return function (a) {
        return y + 2
    }
}
const add1 = add();
/*
* 执行 add() 后
* x 将会被释放
* y 将存在 add1 的上下文中,不会被释放( 除非 add1 = null )
* */

  

  3、缓存大量数据

// 分成小的一部分一部分处理
const array = new Array(20 * 1024 * 1024)

  

  

  内存分析工具

  利用内存检查工具( node-heapdump、node-profiler ) 生成内存快照

  利用 chrome 进行分析。

 

posted @ 2020-04-24 15:27  小结巴巴吧  阅读(439)  评论(0编辑  收藏  举报