码迷,mamicode.com
首页 > 编程语言 > 详细

一个线程内存泄漏问题定位过程

时间:2019-12-19 12:42:14      阅读:158      评论:0      收藏:0      [点我收藏+]

标签:libs   重复   --   xxxxx   多次   txt   cto   map   过多   

关键词:meminfo、slabinfo、top、pthread_join、thread stack等等。

 

记录一个关于线程内存泄漏问题的定位过程,以及过程中的收获。

1. 初步定位

是否存在内存泄漏:想到内存泄漏,首先查看/proc/meminfo,通过/proc/meminfo可以看出总体内存在下降。确定内存泄漏确实存在。top中可以显示多种形式内存,进而可以判断是那种泄漏。比如vss/rss/pss等。

确定哪个进程内存泄漏:通过top即可查看到是哪个进程在泄漏。至此基本可以确定到哪个进程。

确定进程泄漏内存类型:然后查看进程的/proc/<pid>/maps,通过maps可以看出泄漏的内存类型(堆、栈、匿名内存等等),有时候运气好可以直接判断泄漏点。

如果是slab:可以通过/proc/slabinfo,可以看出进程的动态变化情况。如果确定是哪一个slab,那么可以在/sys/kernel/slab/<slab name>/alloc_calls和free_calls中直接找到调用点。当然看到的是内核空间的函数。

使用mcheck():可以检查malloc/free造成的泄漏问题,详细参考《2. mtrace/muntrace/MALLOC_TRACE(重复释放、泄漏)

通过如下脚本,然后对每次抓取内容进行Beyond Compare。每个一定周期抓取相关内存消耗信息。

#!/bin/bash
echo > mem_log.txt
while true
do
    cat /proc/meminfo >>mem_log.txt
    cat /proc/<pid>/maps >>mem_log.txt
    cat /proc/slabinfo >>mem_log.txt
    sleep 240
done

当然还有其他工具gcc Sanitier、Valgrind等等,由于嵌入式环境受限未能使用。

2. 深入定位

同步查看meminfo、maps、slabinfo,发觉进程虚拟内存损耗很快,远比系统MemFree损耗快。而且slabinfo没有和maps同步损耗。

所以问题重点检查maps问题。

