近期在公司的某臺linux虛擬機上,發現內存幾乎消耗殆盡,但找不到其去向。
在調查過程當中,重點分析了/proc/meminfo文件,對其內存佔用進行了學習與分析。java
特記錄在此,與君分享。mysql
經過free -h
或top
查看內存消耗,發現used已接近最大可用內存,但各進程常駐內存(RES)遠比used要小。linux
先擺出結論:在VMWare虛擬機平臺上,宿主機能夠經過一個叫Balloon driver(vmware_balloon module)的驅動程序模擬客戶機linux內部的進程佔用內存,被佔用的內存其實是被宿主機調度到其餘客戶機去了。
但這種驅動程序模擬的客戶機進程在linux上的內存動態分配並無被linux內核統計進來,因而形成了上述問題的現象。算法
按內存消耗排序,取消耗大於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
屬性 | 大小(G) | 說明 |
---|---|---|
total | 7.6 | 可分配內存總計值 |
used | 7.26 | 已分配內存 |
free | 0.17 | 未分配內存 |
buff/cache | 0.17 | buff與緩存 |
列 | 合計(G) | 說明 |
---|---|---|
VIRT | 14.236 | 進程申請的虛擬內存大小,申請不意味着分配,該值與實際內存消耗關係不大。 |
RES | 1.9747 | 進程常駐內存,包含進程間共享內存。 |
SHR | 0.0171 | 進程間共享內存,該值是推算出來的,存在偏差,意義不大。 |
RES合計值比used少了5G多!這些內存哪去了?數據庫
理論上,各進程的RES合計值由於會重複計算共享內存,應該比used值略大。實際上這兩個值也每每是接近的,不該該差這麼多。
爲了進一步檢查linux中內存消耗的去向,須要對/proc/meminfo
文件進行一次完全的分析統計。centos
linux上各類內存查看工具如free,top實際上都是從/proc下面找linux內核的各類統計文件。
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
屬性 | 大小(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,而是被統計進了:Cached 或Mapped (當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的內存數量 |
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。
該值遠小於使用free
或top
獲得的used。
這裏推測緣由就是linux沒有統計進來的alloc_pages分配的內存。
考慮到該linux確實是在VMWare平臺上申請的虛擬機,所以咱們推測是因爲虛擬機平臺內存不足,因而宿主機模擬該客戶機內部進程消耗內存,實際上將內存調度到其餘虛客戶機去了。
虛擬機管理員還沒有最終確認,目前僅僅是推測
在VMWare虛擬機平臺上,宿主機能夠經過一個叫Balloon driver(vmware_balloon module)的驅動程序模擬客戶機linux內部的進程佔用內存,被佔用的內存其實是被宿主機調度到其餘客戶機去了。但這種驅動程序模擬的客戶機進程在客戶機linux上是經過alloc_pages實現的內存動態分配,並無被linux內核統計進來,因而形成了內存去向不明的現象。