linux內存佔用分析之meminfo

1、背景

近期在公司的某臺linux虛擬機上,發現內存幾乎消耗殆盡,但找不到其去向。
在調查過程當中,重點分析了/proc/meminfo文件,對其內存佔用進行了學習與分析。java

特記錄在此,與君分享。mysql

2、環境

  • 虛擬機OS : CentOS Linux release 7.4.1708 (Core)
  • 虛擬機平臺 : VMWare

3、問題描述

經過free -htop查看內存消耗,發現used已接近最大可用內存,但各進程常駐內存(RES)遠比used要小。linux

先擺出結論:

在VMWare虛擬機平臺上,宿主機能夠經過一個叫Balloon driver(vmware_balloon module)的驅動程序模擬客戶機linux內部的進程佔用內存,被佔用的內存其實是被宿主機調度到其餘客戶機去了。
但這種驅動程序模擬的客戶機進程在linux上的內存動態分配並無被linux內核統計進來,因而形成了上述問題的現象。算法

3.1 top 結果

按內存消耗排序,取消耗大於0的部分sql

top - 16:46:45 up 8 days, 10:25,  1 user,  load average: 0.00, 0.01, 0.05
Tasks: 109 total,   1 running, 108 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.0 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7994080 total,   185776 free,  7625996 used,   182308 buff/cache
KiB Swap:  4157436 total,   294944 free,  3862492 used.   115964 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND               DATA
  3725 root      20   0 9057140 1.734g   5020 S   0.3 22.7 367:48.86 java               8882680
  1087 mysql     20   0 2672240 233064   1076 S   0.0  2.9 102:33.71 mysqld             2596840
   496 root      20   0   36828   3512   3388 S   0.0  0.0   0:31.13 systemd-journal        356
 14564 root      20   0  145700   2424   1148 S   0.0  0.0   0:02.94 sshd                   924
     1 root      20   0  128164   2404    724 S   0.0  0.0   1:02.18 systemd              84628
 14713 root      20   0  157716   2204   1512 R   0.0  0.0   0:00.08 top                   1176
 14568 root      20   0  115524   1784   1272 S   0.0  0.0   0:00.59 bash                   632
   687 root      20   0  305408   1548   1168 S   0.0  0.0  13:59.34 vmtoolsd             75352
   676 root      20   0  216388   1240    872 S   0.0  0.0   1:56.69 rsyslogd            148768
   682 root      20   0  472296    908    160 S   0.0  0.0   1:06.73 NetworkManager      222852
   684 root      20   0   24336    752    444 S   0.0  0.0   0:22.19 systemd-logind         504
   690 polkitd   20   0  534132    560    220 S   0.0  0.0   0:07.34 polkitd             450080
   677 dbus      20   0   32772    460    128 S   0.0  0.0   0:08.34 dbus-daemon           8900
   688 root      20   0   21620    452    296 S   0.0  0.0   4:42.68 irqbalance             488
   698 root      20   0  126232    432    328 S   0.0  0.0   0:30.25 crond                 1312
   922 root      20   0  562392    412     28 S   0.0  0.0   4:52.69 tuned               304472
   924 root      20   0  105996    188     92 S   0.0  0.0   0:03.64 sshd                   760
   653 root      16  -4   55452     84      0 S   0.0  0.0   0:08.81 auditd                8664
   532 root      20   0   46684      4      4 S   0.0  0.0   0:02.81 systemd-udevd         1916
   705 root      20   0  110044      4      4 S   0.0  0.0   0:00.02 agetty                 344

3.2 top結果第四行內存整體使用狀況

屬性 大小(G) 說明
total 7.6 可分配內存總計值
used 7.26 已分配內存
free 0.17 未分配內存
buff/cache 0.17 buff與緩存
  • 各屬性知足公式:total = used + free + buff/cache
  • used在該linux版本(centos7)上,已經反映實際分配的內存,不須要再去除buff/cache部分

3.3 top進程列表內存相關列統計

