Linux內存使用情況以及內存泄露情況


1. 內存使用情況分析

http://www.360doc.com/content/15/1118/13/17283_514054063.shtml

https://www.linuxidc.com/Linux/2016-04/130069.htm

1.1 系統總內存分析

通過cat /proc/meminfo,可用的物理內存=MemFree+Buffers+Cached。

MemTotal:        5933132 kB
MemFree:         4485932 kB
MemAvailable:    4822944 kB
Buffers:          122148 kB
Cached:           630048 kB
SwapCached:            0 kB
Active:           806136 kB
Inactive:         461288 kB
Active(anon):     516344 kB
Inactive(anon):   230112 kB
Active(file):     289792 kB
Inactive(file):   231176 kB
Unevictable:          32 kB
Mlocked:              32 kB
SwapTotal:       7999484 kB
SwapFree:        7999484 kB
Dirty:               204 kB
Writeback:             0 kB
AnonPages:        515264 kB

echo 3 > /proc/sys/vm/drop_caches,會清理系統的cache。

Writing to this will cause the kernel to drop clean caches, dentries and inodes from memory, causing that memory to become free.

1-to free pagecache, 2-to free dentries and inodes, 3-to free pagecache, dentries and inodes。

1.2 進程內存分析

cat /proc/{pid}/maps

2.1 內存泄露類型

1. 常發性內存泄漏
發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏

2. 偶發性內存泄漏
發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要

3. 一次性內存泄漏
發生內存泄漏的代碼只會被執行一次,或者由於算法上的缺陷,導致總會有一塊且僅有一塊內存發生泄漏

4. 隱式內存泄漏
程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里並沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但是對於一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏

2.2 常用內存泄露檢測工具

C/C++
1. Valgrind: Debugging and profiling Linux programs, aiming at programs written in C and C++ 
2. ccmalloc: Linux和Solaris下對C和C++程序的簡單的使用內存泄漏和malloc調試庫 
3. LeakTracer: Linux、Solaris和HP-UX下跟蹤和分析C++程序中的內存泄漏 
4. Electric Fence: Linux分發版中由Bruce Perens編寫的malloc()調試庫 
5. Leaky: Linux下檢測內存泄漏的程序 
6. Dmalloc: Debug Malloc Library 
7. MEMWATCH: 由Johan Lindh編寫,是一個開放源代碼C語言內存錯誤檢測工具,主要是通過gcc的precessor來進行 
8. KCachegrind: A visualization tool for the profiling data generated by Cachegrind and Calltree 

2.3 內存檢測原理

3.1 Linux內核內存泄漏檢測kmemleak

kmemlean提供了一種檢測內核內存泄露的方法,當內存對象沒有被釋放是,將其記錄在/sys/kernel/debug/kmemleak中。

3.1.1 使能kmemleak

CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
# CONFIG_DEBUG_KMEMLEAK_TEST is not set
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n----------默認打開

如果沒有打開KMEMLEAK_FULL,則使用了精簡版。完整版比較耗內存,精簡版適合嵌入式調試。

3.1.2 配置獲取kmemleak結果

參數配置:

off 禁用kmemleak(不可逆)
stack=on 啟用任務堆棧掃描(default)
stack=off 禁用任務堆棧掃描
scan=on 啟動自動記憶掃描線程(default)
scan=off 停止自動記憶掃描線程
scan=<secs> 設置n秒內自動記憶掃描
scan 開啟內核掃描
clear 清除內存泄露報告
dump=<addr> 轉存信息對象在<addr>
通過“kmemleak = OFF”,也可以在啟動時禁用Kmemleak在內核命令行。在初始化kmemleak之前,內存的分配或釋放這些動作被存儲在一個前期日志緩沖區。這個緩沖區的大小通過配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE設置。 
cat /sys/kernel/debug/kmemleak > kmemleak.txt

3.1.3 分析kmemleak泄漏情況

參考文檔:

kmemleak的使用

Linux Kernel Memory Leak Detection

Linux memory leak detection

3.2 valgrind

Linux C/C++ Memory Leak Detection Tool

Linux 下幾款程序內存泄漏檢查工具

Linux下幾款C++程序中的內存泄露檢查工具

Linux 內存泄露檢測技巧

應用 Valgrind 發現 Linux 程序的內存問題

 

查看程序內存空間兩種方法

一、查看/proc/{pid}/maps文件

 

二、pmap命令,原理上是一樣的

[root@info ~]# pmap 1013

 

什么是內存泄漏
內存泄漏是指程序動態申請的內存在使用完后沒有釋放,導致這段內存不能被操作系統回收再利用。
例如這段程序,申請了4個字節的空間但沒有釋放,有4個字節的內存泄漏。

#include <iostream>
using namespace std;

int main()
{
int *p = new int(1);
cout <<*p<<endl;
return 0
}

