在 POSIX 執行緒程式設計中避免記憶體洩漏

2021-07-27 05:00:32 字數 3362 閱讀 5291

wei dong xie, ibm systems director 產品工程師, ibm

2010 年 9 月 27 日

檢測和避免 posix 執行緒記憶體洩漏的技巧

posix 執行緒(pthread)程式設計定義了一套標準的 c 程式語言型別、函式和常量 — 且 pthreads 提供了一種強大的執行緒管理工具。要充分使用 pthreads,您要避免常見錯誤。乙個常見的錯誤就是忘記聯接可接合的執行緒,從而導致記憶體洩漏並增加工作量。在該篇技巧型文章中,學習 posix 執行緒基礎,了解如何識別和檢測線程記憶體洩漏,並獲得避免出現這種情況的可靠建議。 內容

使用執行緒的主要原因是要提高程式效能。執行緒的建立和管理只需要較小的作業系統開銷和較少的系統資源。乙個程序內的所有執行緒共享相同的位址空間,使得執行緒間的通訊更高效,且比程序間通訊更易於實現。例如,如果乙個執行緒在等待乙個輸入/輸出系統呼叫完成,其他執行緒可以處理 cpu 密集型任務。通過執行緒,可以優先排程重要任務 — 甚至中斷 — 低優先順序任務。可將偶爾發生的任務放在定期排程的任務之間,建立排程靈活性。最後,pthreads 是在多 cpu 計算機上進行並行程式設計的理想之選。

而且使用 posix 執行緒或 pthreads 的主要原因更加簡單:作為標準 c 語言執行緒程式設計介面的一部分,它們可高可移植的。

posix 執行緒程式設計有諸多優勢,但是如果您不明確一些基本規則,就有可能編寫一些難以除錯的**並造成記憶體洩漏。我們首先回顧一下 posix 執行緒,分為可接合執行緒 或分離執行緒。

如果您希望生成乙個新的執行緒,且需要知道它是如何終止的,那麼您需要乙個可接合執行緒。對於可接合執行緒,系統分配專用儲存器來儲存執行緒終止狀態。執行緒終止後狀態得到更新。要獲得執行緒終止狀態,呼叫pthread_join(pthread_t thread, void** value_ptr)

系統為每個執行緒分配底層儲存,包括堆疊、執行緒 id、執行緒終止狀態等。這個底層儲存將一直保留在程序空間(且不能**),直至執行緒終止並為其他執行緒所聯接。

大多數時候,您只需建立乙個執行緒,向其分配一些任務,然後繼續處理其他事務。在這些情況下,您不關注執行緒是如何終止的,這時使用分離執行緒是乙個很好的選擇。

回頁首如果您建立乙個可接合的執行緒,但是忘記聯接它,其資源或私有記憶體一直儲存在程序空間中,從未進行**再利用。一定要聯接可接合的執行緒;否則,可能會引起嚴重的記憶體洩漏問題。

清單 1 顯示在忘記聯接可接合執行緒時引發的嚴重記憶體洩漏。您還可以使用該**檢查可在乙個程序空間中共存的執行緒體的最大量。

清單 1. 引發記憶體洩漏

#include#includevoid run() 

int main ()

count++;

}return 0;

}

清單 1 中呼叫了pthread_create()來建立乙個含預設執行緒屬性的新執行緒。預設情況下,新建立的執行緒是可接合的。它不斷建立新的可接合執行緒,直至有故障發生。然後輸出錯誤**和故障原因。

使用以下命令在 red hat enterprise linux server 5.4 上編譯清單 1 中的**時:[root@server ~]# cc -lpthread thread.c -o thread, 您將獲得清單 2 所示的結果。

清單 2. 記憶體洩漏結果

[root@server ~]# ./thread

error, rc is 12, so far 304 threads created

fail:: cannot allocate memory

在**建立了 304 個執行緒之後,它無法建立更多執行緒。錯誤**是12,這表示無更多記憶體可用。

如清單 1 和清單 2 所示,雖然生成了可接合執行緒,但是卻未將其聯接,因此每個終止的可接合執行緒仍然占用程序空間,洩漏程序記憶體。

