Linux 內存佔用分析

這篇博客主要介紹 linux 環境下,查看內存佔用的兩種方式:使用 ps,top等命令;查看/proc/[pid]/下的文件。文章簡要介紹了命令的使用方法與一些參數意義,同時對/proc/[pid]/下的文件內容進行了一些詳細的介紹。文章內容來自google和自我總結,若有不當之處,歡迎批評指正。
node

查看 linux 中內存佔用的方法

linux 下面查看內存有多種渠道,好比經過命令 ps ,top,free, pmap 等,或者經過/proc系統。通常狀況下,ps,top,pmap,free能夠知足要求,若是須要比較詳細和精確地知道整機內存或者某個進程內存的使用狀況,能夠經過/proc 系統。linux

使用命令

free : 顯示系統可用內存以及已經使用的內存的信息
ps: 查看進程信息,靜態,即當前狀態
top: 查看進程信息,動態
pstree: 查看進程樹
pmap: 根據進程ID查看進程信息緩存

ps vs top

  1. ps命令–提供系統過去信息的一次性快照,也就是說ps命令可以查看剛剛系統的進程信息。
  2. top命令反應的是系統進程動態信息,默認10s更新一次。
  3. ps和top都是從/proc目錄下讀取進程的狀態信息,內核把當前系統進程的各類有用信息都放在這個僞目錄下。

常見ps命令:
ps -aux: 查看系統全部進程
ps -l: 進查看本身的bash相關進程安全

top 命令詳解,請參考http://www.jb51.net/article/40807.htm
基本命令:bash

  • 按鍵盤數字「1」,可監控每一個邏輯CPU的情況:
  • 鍵盤「b」(打開/關閉加亮效果)
  • 鍵盤「x」(打開/關閉排序列的加亮效果)
  • 」shift + >」或」shift + <」能夠向右或左改變排序列
  • 敲擊「f」鍵,編排基本視圖中的顯示字段
  • s,改變畫面更新頻率
  • l,關閉或開啓第一部分第一行 top 信息的表示
  • t,關閉或開啓第一部分第二行 Tasks 和第三行 Cpus 信息的表示
  • m,關閉或開啓第一部分第四行 Mem 和 第五行 Swap 信息的表示

具體使用方法,可使用 man [cmd] 查看。數據結構

相關參數說明

VSZ & VIRT多線程

  1. 進程使用的虛擬內存值總量,包括全部代碼,數據,共享庫已經被swapped out的。VIRT = SWAP + RES。
  2. VSZ來自ps命令, VIRT來自top命令,兩者均表示進程佔用的虛擬內存大小。
  3. 假如進程申請100m的內存,但實際只使用了10m,那麼它會增加100m,而不是實際的使用量

RES & RSSapp

  1. 進程當前使用的內存大小,但不包括swap out. RES = CODE +DATA。
  2. 包含其餘進程的共享
  3. RES 來自 top 命令, RSS 來自 ps 命令,二者在表示意義上沒有區別,都是從 /cat/proc/[pid]/stat 文件中讀取的信息。
  4. 若是申請100m的內存,實際使用10m,它只增加10m,與VIRT相反
  5. 關於庫佔用內存的狀況,它只統計加載的庫文件所佔內存大小

CODE
可執行代碼佔用的物理內存大小函數

DATAgoogle

  1. 物理內存中存放數據的大小,在程序運行中須要用到
  2. 若是top命令沒有顯示, 按f鍵顯示

SHR

  1. 共享內存大小
  2. 除了自身進程的共享內存,也包含其餘進程的共享內存
  3. 計算某個進程所佔用物理內存的大小: RES - SHR
  4. swap out後,該值會降低。

查看一個進程的內存信息步驟

  1. 獲取進程PID
 
    