隨着時間的推移,泄漏的內存越來越多,可用的內存越來越少,輕則性能受損,重則系統崩潰。

一般情況下,發生內存泄漏時,重啟就可以回收泄漏的內存。但是對於linux,通常跑的是服務器程序,不可以隨意重啟,在內存泄漏問題上就要格外小心。

內存泄漏特點
難復現 — 要運行到足夠長的時間才會暴露。

難定位 — 出錯位置是隨機的,看不出與內存泄漏的代碼有什么聯系。

最簡單的方法
為了避免寫出內存泄漏的程序,通常會有這樣的編程規范,要求我們在寫程序時申請和釋放成對出現的。因為每一次申請都意味着必須有一次釋放與它相對應。

基於這個特點,一種簡單的方法就是在代碼中統計申請和釋放的次數,如果申請和釋放的數量不同,就認為是內存泄漏了。

#include "stdio.h"
#include "stdlib.h"

int malloc_count, free_count;

void * my_malloc(int size)
{
malloc_count++;
return malloc(size);
}
void my_free(void *p)
{
free_count++;
free(p);
}
int main()
{
count = 0;
int *p1 = (int *)my_malloc(sizeif(int))
int *p2 = (int *)my_malloc(sizeif(int))
printf("%d, %d", p1, p2);
my_free(p1);
if(malloc_count != free_count)
printf("memory leak!\n");
return 0
}

方法分析
優點:
直觀,容易理解,容易實現

缺點:
1.該方法要求運行結束時對運行中產生的打印分析才能知道結果。

2.該方法要求封裝所有申請和釋放空間的函數,並在調用的地方修改成調用封裝后的函數。雖然C中申請/釋放內存接口並不多,但是對於一個大型的項目,調用這些接口的地方卻是很多的,要全部替換是一個比較大的工作量。

3.只對C語言適用,不能應用於C++

4.對於所調用的庫不適用。如果希望應用於庫,則要修改庫代碼

5.只能檢測是否泄漏,卻沒有具體信息,比如泄漏了多少空間

6.不能說明是哪一行代碼引起了泄漏

 

Linux檢測程序內存泄漏

1.安裝valgrind:
  這是一款開源的程序內存檢測工具,mtrace為內存分配函數(malloc, realloc, memalign,free)安裝hook函數。這些hook函數記錄內存的申請和釋放的trace信息。

Valgrind詳解:

 

Valgrind包括以下一些工具:
1.Memcheck:這是valgrind應用最廣泛的工具,一個重量級的內存檢查器,能夠給發現開發中絕大多數的內存錯誤使用的情況,比如:使用未初始化
2.callgrind:它主要用來檢查程序中函數中調用過程中出現的問題
3.cachegrind:它主要用來檢查程序中緩存使用出現的問題
4.Helgrind:它主要用來檢查多線程中出現的競爭問題
5.Massif:它主要用來檢查程序中堆棧使用中出現的問題
6.Extension:可以使用core提供的 功能,自己編寫特定的內存調試工具
2.mtrace命令

man 3 mtrace 可以在man 手冊中查看該函數


mtrace 也有對應的命令,其使用方式為:
一.將環境變量MALLOC_TRACE設置為所需輸出文件的路徑名

 

二.在需要檢測的源代碼中引入mcheck.h頭文件
1.在分配內存之前調用mtrace(); ,一般在main函數的開頭調用
2.在結束檢測的地方調用muntrace(); ,一般在return之前調用
3.編譯程序時需要加上-g 選項
#include <stdio.h>
#include <malloc.h>
#include <mcheck.h>
int main()
{
mtrace();
int *p = NULL;
p =(int *)malloc(sizeof(int) * 1);
//free(p);//未free,內存泄漏
muntrace();
return 0;
}


4.內存泄漏信息將在MALLOC_TRACE環境變量指定的文件中報告,需要使用mtrace命令將信息轉換。

Valgrind的最新版是3.11.0,它一般包含下列工具: 

1.Memcheck 

    最常用的工具,用來檢測程序中出現的內存問題,所有對內存的讀寫都會被檢測到,一切對malloc()/free()/new/delete的調用都會被捕獲。所以,它能檢測以下問題: 

    對未初始化內存的使用; 

    讀/寫釋放后的內存塊; 

    讀/寫超出malloc分配的內存塊; 

    讀/寫不適當的棧中內存塊; 

    內存泄漏,指向一塊內存的指針永遠丟失; 

    不正確的malloc/free或new/delete匹配; 

    memcpy()相關函數中的dst和src指針重疊。 

2.Callgrind 

    和gprof類似的分析工具,但它對程序的運行觀察更是入微,能給我們提供更多的信息。和gprof不同,它不需要在編譯源代碼時附加特殊選項,但加上調試選項是推薦的。Callgrind收集程序運行時的一些數據,建立函數調用關系圖,還可以有選擇地進行cache模擬。在運行結束時,它會把分析數據寫入一個文件。callgrind_annotate可以把這個文件的內容轉化成可讀的形式。 

