如何查看和调试动态链接库的内存泄露
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何查看和调试动态链接库的内存泄露相关的知识,希望对你有一定的参考价值。
参考技术A ios怎么查看内存泄露,有以下几种方法供大家参考:1.静态分析
通过静态分析我们可以最初步的了解到代码的一些不规范的地方或者是存在的内存泄漏,这是我们第一步对内存泄漏的检测。当然有一些警告并不是我们关心的可以略过。
2.通过instruments来检查内存泄漏
这个方法能粗略的定位我们在哪里发生了内存泄漏。方法是完成一个循环操作,如果内存增长为0就证明我们程序在该次循环操作中不存在内存泄漏,如果内存增长不为0那证明有可能存在内存泄漏,当然具体问题需要具体分析。
3.代码测试内存泄漏
在做这项工作之前我们要注意一下,在dealloc的方法中我们是否已经释放了该对象所拥有的所有对象。观察对象的生成和销毁是否配对。准确的说就是init(创建对象的方法)和dealloc是否会被成对触发(简单说来就是走一次创建对象就有走一次dealloc该对象)。
下面是自己遇到的一些比较隐秘的造成内存泄漏的情况:
1.两个对象互相拥有:也就是说对象a里面retain/addSubview了b对象,b对象同时也retain/addSubView了a对象。注意:delegate不要用retain属性,要用assign属性也会导致互相拥有。
2.有时候需要用removeFromSuperView来释放:具体说明,也许我的a对象拥有一个b对象,b对象add到了c对象上,而在我们的设计中b对象的生命周期应该和a对象相同;这时候只一句[b release]/self.b = nil是不能把b对象释放掉的(一般情况下release会使其retainCount-1,[super dealloc]会再次将所有subView的retainCount-1,而b并不是a的subView,所有最后的一次-1没有了);所以我们需要在之前加上[b removeFromSuperView]。
如何调试 Node.js的内存泄露
原文:Hassy Veldstra
译文:伯乐在线 - 至秦
链接:http://web.jobbole.com/86556/
在产品应用程序中,内存泄露是很常见的。幸运的是通常不难发现它们。 接下来是一个练习的演练,这个练习是Igor Soarez 和我最近在 WDCNZ 上所教授 Node.js 性能专题课程的一部分。
问题
我们有一个服务运行在一个反向波兰表示法计算器(RPN,Reverse Polish Notation)的产品上,这个产品是基于 WebSockets 实现的。在这个程序的生命周期里,内存使用看上去不断地增长,尤其明显的是当我们使用这个服务时,内存使用会出现一个尖峰。
这个服务的源代码可以在我们 Github 的 calc-server 下找到。你可能仅通过检查这段很短的代码就可以找到这个内存泄露,但我们的想法是不阅读代码就能确定这个泄露。
快速地看一下 client.js 以便知道客户端是如何使用我们的服务。
在继续确认问题和分析程序前,我们先在本地安装和运行服务器。 我们利用 heapdump 来分析 Node 程序里的堆,以便找到一个解决方法。
确认诊断结果
检查内存的使用
在产品中,你可以使用一个应用程序性能管理(APM,Application Performance Management)方案去监控 RAM 使用,如果出现问题它会提醒你。
在这个练习中,我们将结合使用古老的 ps 和 top,以及一些用来引发问题的负载检测方法,以便我们证实问题的存在(检查是代码中的哪些改动造成这样的预期效果)。
使用 ps 来检查进程的内存使用情况:
ps -p $PID -o rss,vsz
(使用 pgrep -lfa node,你可以找到 Node.js 进程的 PID)
这告诉我们进程的驻留集大小和虚拟内存大小。(译者注:RSS,即进程所使用的非交换区的物理内存)
RSS 用来表示这个进程当前正在使用的 RAM 大小。包含所有的栈和堆内存,也会包含共享库的内存,只要那些库的页面实际上是在内存中。
VSZ 表示这个进程上有多少内存可以用。包含交换区的内存和所有的共享库。VSZ 包括 RSS,而且通常比较大。
我们可以使用 top 来观察内存使用的实时情况:
top -pid $PID
OSX 说明:OSX 即使在有大量空闲 RAM 可用时,也会积极地压缩它认为“不活动”进程的内存页面。这可能会导致对于一个空闲的 Node.js 进程, ps 和 top 显示内存占用较小,一旦这个进程重新开始做事情,内存占用就会激增。
(在Node 进程中调用 process.memmoryUsage 函数也可以测量 RAM 使用情况,但是我们不准备使用这种方法。)
测量内存使用的题外话
现代操作系统的内存管理是相当复杂的,对于“我的进程使用了多少内存”这个问题,并没有一个简单的答案。
这里我们要寻找的是确认内存使用在负载时仍然在增长 —— 而不是使用内存的准确数量。
扩展阅读:
http://stackoverflow.com/questions/860878/tracking-actively-used-memory-in-linux-programs/872456
https://mail.gnome.org/archives/gnome-list/1999-September/msg00036.html
http://bmaurer.blogspot.co.uk/2006/03/memory-usage-with-smaps.html
确认增长
下一步就是要在服务器上增加一些负载来确认内存的增加。我们使用 Mingigun,一个简单却强大的负载测试工具(实际上由你自己开发)来做这件事。
我们的负载测试脚本(包含在 test.json 这个repo中)每秒将创建10个新的用户会话,一共持续120秒的时间。每个用户都调用我们的服务去进行两个数字的加法操作:
运行下面脚本,安装 Minigun:
npm install -g minigun
然后运行它:
minigun run load_test_for_calc_server.json
当测试运行的时候,使用 top 监控你的 cal-server。我们应该看到在两分钟里内存使用量稳定地增长。
在我电脑上,内存使用量在 Node 运行后,从 14 MB 增加到 38 MB。重新多次运行这个脚本,内存使用量增加到 110 MB。好吧,休斯顿,我们的确有个问题。
题外话:读者的练习
我们能确认存在一个内存泄露吗?会不会仅仅因为系统还有很多空闲的内存,所以垃圾收集器就不再进行收集?
我们可以通过如下步骤强制运行垃圾收集来加以确认:
使用 –expose-gc 标记运行服务器:node –expose-gc server.js 这让 JS 代码中 gc() 函数可用,可以强制进行收集。
用 process.on 创建一个 SIGUSR2 的处理程序,process.on会调用gc()。
重新运行负载测试,通过下面命令让进程运行垃圾收集 kill -SIGUSR2 $(pgrep -lfa node | grep server.js | awk ‘{print $1}’),看会有什么不一样。
堆分析
heapdump 模块让我们对内存中的对象进行快照。接着我们可以使用 Chrome 开发工具来仔细查看它们以便找到内存泄露的对象类型,这样将帮助我们查明应用程序中有问题的代码。
我们采取如下步骤:
在程序启动后使用 heapdump —— 这作为我们的基准快照
运行一个负载测试程序来引起内存的增加
再一次进行堆快照。这个快照和基准快照的差别就是那些被挂起的对象不能被 GC 回收再利用。
用 heapdump 进行快照
首先,我们用 npm install heapdump 安装 heapdump,server.js 里会使用到它(或者在应用程序的 index.js 里)。
接着我们可以发送 SIGUSR2 给 Node 进程,这样将会把一个 heap 快照写到进程的工作路径下(一个名字类似 heapdump-706203888.138768.heapsnapshot的文件)
在这个练习中,我有三个快照:(1)服务器刚启动的;(2)负载测试过程中的;(3)负载测试结束后的。
说明
某些版本的 Node 或 Io.js,和 Chrome 有一些已知的兼容性问题,开发工具不能正确计算保留的大小和完整显示保留树。我使用的是 Node 0.12.7 和 Chrome 42,如果你偶尔遇到类似问题你可以升级 Node 或者使用 nvm。
另一个可能需要注意的问题是,当进行快照时,你系统中的可用 RAM 需要有 2 个 heap 大小,否则你将看到空的 heap 快照文件或者 内存耗尽的消息(OOM,Out of Memory)。
使用 Chrome 开发工具
一旦我们有了快照,就可以使用开发工具进行分析。
Chrome DevTools memory profile
一旦加载上,我们就可以观察堆。
像保留数量这些不同的术语,可以参考如下内容:
https://developers.google.com/web/tools/profile-performance/memory-problems/memory-101?hl=en
heap snapshot in Chrome DevTools
我们想使用比较视图来进一步定位内存使用量增加的原因。
heap snapshot difference
这个视图告诉我们和刚开始的快照相比,我们有 813 个新的 smalloc 类型对象,它们一共占用了 12.4 MB。
我们对于“保留数量”和“# 新的” 这两列很感兴趣 —— 这个例子中我选择关注 smalloc,因为这些对象和内存增长有关。
object's retaining tree
深入研究这些对象的其中一个,我们可以推出 cleanup() 函数是用来监听 SIGINT 事件的,涉及到一个叫做 clients 的数组,它是保存WebSocket 连接的。SIGINT (和 SIGTERM)是让进程退出的信号,是由进程的管理者发出的,类似 Upstart 或者 init(或者在终端按下 Ctrl+C)。这个例子里,cleanup() 函数在进程退出前断开所有连接的 WebSocket 客户端。我们猜测在进行堆快照时,服务器有 813 个活动的 WebSocket 连接。
负载测试结束后,看看堆是什么样子的:
heap snapshot difference after load-test
糟了,看上去不妙。WebSocket 连接的引用数目已经增加到1649个,这只有在仍有客户端连接到服务器的时候才算合理,但是因为(a)在我们快照前,负载测试已经结束;(b)在快照被写入前,GC 已经执行了,我们知道在代码中(我们可以开始查看了)有些断开连接的引用没有被去除。
修正这个问题就作为一个练习留给读者。:)
备注:如果Node.js 性能是一个深入你内心的主题,你可能会感兴趣知道 Igor Soarez 和 我碰巧正在写一本这方面的书。请前往 nodeperformance.com 登记预览(暂定在这个秋天)。
译者简介
至秦:Linux,Networking
打赏支持作者写出更多好文章,谢谢!
【今日微信公号推荐↓】
更多推荐请看《》
以上是关于如何查看和调试动态链接库的内存泄露的主要内容,如果未能解决你的问题,请参考以下文章