b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

大事件:全局变量,事件绑定,缓存爆炸? Node.js内存泄漏分析

电脑杂谈  发布时间:2020-09-02 03:04:14  来源:网络整理

js内存泄漏怎么解决_win7内存泄漏怎么解决_js内存泄漏及解决方法

Node.js使用V8引擎并将自动执行垃圾回收(Garbage Collection,GC),因此在编写代码时,您无需像C / C ++那样手动分配和释放内存空间,这更加方便,但仍然需要注意内存的使用,以免引起内存泄漏(内存泄漏).

内存泄漏通常非常隐蔽. 例如,您可以在以下代码中看到问题所在吗?

let theThing = null;
let replaceThing = function({
  const newThing = theThing;
  const unused = function({
    if (newThing) console.log("hi");
  };
  // 不断修改引用
  theThing = {
    longStr: new Array(1e8).join("*"),
    someMethod: function() {
      console.log("a");
    },
  };

  // 每次输出的值会越来越大
  console.log(process.memoryUsage().heapUsed);
};

setInterval(replaceThing, 100);

如果可以的话,欢迎加入我们的微信支付海外团队,共同追求卓越. 如果您现在看不到它,让我们一起阅读本文.

本文的前半部分将首先介绍一些理论知识,然后给出定位内存泄漏的示例. 有兴趣的朋友可以直接看一下这个例子.

总体结构

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

从上图可以看到,Node.js的常驻集分为两部分: 堆和栈,具体来说:

堆栈: 用于存储原始数据类型,此处还记录了函数调用的堆栈和弹出.

堆栈的空间由操作系统管理,开发人员不需要太在意;堆的空间由V8引擎管理. 可能由于代码问题而导致内存泄漏,或者在很长一段时间后,垃圾回收会导致程序运行缓慢.

我们可以通过以下代码简单地观察Node.js的内存使用情况:

const format = function (bytes{
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
};

const memoryUsage = process.memoryUsage();

console.log(JSON.stringify({
    rss: format(memoryUsage.rss), // 常驻内存
    heapTotal: format(memoryUsage.heapTotal), // 总的堆空间
    heapUsed: format(memoryUsage.heapUsed), // 已使用的堆空间
    external: format(memoryUsage.external), // C++ 对象相关的空间
}, null, 2));

外部是与C ++对象相关的空间. 例如,当通过新的ArrayBuffer(100000);申请一块缓冲存储器时,您可以清楚地看到外部空间的增加.

js内存泄漏怎么解决_win7内存泄漏怎么解决_js内存泄漏及解决方法

相关空间的默认大小可以通过以下参数(以MB为单位)进行调整:

更常用的是--max_new_space_size和--max-old-space-size.

有很多与新一代Scavenge回收算法和旧版Mark-Sweep和Mark-Compact算法有关的文章,因此在此不再赘述. 例如,本文讨论了Node.js内存管理和V8垃圾回收机制.

内存泄漏

由于代码不正确,有时不可避免地会发生内存泄漏. 有四种常见方案:

全局变量关闭参考事件绑定缓存爆炸

让我们通过示例进行讨论.

全局变量

未使用var / let / const声明的变量将直接绑定到Global对象(Node.js)或Windows对象(在浏览器中). 即使不再使用它们,也不会自动回收它们: <

function test({
  x = new Array(100000);
}

test();
console.log(x);

此代码的输出为[],您可以看到在完成测试功能后,尚未释放数组x.

关闭参考

关闭引起的内存泄漏通常非常隐蔽. 例如,您可以在以下代码中看到问题所在吗?

win7内存泄漏怎么解决_js内存泄漏及解决方法_js内存泄漏怎么解决

let theThing = null;
let replaceThing = function({
  const newThing = theThing;
  const unused = function({
    if (newThing) console.log("hi");
  };
  // 不断修改引用
  theThing = {
    longStr: new Array(1e8).join("*"),
    someMethod: function() {
      console.log("a");
    },
  };

  // 每次输出的值会越来越大
  console.log(process.memoryUsage().heapUsed);
};

setInterval(replaceThing, 100);

运行此代码,您可以看到输出使用的堆内存越来越大,关键是在当前V8实现中,闭包对象由当前范围内的所有内部函数范围共享,这意味着Thing.someMethod和unUsed共享相同的关闭上下文,这导致Thing.someMethod隐式持有对先前newThing的引用,因此Thing-> someMethod-> newThing-> last theThing-> ...导致执行of longStr: 每次执行replaceThing函数时都会使用new Array(1e8).join(“ *”),并且不会自动回收它,从而导致占用更多的内存. 越大,内存最终泄漏.

上述问题有一个非常聪明的解决方案: 通过引入新的块级范围,newThing的声明和使用与外部隔离,从而破坏了共享并防止了循环引用.

let theThing = null;
let replaceThing = function({
  {
    const newThing = theThing;
    const unused = function({
      if (newThing) console.log("hi");
    };
  }
  // 不断修改引用
  theThing = {
    longStr: new Array(1e8).join("*"),
    someMethod: function() {
      console.log("a");
    },
  };

  console.log(process.memoryUsage().heapUsed);
};

setInterval(replaceThing, 100);

这里,{...}形成了单独的块级作用域,并且没有外部引用,因此newThing将在GC期间自动回收. 例如,在我的计算机上运行此代码时,输​​出如下:

2097128
2450104
2454240
...
2661080
2665200
2086736 // 此时进行垃圾回收释放了内存
2093240

事件绑定

事件绑定导致的内存泄漏在浏览器中非常常见. 它们通常是由于未及时删除事件响应函数,导致重复绑定或删除DOM元素后未处理事件响应函数这一事实引起的,例如以下React代码:

class Test extends React.Component {
componentDidMount() {
window.addEventListener('resize', function() {
// 相关操作
});
}

render() {
return
test component
;

}
}

该组件在安装时侦听了resize事件,但是在删除该组件时未处理相应的功能. 如果安装和拆卸非常频繁,则许多无用的事件监视功能将绑定到窗口,最终导致内存泄漏. 可以通过以下方式避免此问题:

class Test extends React.Component {
componentDidMount() {
window.addEventListener('resize', this.handleResize);
}

handleResize() { ... }

componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}

render() {
return <div>test componentdiv>;
}
}

缓存爆炸

Object / Map的内存缓存可以大大提高程序的性能,但是很有可能缓存的大小和过期时间没有得到很好的控制,并且无效数据仍被缓存在内存中,导致在内存泄漏中:

js内存泄漏及解决方法_js内存泄漏怎么解决_win7内存泄漏怎么解决

const cache = {};

function setCache({
  cache[Date.now()] = new Array(1000);
}

setInterval(setCache, 100);

在上面的代码中,缓存是连续设置的,但是没有代码可以释放缓存,最终导致内存突发.

如果确实需要内存缓存,则强烈建议使用npm软件包lru-cache,它可以设置缓存有效期和最大缓存空间,并通过LRU消除算法避免缓存爆炸.

内存泄漏位置的实际操作

当发生内存泄漏时,通常很难定位,主要有两个原因:

程序开始运行时,问题不会立即暴露出来,并且需要一段时间的连续运行,甚至一两天,问题才会再次出现. 该错误消息非常模糊,通常只能看到堆内存不足错误消息.

在这种情况下,可以使用两种工具来确定问题: Chrome DevTools和heapdump. heapdump的功能就像它的名字一样-生成并导出内存中堆的状态信息,然后将其导入Chrome DevTools中以查看特定的详细信息,例如堆中有哪些对象以及有多少空间它占有更多.

接下来,让我们使用上面的闭包参考中的内存泄漏示例进行实际操作. 首先,npm install heapdump安装后,将代码修改为如下所示:

// 一段存在内存泄漏问题的示例代码
const heapdump = require('heapdump');

heapdump.writeSnapshot('init.heapsnapshot'); // 记录初始内存的堆快照

let i = 0; // 记录调用次数
let theThing = null;
let replaceThing = function() {
const newThing = theThing;
let unused = function() {
if (newThing) console.log("hi");
};

// 不断修改引用
theThing = {
longStr: new Array(1e8).join("*"),
someMethod: function() {
console.log("a");
},
};

if (++i >= 1000) {
heapdump.writeSnapshot('leak.heapsnapshot'); // 记录运行一段时间后内存的堆快照
process.exit(0);
}
};

setInterval(replaceThing, 100);

在第3行和第22行中,将导出初始状态的快照和1000个周期后的快照,并将其另存为init.heapsnapshot和Leak.heapsnapshot.

然后打开Chrome浏览器,按F12调出DevTools面板,单击“内存”选项卡,最后使用“加载”按钮依次导入两个快照:

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

标记

js内存泄漏及解决方法_js内存泄漏怎么解决_win7内存泄漏怎么解决

导入后,您可以看到左侧的堆内存显着增加,从1.7 MB增加到3.1 MB,几乎翻了一番:

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

下一步是最关键的步骤. 单击泄漏快照并将其与init快照进行比较:

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

在右侧的红色框中圈出两列:

您会看到增长最快的前两个项目是串联的字符串和结束符,因此让我们单击以查看哪些是:

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

从这两张图可以直观地看出,这主要是由Thing.someMethod函数的关闭上下文和Thing.longStr的很长的拼接字符串引起的内存泄漏. 问题基本上在这里. 现在,我们还可以单击下面的“对象”模块以更清楚地查看调用链的关系:

全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

从图中可以明显看出,内存泄漏的原因是由于newTHing

参考文章

正确可视化V8 EngineGithub内存泄漏示例中的内存管理ali节点打开的Chrome devtools


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-312049-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...