1
2
 
    
$ ps -aux | grep /usr/sbin/NetworkManager
root 845 0.0 0.0 387084 13332 ? Ssl 3月28 0:00 /usr/sbin/NetworkManager --no-daemon
  1. 查看進程的全部線程

     
          
    1
    2
    3
    4
    5
    6
     
          
    $ ps mp 845 -o THREAD,tid
    USER %CPU PRI SCNT WCHAN USER SYSTEM TID
    root 0.0 - - - - - -
    root 0.0 19 - - - - 845
    root 0.0 19 - - - - 1025
    root 0.0 19 - - - - 1027
  2. 查看全部子進程

     
          
    1
    2
    3
    4
    5
     
          
    $ pstree -p 845
    NetworkManager(845)─┬─dhclient(30278)
    ├─dnsmasq(1123)
    ├─{gdbus}(1027)
    └─{gmain}(1025)

使用 /proc 下文件

/proc/[pid]/ 下面與進程內存相關的文件主要有maps , smaps, status
maps: 文件能夠查看某個進程的代碼段、棧區、堆區、動態庫、內核區對應的虛擬地址
smaps: 顯示每一個分區更詳細的內存佔用數據
status: 包含了全部CPU活躍的信息,該文件中的全部值都是從系統啓動開始累計到當前時刻

有名與匿名:
一個文件能夠映射到進程的一段內存區域中,映射的文件描述符保存在vm_area_struct->vm_file域中,這種內存區域叫作有名內存區域,相反,屬於匿名映射內存區域。

maps 文件分析

Proc/[pid]/maps 顯示進程映射了的內存區域和訪問權限。對應內核中的操做集爲 proc_pid_maps_op,具體的導出函數爲 show_map 。內核中進程的一段地址空間用一個vm_area_struct結構體表示,全部地址空間存儲在task->mm->mmap鏈表中。

截取一行內容以下:

 
    
1
 
    
7f4e3f5ca000-7f4e3f674000 r-xp 00000000 08:02 525202 /usr/lib/x86_64-linux-gnu/NetworkManager/libnm-device-plugin-wifi.so

Vm_area_struct每項對應解析以下表所示:

vm_area_struct項 maps項 含義
vm_start 「-」前一列,如7f4e3f5ca000 此段虛擬地址空間起始地址
vm_end 「-」後一列,如7f4e3f674000 此段虛擬地址空間結束地址
vm_flags 第三列,如r-xp 此段虛擬地址空間的屬性。r:讀,w:寫,x:執行,p和s共用一個字段,互斥關係,p表示私有段,s表示共享段,若是沒有相應權限,則用’-’代替
vm_pgoff 第四列,如00000000 對有名映射,表示此段虛擬內存起始地址在文件中以頁爲單位的偏移。對匿名映射,它等於0或者vm_start/PAGE_SIZE
vm_file->f_dentry->d_inode->i_sb->s_dev 第五列,如08:02 映射文件所屬設備號。對匿名映射l來講,由於沒有文件在磁盤上,因此沒有設備號,始終爲00:00。對有名映射來講,是映射的文件所在設備的設備號
vm_file->f_dentry->d_inode->i_ino 第六列,如525202 映射文件所屬節點號。對匿名映射來講,由於沒有文件在磁盤上,因此沒有節點號,始終爲00:00。對有名映射來講,是映射的文件的節點號
  第七列,如/lib/ld-2.5.so 對有名來講,是映射的文件名。對匿名映射來講,是此段虛擬內存在進程中的角色。[stack]表示在進程中做爲棧使用,[heap]表示堆。其他狀況則無顯示

maps文件只能顯示簡單的分區,smap文件能夠顯示每一個分區的更詳細的內存佔用數據。

smap 文件分析

截取一段文件,各字段解析以下:

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
    
