1.V8内存管理 #

1.1 栈 #

1.2 堆 #

new_large_object_space

const v8 = require('v8');
const heapSpace = v8.getHeapSpaceStatistics();
function format(size) {
    return `${(size / 1024 / 1024).toFixed(2)}M`.padEnd(10, ' ');
}
console.log(`${"空间名称".padEnd(20, ' ')} 空间大小 已用空间大小 可用空间大小 物理空间大小`);
for (let i = 0; i < heapSpace.length; i++) {
    const space = heapSpace[i];
    console.log(`${space.space_name.padEnd(23, ' ')}`,
        `${format(space.space_size)}`,
        `${format(space.space_used_size)}`,
        `${format(space.space_available_size)}`,
        `${format(space.physical_space_size)}`);
}

1.2.1 堆空间分类 #

1.2.1.1 新生代(new space) #
1.2.1.2 老生代(old space) #
1.2.1.3 Code space #
1.2.1.4 Large object space #
1.2.1.5 Map space #

1.2.2 什么是垃圾 #

global.a = { name: 'a' };
global.a.b = { name: 'b1' };
global.a.b = { name: 'b2' };

1.2.3 新生代的垃圾回收 #

global.a={};
global.b = {e:{}}
global.c = {f: {},g:{h:{}}} 
global.d = {};
global.d=null;

广度优先遍历过程

bool Heap::ShouldBePromoted(Address old_address) {
  Page* page = Page::FromAddress(old_address);
  Address age_mark = new_space_->age_mark();
  return page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK) &&
         (!page->ContainsLimit(age_mark) || old_address < age_mark);
}

1.2.4 老生代的垃圾回收 #

1.2.4.1 Mark-Sweep(标记清除) #

深度优先遍历过程

1.2.4.2 Mark-Compact(标记整理) #

深度优先遍历过程

1.2.5 优化 #

JavaScript执行 垃圾标记、垃圾清理、垃圾整理 JavaScript执行
--------------                          ---------------->
1.2.5.1 Parallel(并行执行) #
         -------辅助线程----->
         -------辅助线程----->
         -------辅助线程----->
---------                    --------------------------->
1.2.5.2 增量标记 #

增量标记

---------开始标记---增量标记---增量标记---清理---整理----------------->
1.2.5.3 Write-barrier(写屏障) #
global.a = { name: 'a' };
global.a.b = { name: 'b1' };
//执行标记工作
global.a.b = { name: 'b2' };
//继续执行标记工作

Write-barrier(写屏障)

1.2.5.4 Lazy Sweeping(惰性清理) #
1.2.5.5 concurrent(并发回收) #
         ----辅助线程标记---->      -----清理整理---->
         ----辅助线程标记---->      -----清理整理---->
         ----辅助线程标记---->      -----清理整理---->
-----------------------------执行JS>-----清理整理--->--------------------------->
1.2.5.6 并发(concurrent)和并行(parallel) #

2.内存泄露 #

2.1 什么是内存泄露 #

2.2 不合理的闭包 #

function Person(){}
function fn(){
    let name = 'zhufeng';
    let age = 13;
    let arr = new Array();
    for(let i=0;i<10000;i++){
        arr.push(new Person());
    }
    return function(){
        console.log('hello',arr,name);
    }
}
let hello = fn();
debugger
hello();

2.3 隐式全局变量 #

function Person(){}
function fn(){
    p1 = new Person();
    this.p2 = new Person();
}
fn();
p1=null;
p2=null;

2.4 分离的DOM #

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="container">
        <p id="title"></p>
    </div>
    <script>
       var container = document.getElementById('container');
       var title = document.getElementById('title');
       document.body.removeChild(container);
       //Detached  HTMLParagraphElement
       container=null;
       title=null;
    </script>
</body>
</html>

2.5 定时器 #

<script>
 function Person(){}
 function fn(){
    var p1 = new Person();
    var id = setInterval(()=>{
        p1.age = 20;
    },1000);
    clearInterval(id);
 }
 fn();
</script>

2.6 事件监听器 #

var data = new Array(100000);
class App extends React.Component{
    componentDidMount(){
        document.addEventListener('click',this.handleClick);
    }
    handleClick=()=>{
        console.log('click',data);
    }
    componentWillUnmount(){
        document.removeEventListener('click',this.handleClick);
    }
}

2.7 Map、Set对象 #

function Person(){}
let obj = new Person();
let set = new Set([obj]);
let map = new Map([[obj,'zhufeng']]);
obj = null;
function Person(){}
let obj = new Person();
let set = new WeakSet([obj]);
let map = new WeakMap([[obj,'zhufeng']]);
obj = null;

2.8 console #

function Person(){}
function fn(){
    let name = 'zhufeng';
    let age = 13;
    let arr = new Array();
    for(let i=0;i<10000;i++){
        arr.push(new Person());
    }
    return function(){
        console.log('hello',arr);
    }
}
let hello = fn();
hello();

3.内存泄漏排查 #

3.2 定位内存泄漏 #

3.2.1 录制监控 #

3.2.2 内存快照 #

字段 含义
摘要 按构造函数进行分组,捕获对象和其使用内存的情况
对比 对比某个操作前后的内存快照区别
控制 查看堆的具体内容,可以用来查看对象结构
统计信息 统计视图

3.2.2.1 摘要 #

字段 含义
构造函数 显示所有的构造函数,点击每一个构造函数可以查看由该构造函数创建的所有对象
距离 显示通过最短的节点路径到根节点的距离,引用层级
浅层大小 显示对象所占内存,不包含内部引用的其他对象所占的内存
保留的大小 显示对象所占的总内存,包含内部引用的其他对象所占的内存

3.2.2.2 对比 #

字段 含义
新对象数 新建对象数
已删除项 回收对象数
增量 新建对象数减去回收的对象数

4.性能优化 #

4.1 少用全局变量 #

var a = 'a';
function one() {
    return function two() {
        return function three() {
            let b = a;
            for (let i = 0; i < 100000; i++) {
                console.log(b);
            }
        }
    }
}
one()()();

4.2 通过原型新增方法 #

var Person = function () {
    this.getName = function () {
        console.log('person');
    }
}
let p1 = new Person();
var Person = function () {

}
Person.prototype.getName = function () {
    console.log('person');
}
let p1 = new Person();

4.3 尽量创建对象一次搞定 #

d8 --allow-natives-syntax main.js 
let p1 = {name:'zhangsan',age:10}
let p2 = {age:10,name:'zhangsan'}

let p = {};
%DebugPrint(point);
p.name = 'wangwu';
%DebugPrint(point);
p.age = 10;
%DebugPrint(point);

4.4 尽量保持参数结构稳定 #

function read(obj){
 console.log(obj.toString());
}
for(let i=0;i<1000;i++){
    read(i)
}

function read(obj){
 console.log(obj.toString());
}
for(let i=0;i<1000;i++){
    read(i%2===0?i:''+i)
}

function read(obj){
 console.log(obj.toString());
}
function read2(obj){
 console.log(obj.toString());
}
for(let i=0;i<1000;i++){
    read(i%2===0?i:''+i)
    i%2===0?read(i):read2(''+i)
}