合計(G) 說明
VIRT 14.236 進程申請的虛擬內存大小,申請不意味着分配,該值與實際內存消耗關係不大。
RES 1.9747 進程常駐內存,包含進程間共享內存。
SHR 0.0171 進程間共享內存,該值是推算出來的,存在偏差,意義不大。

3.4 問題來了

RES合計值比used少了5G多!這些內存哪去了?數據庫

理論上,各進程的RES合計值由於會重複計算共享內存,應該比used值略大。實際上這兩個值也每每是接近的,不該該差這麼多。

4、清查linux內存消耗

爲了進一步檢查linux中內存消耗的去向,須要對/proc/meminfo文件進行一次完全的分析統計。centos

linux上各類內存查看工具如free,top實際上都是從/proc下面找linux內核的各類統計文件。

4.1 /proc/meminfo內容

MemTotal:        7994080 kB
MemFree:          125256 kB
MemAvailable:     932412 kB
Buffers:               8 kB
Cached:           993796 kB
SwapCached:          252 kB
Active:          1182220 kB
Inactive:        1213960 kB
Active(anon):     693796 kB
Inactive(anon):   717156 kB
Active(file):     488424 kB
Inactive(file):   496804 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       4157436 kB
SwapFree:        4157172 kB
Dirty:                 8 kB
Writeback:             0 kB
AnonPages:       1402140 kB
Mapped:            41584 kB
Shmem:              8576 kB
Slab:             143220 kB
SReclaimable:      86720 kB
SUnreclaim:        56500 kB
KernelStack:        5360 kB
PageTables:         7184 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     8154476 kB
Committed_AS:    2073776 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      191584 kB
VmallocChunk:   34359310332 kB
HardwareCorrupted:     0 kB
AnonHugePages:   1284096 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       89920 kB
DirectMap2M:     4104192 kB
DirectMap1G:     6291456 kB

4.2 meminfo內容分析