7f148b2fa000-7f148b2fb000 rw-p 00026000 08:02 2883675 /lib/x86_64-linux-gnu/ld-2.23.so
Size: 4 kB 虛擬內存大小
Rss: 4 kB 實際使用物理內存大小 RSS = Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty
Pss: 4 kB RSS中私有的內存頁面
Shared_Clean: 0 kB RSS中共享內存,沒有被改寫的頁面
Shared_Dirty: 0 kB RSS中共享內存,被改寫的頁面
Private_Clean: 0 kB RSS中私有內存,未被改寫
Private_Dirty: 4 kB RSS中私有內存,被改寫
Referenced: 4 kB
Anonymous: 4 kB
AnonHugePages: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB 處於交換區的頁面大小
SwapPss: 0 kB
KernelPageSize: 4 kB 操做系統一個頁面大小
MMUPageSize: 4 kB 體系結構MMU一個頁面大小
Locked: 0 kB
VmFlags: rd wr mr mw me dw ac sd

Dirty頁面若是沒有交換機制的狀況下,應該是不能回收的。

分析腳本:
本身寫了個簡單的分析腳本,以下,能夠根據須要進行修改。

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
    
#! /bin/bash
awk 'BEGIN{
total = 0;
printf("SIZE\tRSS\tSHARED_CLEAN\tSHARED_DIRTY\tPRIVATE_CLEAN\tPRIVATE_DIRTY\n")
}{
if(NF >3){
if($2 ~ /[r-][w-][x-][ps]/){
if($6 =="")
name = $1;
else
name = $6;
}
}
while(getline)
{
if(NF >3){
if($2 ~ /[r-][w-][x-][ps]/){
if($6 =="")
name = $1;
else
name = $6;
}
}
if($1 ~ /^Size/){
size = $2;
total += $2;
}
if($1 ~ /Rss/){
rss = $2;
}
if($1 ~ /Shared_Clean/){
shared_clean = $2;
}
if($1 ~ /Shared_Dirty/){
shared_dirty = $2;
}
if($1 ~ /Private_Clean/){
private_clean = $2;
}
if($1 ~ /Private_Dirty/){
private_dirty = $2;
}
if($1 ~ /VmFlags/){
printf("%d\t%d\t%d\t%d\t%d\t%d\t%s\n",size,rss,shared_clean,shared_dirty,private_clean,private_dirty,name);
size = 0;
name = "";
rss = 0;
shared_clean = 0;
shared_dirty = 0;
private_clean = 0;
private_dirty = 0;
continue;
}
}
}END{
printf("====total: %d\n", total);
}' $1

關於匿名映射
smaps 中可能會存在大量的匿名區域,它們是使用 mmap 機制生成的,可是沒有關聯到任何一個文件。一般狀況下,它們主要用於處理一些瑣碎的任務,好比處理沒有在堆中申請的共享內存或者緩衝區。好比 pthread 使用匿名映射區做爲新線程的棧,在linux 下,pthread 爲 新線程申請 8M 空間做爲棧空間 已經一個小小片內存(如4Kb)用於檢測內存溢出。因此在每一個pthread建立時,會分配一個映射到節點0的8Mb內存,和一個映射到節點0的4Kb區域。

status 文件分析

截取文件,解析個字段以下:

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 
    
Develop>cat /proc/24475/status
Name: netio 可執行程序的名字
State: R (running) 任務狀態,運行/睡眠/僵死
Tgid: 24475 線程組號
Pid: 24475 進程id
PPid: 19635 父進程id
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256 該進程最大文件描述符個數
Groups: 0
VmPeak: 6330708 kB 內存使用峯值
VmSize: 268876 kB 進程虛擬地址空間大小
VmLck: 0 kB 進程鎖住的物理內存大小,鎖住的物理內存沒法交換到硬盤
VmHWM: 16656 kB
VmRSS: 11420 kB 進程正在使用的物理內存大小
VmData: 230844 kB 進程數據段大小
VmStk: 136 kB 進程用戶態棧大小
VmExe: 760 kB 進程代碼段大小
VmLib: 7772 kB 進程使用的庫映射到虛擬內存空間的大小
VmPTE: 120 kB 進程頁表大小
VmSwap: 0 kB
Threads: 5 共享使用該信號描述符的任務的個數,在POSIX多線程序應用程序中,線程組中的全部線程使用同一個信號描述符。
SigQ: 0/63346 待處理信號的個數
SigPnd: 0000000000000000 屏蔽位,存儲了該線程的待處理信號
ShdPnd: 0000000000000000 屏蔽位,存儲了該線程組的待處理信號
SigBlk: 0000000000000000 存放被阻塞的信號
SigIgn: 0000000001000000 存放被忽略的信號
SigCgt: 0000000180000000 存放被俘獲到的信號
CapInh: 0000000000000000 能被當前進程執行的程序的繼承的能力
CapPrm: ffffffffffffffff 進程可以使用的能力,能夠包含CapEff中沒有的能力,這些能力是被進程本身臨時放棄的,CapEff是CapPrm的一個子集,進程放棄沒有必要的能力有利於提升安全性
CapEff: ffffffffffffffff 進程的有效能力
CapBnd: ffffffffffffffff
Cpus_allowed: 01
Cpus_allowed_list: 0
Mems_allowed: 01
Mems_allowed_list: 0
voluntary_ctxt_switches: 201
nonvoluntary_ctxt_switches: 909

