近期在公司的某台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内核统计进来,因而形成了内存去向不明的现象。