00010000-00083000 r-xp 00000000 b3:11 22         /heop/package/AiApp/AiApp
00092000-00099000 rwxp 00072000 b3:11 22         /heop/package/AiApp/AiApp
00099000-00b25000 rwxp 00000000 00:00 0          [heap]
00b51000-00b52000 ---p 00000000 00:00 0 
00b52000-01351000 rwxp 00000000 00:00 0          [stack:30451]
01351000-01352000 ---p 00000000 00:00 0 
01352000-01b51000 rwxp 00000000 00:00 0 
01b51000-01b52000 ---p 00000000 00:00 0 
01b52000-02351000 rwxp 00000000 00:00 0          [stack:30432]
02351000-02352000 ---p 00000000 00:00 0 
02352000-02b51000 rwxp 00000000 00:00 0 
02b51000-02b52000 ---p 00000000 00:00 0 
...
64f55000-65754000 rwxp 00000000 00:00 0          [stack:28646]
65754000-65755000 ---p 00000000 00:00 0 
65755000-65f54000 rwxp 00000000 00:00 0          [stack:28645]
65f54000-65f55000 ---p 00000000 00:00 0 
65f55000-66754000 rwxp 00000000 00:00 0          [stack:28642]
66754000-6675a000 r-xp 00000000 00:02 5000324    /usr/lib/AiApp/gstreamer-1.0/libgsticcsink.so
6675a000-66769000 ---p 00000000 00:00 0 
...
6699f000-669a0000 rwxp 00000000 00:02 4999516    /usr/lib/AiApp/gstreamer-1.0/libgstapp.so
669a0000-66a2e000 rwxp 00000000 00:02 4999517    /usr/lib/AiApp/gstreamer-1.0/libgstlive555src.so
66a2e000-66a3e000 ---p 00000000 00:00 0 
66a3e000-66a44000 rwxp 0008e000 00:02 4999517    /usr/lib/AiApp/gstreamer-1.0/libgstlive555src.so
66a44000-66a45000 rwxp 00000000 00:00 0 
66a45000-66a46000 ---p 00000000 00:00 0 
66a46000-67245000 rwxp 00000000 00:00 0          [stack:28631]
67245000-67246000 ---p 00000000 00:00 0 
67246000-67a45000 rwxp 00000000 00:00 0          [stack:28630]
...
6b245000-6b246000 ---p 00000000 00:00 0 
6b246000-6ba45000 rwxp 00000000 00:00 0          [stack:28613]
6ba45000-6ba46000 ---p 00000000 00:00 0 
6ba46000-6c245000 rwxp 00000000 00:00 0          [stack:28610]
6c245000-71066000 rwxs 00000000 00:01 196614     /SYSV5553fc99 (deleted)
71066000-71067000 ---p 00000000 00:00 0 
71067000-71866000 rwxp 00000000 00:00 0          [stack:28609]
71866000-71867000 ---p 00000000 00:00 0 
71867000-72066000 rwxp 00000000 00:00 0          [stack:28608]
72066000-72228000 rwxs e3dc4000 00:02 6918       /dev/mmz_userdev
72228000-725ac000 rwxs e3a40000 00:02 6918       /dev/mmz_userdev
725ac000-75cac000 rwxs 00000000 00:01 131076     /SYSV6702121c (deleted)
75cac000-75e8a000 rwxs 00000000 00:01 98307      /SYSV6602121c (deleted)
75e8a000-7608e000 rwxp 00000000 00:00 0...
76eeb000-76efb000 ---p 00000000 00:00 0 
76efb000-76eff000 r-xp 000ce000 00:02 1234       /lib/libstdc++.so.6.0.20
76eff000-76f01000 rwxp 000d2000 00:02 1234       /lib/libstdc++.so.6.0.20
76f01000-76f08000 rwxp 00000000 00:00 0 
76f08000-76f0f000 r-xp 00000000 00:02 1235       /lib/ld-uClibc-0.9.33.2.so
76f1a000-76f1e000 rwxp 00000000 00:00 0 
76f1e000-76f1f000 rwxp 00006000 00:02 1235       /lib/ld-uClibc-0.9.33.2.so
76f1f000-76f20000 ---p 00000000 00:00 0 
76f20000-7771f000 rwxp 00000000 00:00 0 
7771f000-77720000 ---p 00000000 00:00 0 
77720000-77f1f000 rwxp 00000000 00:00 0          [stack:30470]
77f1f000-77f20000 ---p 00000000 00:00 0 
77f20000-7871f000 rwxp 00000000 00:00 0 
7871f000-78720000 ---p 00000000 00:00 0 
78720000-78f1f000 rwxp 00000000 00:00 0          [stack:30489]
78f1f000-78f20000 ---p 00000000 00:00 0 
78f20000-7971f000 rwxp 00000000 00:00 0 
7971f000-79720000 ---p 00000000 00:00 0 
79720000-79f1f000 rwxp 00000000 00:00 0          [stack:30508]
79f1f000-79f20000 ---p 00000000 00:00 0 
79f20000-7a71f000 rwxp 00000000 00:00 0 
7a71f000-7a720000 ---p 00000000 00:00 0 
7a720000-7af1f000 rwxp 00000000 00:00 0          [stack:30539]
7af1f000-7af20000 ---p 00000000 00:00 0 
7af20000-7b71f000 rwxp 00000000 00:00 0 
7b71f000-7b720000 ---p 00000000 00:00 0 
7b720000-7bf1f000 rwxp 00000000 00:00 0          [stack:30556]
7bf1f000-7bf20000 ---p 00000000 00:00 0 
7bf20000-7c71f000 rwxp 00000000 00:00 0 
7c71f000-7c720000 ---p 00000000 00:00 0 
7c720000-7cf1f000 rwxp 00000000 00:00 0          [stack:30574]
7cf1f000-7cf20000 ---p 00000000 00:00 0 
7cf20000-7e121000 rwxp 00000000 00:00 0          [stack:30575]
7eef7000-7ef18000 rwxp 00000000 00:00 0          [stack]
7efb7000-7efb8000 r-xp 00000000 00:00 0          [sigpage]
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

通过多次maps对比,可以发现[stack:xxxxx]类型的内存以及一个匿名内存在不停增加消耗内存。

其中[stack:xxxxx]类型的内存,在内核查找相关代码没有明确对应属性。初步判断是线程的栈,xxxxxx表示线程id号。

所以这里应该是某个线程泄漏。

2.1 线程栈泄漏(Joinable线程栈)

一个导致线程栈泄漏原因可能是对于一个Joinable线程,系统会创建线程私有的栈、threand ID、线程结束状态等信息。

如果此线程没有pthread_join(),那么系统不会对以上信息进行回收。这就可能造成线程栈等泄漏。

确定线程栈泄漏的方法是:通过ls /proc/<pid>/task | wc -l确定进程下线程数目。然后在maps中检查[stack:xxxxx]数目。两者如果不一致,则存在Joinable线程没有调用pthread_join()造成的泄漏。

如果maps没有[stack:xxxxx],可以通过pmap <pid> | grep <stack size> | wc -l,即通过检查栈大小的vma数目来确定栈数目。

关于线程内存泄漏参考:《Avoiding memory leaks in POSIX thread programming

3. 问题根源

通过检查线程栈消耗与实际线程数目,发现两者数目吻合。所以线程并没有退出。也即不是由于未使用pthread_join()导致的内存泄漏。

然后根据maps中[stack:xxxxx]的pid号,cat /proc/<pid>/comm发现是同一个线程不停创建。但是没有释放。

其实通过top -H -p <pid>和maps也可发现问题,中间走了弯路。

所以问题的根源是,进程不停创建但是没有退出造成内存消耗殆尽。

4. 收获

有两个收获,一是创建的pthread线程Join和Detach两种状态下内存处理差别;二是在进程maps中显示线程栈[stack:xxxxx]更有利于调试。

[stack:xxxxx]不是标准内核提供的功能,这个有待研究。

 

一个线程内存泄漏问题定位过程

标签:libs   重复   --   xxxxx   多次   txt   cto   map   过多   

原文地址:https://www.cnblogs.com/arnoldlu/p/12063591.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!