什么是内存泄露内存泄露如何解决
内存泄露:指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
内存泄漏解决方法:
1、非静态内部类创建静态实例造成的内存泄漏:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例即可;
2、Handler造成的内存泄漏:将Handler类独立出来或者使用静态内部类,这样便可以避免内存泄漏;
3、集合容器中的内存泄露:在退出程序之前,将集合里的东西“clear”,然后置为“null”,再退出程序;
4、WebView造成的泄露:为WebView另外开启一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。
内存泄漏的原因及解决办法
1、非静态内部类创建静态实例造成的内存泄漏。解决方法:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,就使用Application的Context。2、Handler造成的内存泄漏,解决方法:将Handler类独立出来或者使用静态内部类,这样便可以避免内存泄漏。3、线程造成的内存泄漏,解决方法:将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。4、使用ListView时造成的内存泄漏,解决方法:在构造Adapter时,使用缓存的convertView。5、集合容器中的内存泄露,解决方法:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。以上内容参考 百度百科-内存泄漏
如何自己检查NodeJS的代码是否存在内存泄漏
内存泄露的检测npm模块 memwatch 是一个非常好的内存泄漏检查工具,让我们先将这个模块安装到我们的app中去,执行以下命令:npm install --save memwatch然后,在我们的代码中,添加:var memwatch = require('memwatch');memwatch.setup();然后监听 leak 事件memwatch.on('leak', function(info) {console.error('Memory leak detected: ', info);});这样当我们执行我们的测试代码,我们会看到下面的信息:{start: Fri Jan 02 2015 10:38:49 GMT+0000 (GMT),end: Fri Jan 02 2015 10:38:50 GMT+0000 (GMT),growth: 7620560,reason: 'heap growth over 5 consecutive GCs (1s) - -2147483648 bytes/hr'}memwatch发现了内存泄漏!memwatch 判定内存泄漏事件发生的规则如下:当你的堆内存在5个连续的垃圾回收周期内保持持续增长,那么一个内存泄漏事件被派发了解更加详细的内容,查看 memwatch内存泄漏分析使用memwatch我们发现了存在内存泄漏,这非常好,但是现在呢?我们还需要定位内存泄漏出现的实际位置。要做到这一点,有两种方法可以使用。memwatch heap diff通过memwatch你可以得到堆内存使用量和内存随程序运行产生的差异。详细的文档在这里例如,我们可以在两个leak事件发生的间隔中做一个heap dump:var hd;memwatch.on('leak', function(info) {console.error(info);if (!hd) {hd = new memwatch.HeapDiff();} else {var diff = hd.end();console.error(util.inspect(diff, true, null));hd = null;}});执行这段代码会输出更多的信息:{ before: {nodes: 244023,time: Fri Jan 02 2015 12:13:11 GMT+0000 (GMT),size_bytes: 22095800,size: '21.07 mb' },after: {nodes: 280028,time: Fri Jan 02 2015 12:13:13 GMT+0000 (GMT),size_bytes: 24689216,size: '23.55 mb' },change: {size_bytes: 2593416,size: '2.47 mb',freed_nodes: 388,allocated_nodes: 36393,details:[ { size_bytes: 0,'+': 0,what: '(Relocatable)','-': 1,size: '0 bytes' },{ size_bytes: 0,'+': 1,what: 'Arguments','-': 1,size: '0 bytes' },{ size_bytes: 2856,'+': 223,what: 'Array','-': 201,size: '2.79 kb' },{ size_bytes: 2590272,'+': 35987,what: 'Closure','-': 11,size: '2.47 mb' },...所以在内存泄漏事件之间,我们发现堆内存增长了2.47MB,而导致内存增长的罪魁祸首是闭包。如果你的泄漏是由某个class造成的,那么what字段可能会输出具体的class名字,所以这样的话,你会获得足够的信息来帮助你最终定位到泄漏之处。然而,在我们的例子中,我们唯一获得的信息只是泄漏来自于闭包,这个信息非常有用,但是仍不足以在一个复杂的应用中迅速找到问题的来源(复杂的应用往往有很多的闭包,不知道哪一个造成了内存泄漏——译者注)所以我们该怎么办呢?这时候该Heapdump出场了。Heapdumpnpm模块node-heapdump是一个非凡的模块,它可以使用来将v8引擎的堆内存内容dump出来,这样你就可以在Chrome的开发者工具中查看问题。你可以在开发工具中对比不同运行阶段的堆内存快照,这样可以帮助你定位到内存泄漏的位置。要想了解heapdump的更多内容,可以阅读这篇文章现在让我们来试试 heapdump,在每一次发现内存泄漏的时候,我们都将此时的内存堆栈快照写入磁盘中:memwatch.on('leak', function(info) {console.error(info);var file = '/tmp/myapp-' + process.pid + '-' + Date.now() + '.heapsnapshot';heapdump.writeSnapshot(file, function(err){if (err) console.error(err);else console.error('Wrote snapshot: ' + file);});});运行我们的代码,磁盘上会产生一些.heapsnapshot的文件到/tmp目录下。现在,在Chrome浏览器中,启动开发者工具(在mac下的快捷键是alt+cmd+i),点击Profiles标签并点击Load按钮载入我们的快照。我们能够很清晰地发现原来leakyfunc()是内存泄漏的元凶。我们依然还可以通过对比两次记录中heapdump的不同来更加迅速确认两次dump之间的内存泄漏:想要进一步了解开发者工具的memory profiling功能,可以阅读 Taming The Unicorn: Easing JavaScript Memory Profiling In Chrome DevTools 这篇文章。Turbo Test Runner我们给Turbo - FeedHenry开发的测试工具提交了一个小补丁 — 使用了上面所说的内存泄漏检查技术。这样就可以让开发者写针对内存的单元测试了,如果模块有内存问题,那么测试结果中就会产生相应的警告。详细了解具体的内容,可以访问Turbo模块。结论和其他细节上面的内容讨论了一种检测NodeJS内存泄漏的基本方法,以下是一些结论:heapdump有一些潜规则,例如快照大小等。仔细阅读说明文档,并且生成快照也是比较消耗CPU资源的。还有些其他方法也能生成快照,各有利弊,针对你的项目选择最适合的方式。(例如,发送sigusr2到进程等等,这里有一个memwatch-sigusr2项目)需要考虑在什么情况下开启memwatch/heapdump。只有在测试环境中有开启它们的必要,另外也需要考虑heapdump的频度以免耗尽了CPU。总之,选择最适合你项目的方式。也可以考虑其他的方式来检测内存的增长,比如直接监控process.memoryUsage()是一个可以考虑的方法。当内存问题被探测到之后,你应该要确定这确实是个内存泄漏问题,然后再告知给相关人员。当心误判,短暂的内存使用峰值表现得很像是内存泄漏。如果你的app突然要占用大量的CPU和内存,处理时间可能会跨越数个垃圾回收周期,那样的话memwatch很有可能将之误判为内存泄漏。但是,这种情况下,一旦你的app使用完这些资源,内存消耗就会降回正常的水平。所以,你其实需要注意的是持续报告的内存泄漏,而可以忽略一两次突发的警报。memwatch目前仅支持node 0.10.x,node 0.12.x(可能还有io.js)支持的版本在这个分支
如何分析 Node.js 中的内存泄漏
内存泄漏的几种情况一、全局变量a = 10;//未声明对象。global.b = 11;//全局变量引用这种比较简单的原因,全局变量直接挂在 root 对象上,不会被清除掉。二、闭包function out() {const bigData = new Buffer(100);inner = function () {void bigData;}}闭包会引用到父级函数中的变量,如果闭包未释放,就会导致内存泄漏。上面例子是 inner 直接挂在了 root 上,那么每次执行 out 函数所产生的 bigData 都不会释放,从而导致内存泄漏。需要注意的是,这里举得例子只是简单的将引用挂在全局对象上,实际的业务情况可能是挂在某个可以从 root 追溯到的对象上导致的。三、事件监听Node.js 的事件监听也可能出现的内存泄漏。例如对同一个事件重复监听,忘记移除(removeListener),将造成内存泄漏。这种情况很容易在复用对象上添加事件时出现,所以事件重复监听可能收到如下警告:(node:2752) Warning: Possible EventEmitter memory leak detected。11 haha listeners added。Use emitter。setMaxListeners() to increase limit例如,Node.js 中 Agent 的 keepAlive 为 true 时,可能造成的内存泄漏。当 Agent keepAlive 为 true 的时候,将会复用之前使用过的 socket,如果在 socket 上添加事件监听,忘记清除的话,因为 socket 的复用,将导致事件重复监听从而产生内存泄漏。原理上与前一个添加事件监听的时候忘了清除是一样的。在使用 Node.js 的 http 模块时,不通过 keepAlive 复用是没有问题的,复用了以后就会可能产生内存泄漏。所以,你需要了解添加事件监听的对象的生命周期,并注意自行移除。关于这个问题的实例,可以看 Github 上的 issues(node Agent keepAlive 内存泄漏)四、其他原因还有一些其他的情况可能会导致内存泄漏,比如缓存。在使用缓存的时候,得清楚缓存的对象的多少,如果缓存对象非常多,得做限制最大缓存数量处理。还有就是非常占用 CPU 的代码也会导致内存泄漏,服务器在运行的时候,如果有高 CPU 的同步代码,因为Node.js 是单线程的,所以不能处理处理请求,请求堆积导致内存占用过高。定位内存泄漏一、重现内存泄漏情况想要定位内存泄漏,通常会有两种情况:对于只要正常使用就可以重现的内存泄漏,这是很简单的情况只要在测试环境模拟就可以排查了。对于偶然的内存泄漏,一般会与特殊的输入有关系。想稳定重现这种输入是很耗时的过程。如果不能通过代码的日志定位到这个特殊的输入,那么推荐去生产环境打印内存快照了。需要注意的是,打印内存快照是很耗 CPU 的操作,可能会对线上业务造成影响。快照工具推荐使用 heapdump 用来保存内存快照,使用 devtool 来查看内存快照。使用 heapdump 保存内存快照时,只会有 Node.js 环境中的对象,不会受到干扰(如果使用 node-inspector 的话,快照中会有前端的变量干扰)。PS:安装 heapdump 在某些 Node.js 版本上可能出错,建议使用 npm install heapdump -target=Node.js 版本来安装。二、打印内存快照将 heapdump 引入代码中,使用 heapdump.writeSnapshot 就可以打印内存快照了了。为了减少正常变量的干扰,可以在打印内存快照之前会调用主动释放内存的 gc() 函数(启动时加上 --expose-gc 参数即可开启)。const heapdump = require('heapdump');const save = function () { gc(); heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot');}在打印线上的代码的时候,建议按照内存增长情况来打印快照。heapdump 可以使用 kill 向程序发送信号来打印内存快照(只在 *nix 系统上提供)。kill -USR2 推荐打印 3 个内存快照,一个是内存泄漏之前的内存快照,一个是少量测试以后的内存快照,还有一个是多次测试以后的内存快照。第一个内存快照作为对比,来查看在测试后有哪些对象增长。在内存泄漏不明显的情况下,可以与大量测试以后的内存快照对比,这样能更容易定位。三、对比内存快照找出泄漏位置通过内存快照找到数量不断增加的对象,找到增加对象是被谁给引用,找到问题代码,改正之后就行,具体问题具体分析,这里通过我们在工作中遇到的情况来讲解。const {EventEmitter} = require('events');const heapdump = require('heapdump');global.test = new EventEmitter();heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot');function run3() { const innerData = new Buffer(100); const outClosure3 = function () { void innerData; }; test.on('error', () => { console.log('error'); }); outClosure3();}for(let i = 0; i < 10; i++) { run3();}gc();heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot');这里是对错误代码的最小重现代码。首先使用 node --expose-gc index.js 运行代码,将会得到两个内存快照,之后打开 devtool,点击 profile,载入内存快照。打开对比,Delta 会显示对象的变化情况,如果对象 Delta 一直增长,就很有可能是内存泄漏了。