屬性 大小(k) 說明 擴展說明
MemTotal: 7994080 可供linux內核分配的內存總量。 比物理內存總量少一點,由於主板/固件會保留一部份內存、linux內核本身也會佔用一部份內存。
MemFree: 125256 表示系統還沒有分配的內存。
MemAvailable: 932412 當前可用內存。 MemFree只是還沒有分配的內存,並非全部可用的內存。有些已經分配掉的內存是能夠回收再分配的。好比cache/buffer、slab都有一部分是能夠回收的,這部分可回收的內存加上MemFree纔是系統可用的內存,即MemAvailable。同時要注意,MemAvailable是內核使用特定的算法估算出來的,並不精確。
Buffers: 8 塊設備(block device)所佔用的特殊file-backed pages,包括:直接讀寫塊設備,以及文件系統元數據(metadata)好比superblock使用的緩存頁。 Buffers內存頁同時也在LRU list中,被統計在Active(file)或Inactive(file)之中。
Cached: 993796 全部file-backed pages 用戶進程的內存頁分爲兩種:file-backed pages(與文件對應的內存頁),和anonymous pages(匿名頁),好比進程的代碼、映射的文件都是file-backed,而進程的堆、棧都是不與文件相對應的、就屬於匿名頁。file-backed pages在內存不足的時候能夠直接寫回對應的硬盤文件裏,稱爲page-out,不須要用到交換區(swap);而anonymous pages在內存不足時就只能寫到硬盤上的交換區(swap)裏,稱爲swap-out。
SwapCached: 252 SwapCached包含的是被肯定要swap-out,可是還沒有寫入交換區的匿名內存頁。 SwapCached內存頁會同時被統計在LRU或AnonPages或Shmem中,它自己並不佔用額外的內存。
Active: 1182220 active包含active anon和active file LRU是一種內存頁回收算法,Least Recently Used,最近最少使用。LRU認爲,在最近時間段內被訪問的數據在之後被再次訪問的機率,要高於最近一直沒被訪問的頁面。因而近期未被訪問到的頁面就成爲了頁面回收的第一選擇。Linux kernel會記錄每一個頁面的近期訪問次數,而後設計了兩種LRU list: active list 和 inactive list, 剛訪問過的頁面放進active list,長時間未訪問過的頁面放進inactive list,回收內存頁時,直接找inactive list便可。另外,內核線程kswapd會週期性地把active list中符合條件的頁面移到inactive list中。
Inactive: 1213960 inactive包含inactive anon和inactive file
Active(anon): 693796 活躍匿名頁,anonymous pages(匿名頁)。
Inactive(anon): 717156 非活躍匿名頁
Active(file): 488424 活躍文件內存頁
Inactive(file): 496804 非活躍文件內存頁
Unevictable: 0 由於種種緣由沒法回收(page-out)或者交換到swap(swap-out)的內存頁 Unevictable LRU list上是不能pageout/swapout的內存頁,包括VM_LOCKED的內存頁、SHM_LOCK的共享內存頁(同時被統計在Mlocked中)、和ramfs。在unevictable list出現以前,這些內存頁都在Active/Inactive lists上,vmscan每次都要掃過它們,可是又不能把它們pageout/swapout,這在大內存的系統上會嚴重影響性能,unevictable list的初衷就是避免這種狀況的發生。
Mlocked: 0 被系統調用"mlock()"鎖定到內存中的頁面。Mlocked頁面是不可收回的。 被鎖定的內存由於不能pageout/swapout,會從Active/Inactive LRU list移到Unevictable LRU list上。Mlocked與如下統計項重疊:LRU Unevictable,AnonPages,Shmem,Mapped等。
SwapTotal: 4157436 swap空間總計
SwapFree: 4157172 當前剩餘swap
Dirty: 8 須要寫入磁盤的內存頁的大小 Dirty並不包括系統中所有的dirty pages,須要再加上另外兩項:NFS_Unstable 和 Writeback,NFS_Unstable是發給NFS server但還沒有寫入硬盤的緩存頁,Writeback是正準備回寫硬盤的緩存頁。
Writeback: 0 正在被寫回的內存頁的大小
AnonPages: 1402140 Anonymous pages(匿名頁)數量 + AnonHugePages(透明大頁)數量 進程所佔的內存頁分爲anonymous pages和file-backed pages,理論上,全部進程的PSS之和 = Mapped + AnonPages。PSS是Proportional Set Size,每一個進程實際使用的物理內存(比例分配共享庫佔用的內存),能夠在/proc/[1-9]*/smaps中查看。
Mapped: 41584 正被用戶進程關聯的file-backed pages Cached包含了全部file-backed pages,其中有些文件當前不在使用,但Cached仍然可能保留着它們的file-backed pages;而另外一些文件正被用戶進程關聯,好比shared libraries、可執行程序的文件、mmap的文件等,這些文件的緩存頁就稱爲mapped。
Shmem: 8576 Shmem統計的內容包括:1.shared memory;2.tmpfs和devtmpfs。全部tmpfs類型的文件系統佔用的空間都計入共享內存,devtmpfs是/dev文件系統的類型,/dev/下全部的文件佔用的空間也屬於共享內存。能夠用ls和du命令查看。若是文件在沒有關閉的狀況下被刪除,空間仍然不會釋放,shmem不會減少,能夠用 lsof -a +L1 /<mount_point> 命令列出這樣的文件。 shared memory被視爲基於tmpfs文件系統的內存頁,既然基於文件系統,就不算匿名頁,因此不被計入/proc/meminfo中的AnonPages,而是被統計進了:CachedMapped(當shmem被attached時候)。然而它們背後並不存在真正的硬盤文件,一旦內存不足的時候,它們是須要交換區才能swap-out的,因此在LRU lists裏,它們被放在Inactive(anon)Active(anon)unevictable (若是被locked的話)裏。注意:/proc/meminfo中的 Shmem 統計的是已經分配的大小,而不是建立時申請的大小。
Slab: 143220 經過slab分配的內存,Slab=SReclaimable+SUnreclaim slab是linux內核的一種內存分配器。linux內核的動態內存分配有如下幾種方式:1.alloc_pages/__get_free_page:以頁爲單位分配。2.vmalloc:以字節爲單位分配虛擬地址連續的內存塊。3.slab:對小對象進行分配,不用爲每一個小對象分配一個頁,節省了空間;內核中一些小對象建立析構很頻繁,Slab對這些小對象作緩存,能夠重複利用一些相同的對象,減小內存分配次數。4.kmalloc:以slab爲基礎,以字節爲單位分配物理地址連續的內存塊。
SReclaimable: 86720 slab中可回收的部分。
SUnreclaim: 56500 slab中不可回收的部分。
KernelStack: 5360 給用戶線程分配的內核棧消耗的內存頁 每個用戶線程都會分配一個kernel stack(內核棧),內核棧雖然屬於線程,但用戶態的代碼不能訪問,只有經過系統調用(syscall)、自陷(trap)或異常(exception)進入內核態的時候纔會用到,也就是說內核棧是給kernel code使用的。在x86系統上Linux的內核棧大小是固定的8K或16K。Kernel stack(內核棧)是常駐內存的,既不包括在LRU lists裏,也不包括在進程的RSS/PSS內存裏。RSS是Resident Set Size 實際使用物理內存(包含共享庫佔用的內存),能夠在/proc/[1-9]*/smaps中查看。
PageTables: 7184 Page Table的消耗的內存頁 Page Table的用途是翻譯虛擬地址和物理地址,它是會動態變化的,要從MemTotal中消耗內存。
NFS_Unstable: 0 發給NFS server但還沒有寫入硬盤的緩存頁
Bounce: 0 bounce buffering消耗的內存頁 有些老設備只能訪問低端內存,好比16M如下的內存,當應用程序發出一個I/O 請求,DMA的目的地址倒是高端內存時(好比在16M以上),內核將在低端內存中分配一個臨時buffer做爲跳轉,把位於高端內存的緩存數據複製到此處。
WritebackTmp: 0 正準備回寫硬盤的緩存頁
CommitLimit: 8154476 overcommit閾值,CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap Linux是容許memory overcommit的,即承諾給進程的內存大小超過了實際可用的內存。commit(或overcommit)針對的是內存申請,內存申請不等於內存分配,內存只在實際用到的時候才分配。但能夠申請的內存有個上限閾值,即CommitLimit,超出之後就不能再申請了。
Committed_AS: 2073776 全部進程已經申請的內存總大小
VmallocTotal: 34359738367 可分配的虛擬內存總計
VmallocUsed: 191584 已經過vmalloc分配的內存,不止包括了分配的物理內存,還統計了VM_IOREMAP、VM_MAP等操做的值 VM_IOREMAP是把IO地址映射到內核空間、並未消耗物理內存
VmallocChunk: 34359310332 經過vmalloc可分配的虛擬地址連續的最大內存
HardwareCorrupted: 0 由於內存的硬件故障而刪除的內存頁
AnonHugePages: 1284096 AnonHugePages統計的是Transparent HugePages (THP),THP與Hugepages不是一回事,區別很大。Hugepages在/proc/meminfo中是被獨立統計的,與其它統計項不重疊,既不計入進程的RSS/PSS中,又不計入LRU Active/Inactive,也不會計入cache/buffer。若是進程使用了Hugepages,它的RSS/PSS不會增長。而AnonHugePages徹底不一樣,它與/proc/meminfo的其餘統計項有重疊,首先它被包含在AnonPages之中,並且在/proc/<pid>/smaps中也有單個進程的統計,與進程的RSS/PSS是有重疊的,若是用戶進程用到了THP,進程的RSS/PSS也會相應增長,這與Hugepages是不一樣的。 Transparent Huge Pages 縮寫 THP ,這個是 RHEL 6 開始引入的一個功能,在 Linux6 上透明大頁是默認啓用的。因爲 Huge pages 很難手動管理,並且一般須要對代碼進行重大的更改纔能有效的使用,所以 RHEL 6 開始引入了 Transparent Huge Pages ( THP ), THP 是一個抽象層,可以自動建立、管理和使用傳統大頁。THP 爲系統管理員和開發人員減小了不少使用傳統大頁的複雜性 , 由於 THP 的目標是改進性能 , 所以其它開發人員 ( 來自社區和紅帽 ) 已在各類系統、配置、應用程序和負載中對 THP 進行了測試和優化。這樣可以讓 THP 的默認設置改進大多數系統配置性能。可是 , 不建議對數據庫工做負載使用 THP 。這二者最大的區別在於 : 標準大頁管理是預分配的方式,而透明大頁管理則是動態分配的方式。
HugePages_Total: 0 預分配的可以使用的標準大頁池的大小。HugePages在內核中獨立管理,只要一經定義,不管是否被使用,都再也不屬於free memory。 Huge pages(標準大頁) 是從 Linux Kernel 2.6 後被引入的,目的是經過使用大頁內存來取代傳統的 4kb 內存頁面, 以適應愈來愈大的系統內存,讓操做系統能夠支持現代硬件架構的大頁面容量功能。
HugePages_Free: 0 標準大頁池中還沒有分配的標準大頁
HugePages_Rsvd: 0 用戶程序預申請的標準大頁,還沒有真的分配走
HugePages_Surp: 0 標準大頁池的盈餘
Hugepagesize: 2048 標準大頁大小,這裏是2M
DirectMap4k: 89920 映射爲4kB的內存數量 DirectMap所統計的不是關於內存的使用,而是一個反映TLB效率的指標。TLB(Translation Lookaside Buffer)是位於CPU上的緩存,用於將內存的虛擬地址翻譯成物理地址,因爲TLB的大小有限,不能緩存的地址就須要訪問內存裏的page table來進行翻譯,速度慢不少。爲了儘量地將地址放進TLB緩存,新的CPU硬件支持比4k更大的頁面從而達到減小地址數量的目的, 好比2MB,4MB,甚至1GB的內存頁,視不一樣的硬件而定。因此DirectMap實際上是一個反映TLB效率的指標。
DirectMap2M: 4104192 映射爲2MB的內存數量
DirectMap1G: 6291456 映射爲1GB的內存數量

