『壹』 linux下一般怎麼診斷是哪個進程有memory leak
Memcheck。這是應用最廣泛的工具,一個重量級的內存檢查器,能夠發現開發中絕大多數內存錯誤使用情況,比如:使用未初始化的內存,使用已經釋放了的內存,內存訪問越界等。這也是本文將重點介紹的部分。
Callgrind。它主要用來檢查程序中函數調用過程中出現的問題。
Cachegrind。它主要用來檢查程序中緩存使用出現的問題。
Helgrind。它主要用來檢查多線程程序中出現的競爭問題。
Massif。它主要用來檢查程序中堆棧使用中出現的問題。
Extension。可以利用core提供的功能,自己編寫特定的內存調試工具
用法:valgrind[options] prog-and-args [options]: 常用選項,適用於所有Valgrind工具
-tool=<name> 最常用的選項。運行valgrind中名為toolname的工具。默認memcheck。
h –help 顯示幫助信息。
-version 顯示valgrind內核的版本,每個工具都有各自的版本。
q –quiet 安靜地運行,只列印錯誤信息。
v –verbose 更詳細的信息, 增加錯誤數統計。
-trace-children=no|yes 跟蹤子線程? [no]
-track-fds=no|yes 跟蹤打開的文件描述?[no]
-time-stamp=no|yes 增加時間戳到LOG信息? [no]
-log-fd=<number> 輸出LOG到描述符文件 [2=stderr]
-log-file=<file> 將輸出的信息寫入到filename.PID的文件里,PID是運行程序的進行ID
-log-file-exactly=<file> 輸出LOG信息到 file
-log-file-qualifier=<VAR> 取得環境變數的值來做為輸出信息的文件名。 [none]
-log-socket=ipaddr:port 輸出LOG到socket ,ipaddr:port
-xml=yes 將信息以xml格式輸出,只有memcheck可用
-num-callers=<number> show <number> callers in stack traces [12]
-error-limit=no|yes 如果太多錯誤,則停止顯示新錯誤? [yes]
-error-exitcode=<number> 如果發現錯誤則返回錯誤代碼 [0=disable]
-db-attach=no|yes 當出現錯誤,valgrind會自動啟動調試器gdb。[no]
-db-command=<command> 啟動調試器的命令行選項[gdb -nw %f %p]
適用於Memcheck工具的相關選項:
-leak-check=no|summary|full 要求對leak給出詳細信息? [summary]
-leak-resolution=low|med|high how much bt merging in leak check [low]
-show-reachable=no|yes show reachable blocks in leak check? [no]
下面是一段有問題的C程序代碼test.c
#i nclude <stdlib.h>
void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; //問題1: 數組下標越界
} //問題2: 內存沒有釋放
int main(void)
{
f();
return 0;
}
1、 編譯程序test.c
gcc -Wall test.c -g -o test
2、 使用Valgrind檢查程序BUG
valgrind --tool=memcheck --leak-check=full ./test
使用未初始化內存問題
對於位於程序中不同段的變數,其初始值是不同的,全局變數和靜態變數初始值為0,而局部變數和動態申請的變數,其初始值為隨機值。如果程序使用了為隨機值的變數,那麼程序的行為就變得不可預期。
下面的程序就是一種常見的,使用了未初始化的變數的情況。數組a是局部變數,其初始值為隨機值,而在初始化時並沒有給其所有數組成員初始化,如此在接下來使用這個數組時就潛在有內存問題。
輸出結果顯示,在該程序的第15行,進行了非法的寫操作;在第16行,進行了非法讀操作。准確地發現了上述問題
『貳』 static 變數初始化分析
在C++中,靜態變數的初始化策略有所不同,特別是全局和局部靜態變數。它們均存儲在bss段,但初始化方式各異。全局靜態變數在程序啟動前初始化,確保線程安全;而局部靜態變數則在首次調用相關函數時初始化,可能引發多線程並發問題。Linux平台的局部靜態變數初始化涉及對一個bss段標記的檢查:如果未初始化,會加鎖後開始初始化,並更新標記。Windows環境下,初始化邏輯相似,但使用的標志值和初始化條件不同,同樣包含二次加鎖機制。
在代碼層面,我們可以觀察到如下的初始化過程:
- Linux:在執行初始化語句時,首先檢查一個bss段的標記,若未初始化,進行加鎖,初始化後將標記設為已初始化,且可能包含二次加鎖以避免並發問題。
- Windows:在同樣檢查標記值後,Windows環境認為0x80000001表示初始化,與Linux的0x00000001不同。初始化過程同樣包含加鎖和二次判斷。
通過調試工具,如gdb和objmp,可以查看匯編代碼,具體到地址的值和操作,從而理解這個初始化機制是如何確保局部靜態變數在多線程環境下的線程安全的。
『叄』 Linux動態鏈接為什麼要用PLT和GOT表
編譯時,-fPIC編的是.so文件,這個文件也是要訪問外部變數的,但是鏈接它程序很多,它裡面的地址不能寫死。 以下引用一段話,關鍵第一句:
對於模塊外部引用的全局變數和全局函數,用 GOT表的表項內容作為地址來間接定址;對於本模塊內的靜態變數和靜態函數,用 GOT表的首地址作為一個基準,用相對於該基準的偏移量來引用,因為不論程序被載入到何種地址空間,模塊內的靜態變數和靜態函數與GOT 的距離是固定的,並且在鏈接階段就可知曉其距離的大小。這樣,PIC 使用 GOT來引用變數和函數的絕對地址,把位置獨立的引用重定向到絕對位置。
『肆』 linux是多線程還是多進程
進程:可執行程序是存儲在磁碟設備上的由代碼和數據按某種格式組織的靜態實體,而內進程是可被容調度的代碼的動態運行。在Linux系統中,每個進程都有各自的生命周期。在一個進程的生命周期中,都有各自的運行環境以及所需的資源,這些信息都記錄在各自的進程式控制制塊中,以便系統對這些進程進行有效的管理,進程式控制制塊的結構如下圖所示:
每個進程都有各自獨立的虛擬地址空間,空間的大小與所基於的硬體體系結構有關。虛擬空間中各區代表的意義,代碼段存儲指令序列和只讀數據,多個進程實例可共享代碼段。數據段用來存放全局變數和靜態變數。堆區域用於程序的動態內存管理,new或者malloc申請的內存就位於堆中。棧用來存放進程運行過程中的局部變數,函數返回地址,參數和進程上下文環境。
線程:引入進程是為了解決程序並發執行的問題,而引入線程是為了減少程序並發所帶來的時間和空間的開銷,線程是比進程更小的單位,一個進程至少有一個線程,線程是操作系統進行調度的基本單位,線程基本上不佔用系統資源,線程與其他同屬一個進程的線程共享該進程所佔有的資源。
linux是個系統,支持各種服務,服務可以開啟多進程,進程可以開啟多線程
『伍』 java內存機制的本地方法介面,本地方法棧,static靜態數據的問題
雖然 JVM 是來 Java 虛擬的計算機,自同樣有堆內存和棧內存。但 Java 程序運行時,供成員變數存儲的堆內存,以及供局部變數存儲的棧內存,都是由操作系統(如 Windows, Linux等)提供。而 JVM 的堆內存和棧內存,其作用是調用操作系統的內存或文件後進行模擬用的。 其實把JVM看成是個中間層就可以,不止是內存分配,還有線程、網路連接等等,最終在底層都要靠操作系統來搞。
首先內存總體分為了4個部分,包括 stack segment 、heap segment、code segment 、data segment ;
其中我們程序中用關鍵字new出來的東西都是存放在heap segment;
程序中的局部變數存放在stack segment,這些局部變數是在具體方法執行結束之後,系統自動釋放內存資源(而heap segment中的資源需要java垃圾回收機制來處理);
程序中的方法,是內存中的code segment中的,而且是多個對象 共享一個代碼空間區域;
static靜態變數,需要放在內存中的data segment中,
『陸』 linux系統中 初始化的全局變數和未初始化過的全局變數保存在哪
一個由C/C++編譯的程序佔用的內存分為以下幾個部分
1、棧區(stack)— 由編譯器自動回分配釋放 ,存放函答數的參數值,局部變數的值等。其
操作方式類似於數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回
收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static)—,全局變數和靜態變數的存儲是放在一塊的,初始化的
全局變數和靜態變數在一塊區域, 未初始化的全局變數和未初始化的靜態變數在相鄰的另
一塊區域。 - 程序結束後由系統釋放。
4、文字常量區 —常量字元串就是放在這里的。 程序結束後由系統釋放
5、程序代碼區—存放函數體的二進制代碼。
『柒』 linux下怎樣使用自己創建的一個靜態庫: 這個靜態庫中有一個全局變數和函數,會在庫外被調用,怎樣實現
先編譯源代碼生成這個靜態庫,如:libmyfunc.a,存放目錄為:./lib
在編譯執行碼時,增加以下參數:-L./lib -lmyfunc