maodouchen / note

学习笔记
0 stars 0 forks source link

Node-内存控制1 #21

Open maodouchen opened 5 years ago

maodouchen commented 5 years ago

V8的内存限制

在一般的后端语言开发中,基本的内存使用上没有限制,但是在node中通过js使用内存时,就会发现只能使用部分内存。64位操作系统下1.4gb 32位系统下是0.7gb。 原因是Node基于V8构建,V8是通过自己的方式管理分配内存。在浏览器端足以胜任所有需求,但在服务端就有所限制,比如不能讲一个2G的文件读入内存中。

V8的对象分配

V8中,js对象都是通过堆来进行分配的,执行以下代码,可以得到内存信息: image

当我们在代码中声明变量并且赋值时,所使用对象的内存就被分配在堆中。 var b = {a: 1} 当我们给b赋值时,b这个对象的内存被分配在堆中。当堆的大小不能超过V8的限制。 为什么会有这个限制呢? 主要是因为V8的垃圾回收机制,他会引起js的线程暂停执行,且做一次小的垃圾回收需要50毫秒以上,做一次非增量式的垃圾回收甚至要1s以上。在这样的花销下,在浏览器侧,用户体验是非常不好的 V8最初是为浏览器设计的,这样的内存限制,已经绰绰有余,但是在node中怎么办呢? 当你想要读取一个2G的文件?
Node在启动时可以设置这个内存限制大小,如: node --max-old-space-size=1700 test.js // 老生代内存空间最大值 node --max-new-space-size=1700 test.js // 新生代内存空间最大值 一旦生效就不能动态改变。

V8垃圾回收机制

分代式垃圾回收机制: 在V8的堆内存中,分成新生代和老生代,新生代内存中放存活时间短的对象,老生代内存中放存活时间长的对象。

image

image

新生代和老生代采用不同方式的垃圾回收: 新生代垃圾回收算法:Scavenge算法 老生代垃圾回收算法:Mark-Sweep和Mark-Compact

Scavenge

这个算法将内存平均分成两份一份叫from,一份叫to, 只有from处于使用中,to处于闲置状态。我们给对象分配内存时,在from空间进行分配,to空间处于闲置状态。当进行垃圾回收时,检测from空间中存活的对象,将这些存活的对象复制到to空间中,from和to空间的角色发生对换,原来from空间将会被释放。 这个算法的缺点是只能使用堆内存的一般,这是由划分空间和复制机制所决定的。他是牺牲空间换时间的算法。他非常适合新生代的垃圾回收,新生代的空间中的大多数对象寿命短,需要清除的比较多,只有很少部分的对象需要复制到to空间中。 image

Mark-Sweep(标记-清理)

老生代的对象中,存活对象的比重大,如果用scavenge算法,复制存活对象的效率会降低。 Mark-Sweep是标记清除的意思,先标记活着的对象,在清除没有被标记的对象。

image

白色的是被标记为活着的 的对象,黑色的是被标记为死亡对象,问题: 在清除死亡对象后,内存空间会出现不连续的状态。这种内存碎片会对后续的内存分配造成问题,当出现需要分配一个大对象的情况时,这时所有碎片的内存空间都无法完成此次分配,就会提前出发垃圾回收,而这次回收是没有必要的。

为了解决上述问题,Mark-Compact被提出来;

Mark-Compact(标记-紧凑)

就是将或者的对象往一端移动,移动后,直接清理掉边界外的内存。 image

什么情况下会使用Mark-Compact算法呢? 当内存碎片无法满足情况时,会调用compact算法

image

incremental Marking(增量标记)

为了避免js的应用逻辑和垃圾回收器看到的不一致的情况,所以当进行垃圾回收时,js的应用逻辑需要完全停下来全停顿。当新生代的内存比较小,垃圾回收耗费的时间也端,即便是全停顿也没关系。但是老生代做垃圾回收时全停顿就会比较可怕,非常影响体验。所以将原本要一口气停顿完成的垃圾回收改成,拆分成许多小步骤,每走完一步就让js的应用逻辑执行一小会,垃圾回收与应用逻辑交替完成直到标记阶段完成,V8也引进了延迟清理和增量式清理,标记和清理的动作全部都是增量式的一步步的完成。

查看垃圾回收与性能日志

垃圾回收:node --trace_gc test.js 性能: node --prof test.js 会生成一个日志,这个日志很难读懂,可以使用tic-process v8.log 第三方工具来帮助读懂日志

内存分类

image

rss是常住内存,进程的内存分为几种,rss(常驻内存),其余部分在交换区和文件系统中。这几种内存有啥区别??? image

buffer的对象不同于其他对象,他不受限于V8的内存分配机制,所以也不会有堆内存的大小限制。这意味着可以利用堆外内存突破内存限制问题。

内存泄漏排查与避免

避免:

排查: heapdump: image

node index.js while true; do curl "http://127.0.0.1:1337/"; done 此时会发生内存泄漏 运行kill -USR2 pid 这个命令会生成一个内存快照在项目目录中,可以将这个内存快照放在chrome浏览器中分析 image

memwatch image

他可以监听到内存泄漏;当监听到内存泄漏时,拍快照进行分析; 那么他是如何监听到内存泄漏的呢? 连续5次垃圾回收后,内存依然没有被释放,这意味着内存泄漏的产生,memwatch 会发出一个leak事件。 每次进行全堆垃圾回收时,会触发一次stats事件,这个事件会传递内存的统计信息。