4.3 根據meminfo統計內存佔用

linux上的內存消耗總的來講有兩部分,一部分是內核kernel進程,另外一部分是用戶進程。所以咱們統計這兩部分進程的內存消耗再相加便可。緩存

  • 內核部分:Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X
X是指linux內核沒有統計進來的,動態內存分配中經過alloc_pages分配的內存。

http://linuxperf.com/?cat=7就指出了一個這樣的例子:bash

在VMware guest上有一個常見問題,就是VMWare ESX宿主機會經過guest上的Balloon driver(vmware_balloon module)佔用guest的內存,有時佔用得太多會致使guest無內存可用,這時去檢查guest的/proc/meminfo只看見MemFree不多、但看不出內存的去向,緣由就是Balloon driver經過alloc_pages分配內存,沒有在/proc/meminfo中留下統計值,因此很難追蹤。架構

  • 用戶進程部分:Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)Cached + AnonPages + Buffers + (HugePages_Total * Hugepagesize)

根據上述公式,除掉X部分,得出linux內核統計出來的已分配內存爲:2.62G。

該值遠小於使用 freetop獲得的used。
這裏推測緣由就是linux沒有統計進來的alloc_pages分配的內存。
考慮到該linux確實是在VMWare平臺上申請的虛擬機,所以咱們推測是因爲虛擬機平臺內存不足,因而宿主機模擬該客戶機內部進程消耗內存,實際上將內存調度到其餘虛客戶機去了。

5、結論

虛擬機管理員還沒有最終確認,目前僅僅是推測

在VMWare虛擬機平臺上,宿主機能夠經過一個叫Balloon driver(vmware_balloon module)的驅動程序模擬客戶機linux內部的進程佔用內存,被佔用的內存其實是被宿主機調度到其餘客戶機去了。但這種驅動程序模擬的客戶機進程在客戶機linux上是經過alloc_pages實現的內存動態分配,並無被linux內核統計進來,因而形成了內存去向不明的現象。

相關文章
相關標籤/搜索