偶然发现,当react在服务器上呈现时,当NODE_ENV!=生产时,它会导致内存泄漏。问题: https://github.com/facebook/react/issues/7406.随着节点和反应同构等技术的广泛应用,节点端内存泄漏等问题应引起重视。为什么节点容易出现内存泄漏,出现后如何排除,这里简单介绍一下,举个例子来说明。
首先,node基于v8引擎,其内存管理模式与v8一致。下面简单介绍v8相关的记忆效果。
V8内存限制
节点建立在V8之上,js对象由V8分配和管理。V8对内存的使用有限制(老一代64位系统约1.4G,32位系统约0.7G,新一代64位系统约32MB,32位系统约16MB)。在这样的限制下,大内存对象无法操作。如果不小心触碰到这个边界,就会导致进程退出。
原因:当垃圾收集完成后,V8会阻塞JavaScript应用逻辑,然后重新执行JavaScript应用逻辑,直到垃圾收集完成。这种行为被称为“停止世界”。如果V8的堆内存是1.5GB,那么小的垃圾收集需要50毫秒以上,非增量垃圾收集甚至需要1秒以上。
通过node-max-old-space-size=XXX(单位MB)和node-max-new-space-size=XXX(单位KB)设置新一代内存和老一代内存,打破默认内存限制。
V8的堆栈组成
事实上,V8堆不仅由老一代和新一代组成,还可以分为几个不同的领域:
新生代记忆区:大部分对象都分配在这里。这个区域很小,但垃圾经常被退回。老一代指针区:属于老一代,包含大部分可能有指向其他对象的指针的对象。新生代提升的大部分物体都会被移到这里。老一代数据区:属于老一代,这里只保存原始数据对象。这些对象没有指向其他对象的指针。大对象区域存储体积超过其他区域的对象。每个对象都有自己的内存。垃圾收集不会移动大对象的代码区域。这里将分配代码对象,即包含JIT后指令的对象。唯一有执行权限的内存区域:单元区、属性单元区、映射区:存储单元、属性单元、映射,每个区域存储相同大小的元素,结构简单,GC回收类型
增量垃圾收集
指示垃圾收集器在扫描内存空间时是否收集(增加)垃圾,并在扫描周期结束时清空垃圾。
非化学气相色谱
当使用非增量垃圾收集器时,垃圾一被收集就被清空。
垃圾收集器只回收新一代存储区、老一代指针区和老一代数据区的垃圾。对象首先进入占用空间较小的新一代内存。大多数对象会很快失败,非增量式垃圾回收会直接回收这些少量内存。如果有些对象在一段时间内无法回收,就去老一代内存区。在这一领域,增量垃圾收集很少执行,需要很长时间。
什么时候会发生内存泄漏?
内存泄漏的方式
内存泄漏缓存队列消耗不及时,作用域未释放的Node内存组成主要由V8分配,Node自身分配。V8的垃圾收集主要受限于V8的堆内存。内存泄漏的主要原因有:1。缓存;2.排队消费不及时;3.范围未发布
内存泄漏分析
检查V8的内存使用情况(字节)
process . memoryusage();{RESS : 47038464,堆总数: 34264656,堆已用: 2052866} RESS:进程的内存驻留部分
HeapTotal,heap pussed:V8堆内存信息
检查系统内存使用情况(以字节为单位)
os.totalmem()os.freemem()
返回总系统内存和空闲内存
查看垃圾收集日志
node-trace _ GC-e ' var a=[];for(var I=0;我1000000;i ) { a.push(新数组(100));}' gc.log //输出垃圾收集日志
node-prof//输出节点执行性能日志。使用windows-tick.processor查看。
分析和监测工具
V8-profiler捕获V8堆内存的快照并分析cpu。node-heapdump捕获v8堆内存的快照。node-mtrace分析堆栈使用node-memwatch来监视垃圾收集
节点-memwatch
Memwatch。on ('stats ',function (info) {console。log (info)}) memwatch。on ('leak ',function (info) {console。log (info)}) stats事件3360在每次执行整个堆垃圾收集时都会触发stats事件。此事件将传递内存统计信息。
{'num_full_gc': 17,//什么时候是全栈垃圾收集' num_inc_gc': 8,//什么时候是增量垃圾收集' heap_compactions': 8,//老一代什么时候整理出来' estimated _ base ' : 259255//估计基数' current_base': 2592568,//当前基数' min': 2499912,
泄漏事件:如果连续五次垃圾收集后内存仍未释放,则意味着内存泄漏。此时,会触发泄漏事件。
{ start: Fri,2012年6月29日14:12:13 GMT,end: Fri,2012年6月29日14:33 GMT,growth: 67984,Reason : '堆增长超过5个故意GCS (20s)-11.67 MB/HR'}堆不同堆内存比较和内存溢出代码故障排除。下面,我们通过一个示例演示如何排除和定位内存泄漏:
首先,让我们创建一个导致内存泄漏的示例:
//app . jsvar app=require(' express ')();var http=require('http ')。服务器(app);var heap dump=require(' heap dump ');var leakobjs=[];函数LeakClass(){ this . x=1;}app.get('/',function(req,RES){ console . log(' get/');for(var I=0;i 1000I){ leakobjs . push(new LeakClass());} RES . send(' H1Hello world/h1 ');});setInterval(函数(){ heapdump.writeSnapshot(')。/“date . now()”。heapsnapshot’);}, 3000);http.listen(3000,function(){ console.log('监听端口3000 ');});在这里,我们通过设置一个递增且不循环的数组来模拟内存泄漏。
使用堆转储模块定期记录内存快照,使用chrome developer工具配置文件导入快照进行对比分析。
我们可以看到,浏览器访问localhost:3000并刷新几次后,快照的大小不断增加,即使不请求,也不减少,说明已经发生了泄露。
然后我们通过chrome开发者工具配置文件导入快照。通过设置比较,比较初始快照,发送请求,稳定,然后发送请求的内存快照。你可以发现LeakClass在右边不断增加新的内容。delta中始终为正数,表示尚未回收。
总结
根据内存泄漏情况,可以植入memwatch,也可以定期向监控器报告process.memoryUsage的内存使用率,并设置报警阈值进行监控。
当发现内存泄漏问题时,如果允许,您可以在本地运行node-heapdump,并定期生成内存快照。并通过铬型材分析泄漏原因。如果无法在本地调试,可以使用v8-profiler输出内存快照来比较和分析测试服务器上的json(需要代码入侵)。
在什么情况下应该打开memwatch/heapdump?考虑heapdump的频率,以免耗尽CPU。也可以考虑其他检测内存增长的方法,比如直接监控process.memoryUsage()。
当心误判。短期内存使用高峰看起来像内存泄漏。如果你的应用突然占用大量的CPU和内存,处理时间可能会跨越几个垃圾收集周期,在这种情况下memwatch可能会误判为内存泄漏。然而,在这种情况下,一旦你的应用程序用完了这些资源,内存消耗就会下降到正常水平。因此,需要注意不断报告的内存泄漏,忽略一两个突然的警报。