3.Cachegrind 

    Cache分析器,它模擬CPU中的一級緩存I1,Dl和二級緩存,能夠精確地指出程序中cache的丟失和命中。如果需要,它還能夠為我們提供cache丟失次數,內存引用次數,以及每行代碼,每個函數,每個模塊,整個程序產生的指令數。這對優化程序有很大的幫助。 

4.Helgrind 

    它主要用來檢查多線程程序中出現的競爭問題。Helgrind尋找內存中被多個線程訪問,而又沒有一貫加鎖的區域,這些區域往往是線程之間失去同步的地方,而且會導致難以發掘的錯誤。Helgrind實現了名為“Eraser”的競爭檢測算法,並做了進一步改進,減少了報告錯誤的次數。不過,Helgrind仍然處於實驗階段。 

5.Massif 

    堆棧分析器,它能測量程序在堆棧中使用了多少內存,告訴我們堆塊,堆管理塊和棧的大小。Massif能幫助我們減少內存的使用,在帶有虛擬內存的現代系統中,它還能夠加速我們程序的運行,減少程序停留在交換區中的幾率。 

此外,lackey和nulgrind也會提供。Lackey是小型工具,很少用到;Nulgrind只是為開發者展示如何創建一個工具。

1.3 原理

Memcheck 能夠檢測出內存問題,關鍵在於其建立了兩個全局表。Valid-Value 表

對於進程的整個地址空間中的每一個字節(byte),都有與之對應的 8 個 bits;對於CPU的每個寄存器,也有一個與之對應的bit向量。這些bits負責記錄該字節或者寄存器值是否具有有效的、已初始化的值。

Valid-Address 表

對於進程整個地址空間中的每一個字節(byte),還有與之對應的1個bit,負責記錄該地址是否能夠被讀寫。

檢測原理:

當要讀寫內存中某個字節時,首先檢查這個字節對應的 A bit。如果該A bit顯示該位置是無效位置,memcheck則報告讀寫錯誤。

內核(core)類似於一個虛擬的 CPU 環境,這樣當內存中的某個字節被加載到真實的 CPU 中時,該字節對應的 V bit 也被加載到虛擬的 CPU 環境中。一旦寄存器中的值,被用來產生內存地址,或者該值能夠影響程序輸出,則 memcheck 會檢查對應的V bits,如果該值尚未初始化,則會報告使用未初始化內存錯誤。

2 安裝使用

2.1安裝

從官網http://www.valgrind.org下載最新版本(當前3.11)

#tar xvf valgrind-3.11.1.tar.bz2
#cd valgrind-3.11.1
#./configure --prefix=/usr/local/valgrind--指定安裝目錄
#make
#make install

2.2 命令介紹

用法:valgrind[options] prog-and-args [options]: 常用選項,適用於所有Valgrind工具

  1. -tool=<name> 最常用的選項。運行 valgrind中名為toolname的工具。默認memcheck。
  2. h –help 顯示幫助信息。
  3. -version 顯示valgrind內核的版本,每個工具都有各自的版本。
  4. q –quiet 安靜地運行,只打印錯誤信息。
  5. v –verbose 更詳細的信息, 增加錯誤數統計。
  6. -trace-children=no|yes 跟蹤子線程? [no]
  7. -track-fds=no|yes 跟蹤打開的文件描述?[no]
  8. -time-stamp=no|yes 增加時間戳到LOG信息? [no]
  9. -log-fd=<number> 輸出LOG到描述符文件 [2=stderr]
  10. -log-file=<file> 將輸出的信息寫入到filename.PID的文件里,PID是運行程序的進行ID
  11. -log-file-exactly=<file> 輸出LOG信息到 file
  12. -log-file-qualifier=<VAR> 取得環境變量的值來做為輸出信息的文件名。 [none]
  13. -log-socket=ipaddr:port 輸出LOG到socket ,ipaddr:port

LOG信息輸出:

  1. -xml=yes 將信息以xml格式輸出,只有memcheck可用
  2. -num-callers=<number> show <number> callers in stack traces [12]
  3. -error-limit=no|yes 如果太多錯誤,則停止顯示新錯誤? [yes]
  4. -error-exitcode=<number> 如果發現錯誤則返回錯誤代碼 [0=disable]
  5. -db-attach=no|yes 當出現錯誤,valgrind會自動啟動調試器gdb。[no]
  6. -db-command=<command> 啟動調試器的命令行選項[gdb -nw %f %p]

適用於Memcheck工具的相關選項:

    1. -leak-check=no|summary|full 要求對leak給出詳細信息? [summary]
    2. -leak-resolution=low|med|high how much bt merging in leak check [low]
    3. -show-reachable=no|yes show reachable blocks in leak check? [no]

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com