日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

記錄一個關于線程內存泄漏問題的定位過程,以及過程中的收獲。

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造成的泄漏問題。

通過如下腳本,然后對每次抓取內容進行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...
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:TID]類型的內存以及一個匿名內存在不停增加消耗內存。

其中[stack:TID]類型的內存,在內核查找相關代碼沒有明確對應屬性。初步判斷是線程的棧,TID表示線程id號。

所以這里應該是某個線程泄漏。

2.1 線程棧泄漏(Joinable線程棧)

一個導致線程棧泄漏原因可能是對于一個Joinable線程,系統會創建線程私有的棧、threand ID、線程結束狀態等信息。

如果此線程沒有pthread_join(),那么系統不會對以上信息進行回收。這就可能造成線程棧等泄漏。

確定線程棧泄漏的方法是:通過ls /proc/<pid>/task | wc -l確定進程下線程數目。然后在maps中檢查[stack:TID]數目。兩者如果不一致,則存在Joinable線程沒有調用pthread_join()造成的泄漏。

如果maps沒有[stack:TID],可以通過pmap <pid> | grep <stack size> | wc -l,即通過檢查棧大小的vma數目來確定棧數目。

3. 問題根源

通過檢查線程棧消耗與實際線程數目,發現兩者數目吻合。所以線程并沒有退出。也即不是由于未使用pthread_join()導致的內存泄漏。

然后根據maps中[stack:TID]的pid號,cat /proc/<pid>/comm發現是同一個線程不停創建。但是沒有釋放。

其實通過top -H -p <pid>和maps也可發現問題,中間走了彎路。

所以問題的根源是,進程不停創建但是沒有退出造成內存消耗殆盡。

相關視頻推薦

內存泄漏的3個解決方案與原理實現,知道一個可以輕松應對開發

4種實時線上內存泄漏檢測的實現方式【linux后臺開發】

學習地址:C/C++Linux服務器開發/后臺架構師【零聲教育】-學習視頻教程-騰訊課堂

需要C/C++ linux服務器架構師學習資料加qun812855908獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享

一個線程內存泄漏問題定位過程

 

4. 收獲

有兩個收獲,一是創建的pthread線程Join和Detach兩種狀態下內存處理差別;二是在進程maps中顯示線程棧[stack:TID]更有利于調試。

4.1 pthread線程的join和detach區別

《Avoiding memory leaks in POSIX thread programming》講到如何避免POSIX線程編程時內存泄漏。

首先pthread_create()創建的線程默認是joinable的。

對于joinable線程,系統會分配私有內存存儲線程結束狀態、線程棧、線程ID等等資源。這些資源會一直存在,直到線程結束并且線程被其他線程joined。所以確保joinable線程資源得到釋放的兩個條件是:線程退出、被其他線程joined。

對于detached線程,如果其退出,那么系統會自動回收其占用的資源。

關于joinable線程沒有被其他線程joined造成內存泄漏的實驗。

#include<stdio.h>
#include<pthread.h>

void run() {
   pthread_exit(0);
}

int main () {
    pthread_t thread;
    int rc;
    long count = 0;
    while(1) {
        if(rc = pthread_create(&thread, 0, run, 0) ) {
            printf("ERROR, rc is %d, so far %ld threads createdn", rc, count);
            perror("Fail:");
            return -1;
        }
        usleep(10);
        count++;
    }
    return 0;
}

輸出結果如下:

ERROR, rc is 11, so far 32751 threads created
Fail:: Cannot allocate memory

總共創建了32571個線程,造成內存消耗殆盡。

通過對比中間過程的maps,可以發現每次增加一個8MB的棧以及一個分隔頁。

一個線程內存泄漏問題定位過程

 

在pthread_create()之后增加pthread_join()則內存非常穩定。

#include<stdio.h>
#include<pthread.h>

void run() {
   pthread_exit(0);
}

int main () {
    pthread_t thread;
    int rc;
    long count = 0;
    while(1) {
        if(rc = pthread_create(&thread, 0, run, 0) ) {
            printf("ERROR, rc is %d, so far %ld threads createdn", rc, count);
            perror("Fail:");
            return -1;
        }
        pthread_join(thread, NULL);
        usleep(10);
        count++;
    }
    return 0;
}

借用文檔里面一句話總結一下:Joinable threads should be joined during programming. If you are creating joinable threads in your program, don’t forget to call pthread_join(pthread_t, void**) to recycle the private storage allocated to the thread.

調用pthread_join()將阻塞線程自己,一直等到加入的線程運行結束。

線程可以分為兩種:joined和detached。并不是所有線程創建后都默認joinable,需要顯式指定屬性。

joinable線程在創建后,可以通過pthread_detach()顯式分離。在分離后,不可以再合并。

如果一個線程結束運行,但沒有被join。則它的狀態類似進程中的Zombie Process,即還有一部分資源沒有被回收,所以創建線程者應該調用pthread_join()來等待線程結束,并可得到線程的退出代碼,回收其資源。

如果父進程調用pthread_detach(child_thread_id)或者子進程調用pthread_detack(pthread_self())即可將子進程狀態設置為detached,該程序運行結束后會自動釋放所有資源。

4.2 關于在maps中顯示[stack:TID]

在進程maps中顯示線程棧信息,最后在內核中被放棄。

首先在《procfs: mark thread stack correctly in proc/<pid>/maps》中,添加了[stack:TID]用于表示此vma對應的是線程TID的stack區域。

這樣做的好處是,可以從maps中明確知道此段vma是被哪個線程使用的。

有一個壞處就是先線程非常多情況下,主線程中為了顯示[stack:TIS],開銷就會很大,而實際上用處不是很大。

所以在《proc: revert /proc/<pid>/maps [stack:TID] annotation》將進程maps中的[stack:TID]刪除了,只顯示為匿名內存。

最終再《fs/proc: Stop trying to report thread stacks》將所有[stack:TID]全部移除。

那么在沒有[stack:TID]的情況下如何斷定vma是否是線程棧呢?

首先線程棧大小可以通過ulimit -s查看,所以maps中vma大小和這個一致;并且屬性應該是匿名的rw-p。

然后上面應該是一頁大小作為分隔區間,分隔頁的屬性應該是---p。

分享到:
標簽:泄漏 內存
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定