精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

線程內存泄漏問題的定位

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-13 11:38 ? 次閱讀

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

1. 初步定位

是否存在內存泄漏:想到內存泄漏,首先查看/proc/meminfo,通過/proc/meminfo可以看出總體內存在下降。確定內存泄漏確實存在。top中可以顯示多種形式內存,進而可以判斷是那種泄漏。比如vss/rss/pss等。

確定哪個進程內存泄漏:通過top即可查看到是哪個進程在泄漏。至此基本可以確定到哪個進程。

確定進程泄漏內存類型:然后查看進程的/proc//maps,通過maps可以看出泄漏的內存類型(堆、棧、匿名內存等等),有時候運氣好可以直接判斷泄漏點。

如果是slab:可以通過/proc/slabinfo,可以看出進程的動態變化情況。如果確定是哪一個slab,那么可以在/sys/kernel/slab//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//task | wc -l確定進程下線程數目。然后在maps中檢查[stack:TID]數目。兩者如果不一致,則存在Joinable線程沒有調用pthread_join()造成的泄漏。

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

3. 問題根源

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

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

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

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

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//maps》中,添加了[stack:TID]用于表示此vma對應的是線程TID的stack區域。

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

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

所以在《proc: revert /proc//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。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 嵌入式
    +關注

    關注

    5072

    文章

    19026

    瀏覽量

    303532
  • 函數
    +關注

    關注

    3

    文章

    4308

    瀏覽量

    62449
  • 線程
    +關注

    關注

    0

    文章

    504

    瀏覽量

    19653
  • 內存泄漏
    +關注

    關注

    0

    文章

    39

    瀏覽量

    9206
收藏 人收藏

    評論

    相關推薦

    Linux上對進程進行內存分析和內存泄漏定位

    和redis進程。當memfree不夠時,內核會通過回寫機制(pdflush線程)把cached和buffered內存回寫到后備存儲器,也可以通過手動方式顯式釋放cache內存 echo 3 >
    發表于 07-09 08:15

    AliOS Things 維測典型案例分析 —— 內存泄漏

    使用情況全部輸出,方便定位)這是由于內存不足,無法從系統內存池中mallo出動態內存,出現這種現象一般有2種原因:某組件在運行中持續分配了較大內存
    發表于 10-17 11:29

    Executors使用不當引起的內存泄漏怎么解決

    是否知道了此次引起內存泄漏的原因,其實就是因為阻塞隊列的容量過大。  如果不手動的指定阻塞隊列的大小,那么它默認是Integer.MAX_VALUE,我們的線程池只有20個線程可以處理
    發表于 12-23 17:38

    內存泄漏定位該如何去實現呢

    嵌入式之內存泄漏定位篇在嵌入式開發中,經常會使用malloc,free分配釋放堆內存,當malloc,free不配對使用時,就會導致內存一點
    發表于 12-17 07:24

    寫了一個內存泄漏檢查工具

    的malloc或者free的調用,記錄申請內存的大小,地址,和調用的函數。以便追蹤內存泄漏。并且開啟一個線程,每隔一段時間監測是否有哪個函數申請的
    發表于 12-17 08:25

    分享一種內存泄漏定位排查技巧

    這里寫自定義目錄標題1.對malloc,free進行封裝2.如何確定MALLOC_SIZE_OFFSET大小(編譯器malloc長度地址偏移)3.如何監測內存有無泄漏4.如何快速定位內存泄漏
    發表于 12-17 08:13

    如何編譯使用內存泄漏定位工具

    1.我們知道有個內存泄漏定位工具: valgrind, 非常優秀。現在已經支持arm版本,下面看看如何編譯使用:2.下載源碼:
    發表于 12-17 08:13

    sqlite軟件包內存泄漏如何解決?

    at function:rt_object_init, line number:358, 而從github下載的就能運行),運行一段時間后發現使用內存不斷增大,用memtrace工具確定是操作數據庫的線程導致,請問造成
    發表于 05-24 15:25

    解析Web內存分析與內存泄漏定位

    JavaScript 中開發者并不需要手動地為對象申請內存,只需要聲明變量,JavaScript Runtime 即可以自動地分配內存.所謂的內存泄漏,即是指某個對象被無意間添加了某條
    發表于 11-10 15:00 ?2437次閱讀

    ThreadLocal發生內存泄漏的原因

    ,就可能會導致內存泄漏。下面,我們將圍繞三個方面來分析 ThreadLocal 內存泄漏的問題 ThreadLocal 實現原理 ThreadLocal為什么會
    的頭像 發表于 05-05 16:23 ?3661次閱讀

    內存泄漏的特點和類型

    在計算機科學中,內存泄漏(memory leak)指由于疏忽或錯誤使程序未能釋放而造成不能再使用的內存的情況。內存泄漏并非指
    的頭像 發表于 06-20 10:58 ?2794次閱讀

    內存泄漏問題原理及檢視方法

    可能不少開發者都遇到過內存泄漏導致的網上問題,具體表現為單板在現網運行數月以后,因為內存耗盡而導致單板復位現象。一方面,內存泄漏問題屬于比較
    的頭像 發表于 10-10 10:42 ?2512次閱讀

    什么是內存泄漏內存泄漏有哪些現象

    內存泄漏幾乎是很難避免的,不管是老手還是新手,都存在這個問題,甚至 Windows 與 Linux 這類系統軟件也或多或少存在著內存泄漏
    的頭像 發表于 09-05 17:24 ?9622次閱讀

    什么是內存泄漏?如何避免JavaScript內存泄漏

    JavaScript 代碼中常見的內存泄漏的常見來源: 研究內存泄漏問題就相當于尋找符合垃圾回收機制的編程方式,有效避免對象引用的問題。
    發表于 10-27 11:30 ?363次閱讀
    什么是<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>?如何避免JavaScript<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>

    C語言內存泄漏問題原理

    內存泄漏問題只有在使用堆內存的時候才會出現,棧內存不存在內存泄漏問題,因為棧
    發表于 03-19 11:38 ?496次閱讀
    C語言<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>問題原理