rhel 上的乙個 posix 執行緒擁有乙個大小為 10mb 的私有堆疊。換言之,系統為每個 pthread 分配至少 10mb 的專用儲存。在我們的示例中,304 個執行緒是在程序停止前建立的;這些執行緒占用 304*10mb 記憶體,合計約 3gb。乙個程序的虛擬記憶體的大小是 4gb,其中四分之一的程序空間是為 linux 核心預留的。這樣一來,就有 3gb 的記憶體空間可用作使用者空間。因此,3gb 記憶體由死執行緒消耗。這是很嚴重的記憶體洩漏。而且很容易理解它發生的速度為何如此之快。

要修復洩漏,您可以新增**呼叫pthread_join(),該方法可聯接每個可接合執行緒。

回頁首如同其他記憶體洩漏中一樣,程序啟動時問題可能沒那麼明顯。這裡介紹一種無需訪問源**便可檢測此類問題的方法:

計算程序中線程堆疊的數量。這包括正在執行的活動執行緒和已終止執行緒的數量。

計算程序中正在執行的活動執行緒的數量。比較兩者。如果現有執行緒堆疊的數量大於正在執行的活動執行緒的數量,且在程式執行時這兩個數字的差量在不斷增加,那麼內存在洩漏。

這種記憶體洩漏很有可能是因未能聯接可接合執行緒而造成的。

在乙個執行的程序中,執行緒堆疊的數量等於程序中線程體的數量。執行緒體包括執行的活動執行緒和可接合的死執行緒。

pmap是一種用於匯報程序記憶體的 linux 工具。結合使用以下命令來獲取執行緒堆疊數:

[root@server ~]# pmap pid | grep 10240 | wc -l

(10240kb 是 red hat enterprise linux server 5.4 上的預設堆疊大小。)

每次建立乙個執行緒且該執行緒在執行時,會有乙個條目填充到 /proc/pid/task 中。當執行緒終止時,不管該執行緒是可接合的還是分離的,都會將該條目從 /proc/pid/task 中刪除。因此活動執行緒數可通過執行以下命令得出:

[root@server ~]# ls /proc/pid/task | wc -l.

檢查pmap pid | grep 10240 | wc -l的輸出並將其與ls /proc/pid/task | wc -l的輸出進行比較。如果所有執行緒堆疊的數量大於活動執行緒的數量,且在程式執行時兩者的差量在持續增長,您可以確定記憶體洩漏問題確實存在。

回頁首在程式設計過程中應當聯接可接合執行緒。如果您在程式中建立可接合的執行緒,切勿忘記呼叫pthread_join(pthread_t, void**)來**分配給執行緒的專用儲存。否則,將引發嚴重的記憶體洩漏問題。

在程式設計後的測試階段,您可以使用pmap/proc/pid/task檢測這種洩漏是否存在。如果確實存在,檢查源**,看是否聯接了所有可接合執行緒。

就這些內容。只需少量預防工作即可為您省掉大量後續工作,避免令人頭疼的記憶體洩漏問題。

在 POSIX 執行緒程式設計中避免記憶體洩漏

原文 http www.ibm.com developerworks cn linux l memory leaks index.html?ca drs posix 執行緒簡介 使用執行緒的主要原因是要提高程式效能。執行緒的建立和管理只需要較小的作業系統開銷和較少的系統資源。乙個程序內的所有執行緒共...

在 POSIX 執行緒程式設計中避免記憶體洩漏

posix 執行緒 pthread 程式設計定義了一套標準的 c 程式語言型別 函式和常量 且 pthreads 提供了一種強大的執行緒管理工具。要充分使用 pthreads,您要避免常見錯誤。乙個常見的錯誤就是忘記聯接可接合的執行緒,從而導致記憶體洩漏並增加工作量。在該篇技巧型文章中,學習 pos...

Posix執行緒程式設計指南 2

概念及作用 在單執行緒程式中,我們經常要用到 全域性變數 以實現多個函式間共享資料。在多執行緒環境下,由於資料空間是共享的,因此全域性變數也為所有執行緒所共有。但有時應用程式設計中有必要提供執行緒私有的全域性變數,僅在某個執行緒中有效,但卻可以跨多個函式訪問,比如程式可能需要每個執行緒維護乙個鍊錶,...