meminfo 文件分析

整機內存使用狀況的文件/proc/meminfo,摘取分析以下

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 
    
Develop>cat /proc/meminfo
MemTotal: 8112280 kB 全部可用RAM大小 (即物理內存減去一些預留位和內核的二進制代碼大小)
MemFree: 4188636 kB LowFree與HighFree的總和,被系統留着未使用的內存
Buffers: 34728 kB 用來給文件作緩衝大小
Cached: 289740 kB 被高速緩衝存儲器(cache memory)用的內存的大小
(等於 diskcache minus SwapCache )
SwapCached: 0 kB 被高速緩衝存儲器(cache memory)用的交換空間的大小
已經被交換出來的內存,但仍然被存放在swapfile中。
用來在須要的時候很快的被替換而不須要再次打開I/O端口
Active: 435240 kB 在活躍使用中的緩衝或高速緩衝存儲器頁面文件的大小,
除非很是必要不然不會被移做他用
Inactive: 231512 kB 在不常用中的緩衝或高速緩衝存儲器頁面文件的大小,可能被用於其餘途徑.
Active(anon): 361252 kB
Inactive(anon): 120688 kB
Active(file): 73988 kB
Inactive(file): 110824 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB 交換空間的總大小
SwapFree: 0 kB 未被使用交換空間的大小
Dirty: 0 kB 等待被寫回到磁盤的內存大小
Writeback: 0 kB 正在被寫回到磁盤的內存大小
AnonPages: 348408 kB 未映射頁的內存大小
Mapped: 33600 kB 已經被設備和文件等映射的大小
Shmem: 133536 kB
Slab: 55984 kB 內核數據結構緩存的大小,能夠減小申請和釋放內存帶來的消耗
SReclaimable: 25028 kB 可收回Slab的大小
SUnreclaim: 30956 kB 不可收回Slab的大小(SUnreclaim+SReclaimable=Slab)
KernelStack: 1896 kB 內核棧區大小
PageTables: 8156 kB 管理內存分頁頁面的索引表的大小
NFS_Unstable: 0 kB 不穩定頁表的大小
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2483276 kB
Committed_AS: 1804104 kB
VmallocTotal: 34359738367 kB 能夠vmalloc虛擬內存大小
VmallocUsed: 565680 kB 已經被使用的虛擬內存大小
VmallocChunk: 34359162876 kB
HardwareCorrupted: 0 kB
HugePages_Total: 1536 大頁面數目
HugePages_Free: 0 空閒大頁面數目
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB 大頁面一頁大小
DirectMap4k: 10240 kB
DirectMap2M: 8302592 kB

總結

linux下內存佔用是一個比較複雜的概念,不能簡單經過一個單一指標就判斷某個程序「內存消耗」大小,由於進程所申請的內存不必定真正會被用到(malloc或mmap的實現)並且真正用到了的內存也不必定是隻有該進程本身在用 (好比動態共享庫)。咱們應該根據具體需求選擇合適的方式去分析內存佔用。