一、前言在工作生活中,我们时常会遇到一些性能问题:比如手机用久了,在滑动窗口或点击 APP 时会出现页面反应慢、卡顿等情况;比如运行在某台服务器上进程的某些性能指标(影响用户体验的 PCT99 指标等)不达预期,产生告警等;造成性能问题的原因多种多样,可能是网络延迟高、磁盘 IO 慢、调度延迟高、内存回收等,这些最终都可能影响到用户态进程,进而被用户感知。 在 Linux 服务器场景中,内存是影响性能的主要因素之一,本文从内存管理的角度,总结归纳了一些常见的影响因素(比如内存回收、Page Fault 增多、跨 NUMA 内存访问等),并介绍其对应的调优方法。 二、内存回收2.1什么是内存内存(Memory)是计算机的重要部件之一,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,与硬盘等外部存储器交换的数据。 它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。 只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。 内存的运行也决定计算机整体运行快慢的程度。 2.2Linux内存回收机制为啥要回收:
内存回收针对的目标有两种,一种是针对zone的,另一种是针对一个memcg的,把针对zone的内存回收方式分为三种,分别是快速内存回收、直接内存回收、kswapd内存回收。 在整机层面,设置了三条水线:min、low、high;当系统 free 内存到 low 水线以下时,系统会唤醒kswapd 线程进行异步内存回收,一直回收到 high 水线为止,这种情况不会阻塞正在进行内存分配的进程;但如果 free 内存降到了 min 水线以下,就需要阻塞内存分配进程进行回收,不然就有 OOM(out of memory)的风险,这种情况下被阻塞进程的内存分配延迟就会提高,从而感受到卡顿。 这些水线可以通过内核提供的 /proc/sys/vm/watermark_scale_factor 接口来进行调整,该接口 合法取值的范围为 [0, 1000],默认为 10,当该值设置为 1000 时,意味着 low 与 min 水线,以及 high 与 low 水线间的差值都为总内存的 10% (1000/10000)。 针对 page cache 型的业务场景,我们可以通过该接口抬高 low 水线,从而更早的唤醒 kswapd 来进行异步的内存回收,减少 free 内存降到 min 水线以下的概率,从而避免阻塞到业务进程,以保证影响业务的性能指标。 【文章福利】小编推荐自己的Linux内核技术交流群:【865977150】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100名进群领取,额外赠送一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)
查看/proc/meminfo
使用
参数说明:
Buffer和Cache 通过free或者top命令我们可以发现其将内存分为了buffer和cache等部分。Buffer 和 Cache 的设计目的,是为了提升系统的 I/O 性能。它们利用内存,充当起慢速磁盘与快速 CPU 之间的桥梁,可以加速 I/O 的访问速度。通过man free可以查阅到下面的说明
Buffers 是内核缓冲区用到的内存,对应的是 /proc/meminfo 中的 Buffers 值。 Cache 是内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo 中的 Cached 与 SReclaimable 之和。 进一步通过man proc,可以看到具体说明:
Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。 Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。 SReclaimable 是 Slab 的一部分。Slab 包括两部分,其中的可回收部分,用 SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录。 最终总结:Buffer 是对磁盘数据的缓存,而 Cache 是文件数据的缓存,它们既会用在读请求中,也会用在写请求中。 从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。 从读的角度来说,不仅可以提高那些频繁访问数据的读取速度,也降低了频繁 I/O 对磁盘的压力。 三、Huge Page内存作为宝贵的系统资源,一般都采用延迟分配的方式,应用程序第一次向分配的内存写入数据的时候会触发 Page Fault,此时才会真正的分配物理页,并将物理页帧填入页表,从而与虚拟地址建立映射。 此后,每次 CPU 访问内存,都需要通过 MMU 遍历页表将虚拟地址转换成物理地址。为了加速这一过程,一般都会使用 TLB(Translation-Lookaside Buffer)来缓存虚拟地址到物理地址的映射关系,只有 TLB cache miss 的时候,才会遍历页表进行查找。 页的默认大小一般为 4K, 随着应用程序越来越庞大,使用的内存越来越多,内存的分配与地址翻译对性能的影响越加明显。试想,每次访问新的 4K 页面都会触发 Page Fault,2M 的页面就需要触发 512 次才能完成分配。 对此,Linux 内核提供了大页机制。 上图的 4 级页表中,每个 PTE entry 映射的物理页就是 4K,如果采用 PMD entry 直接映射物理页,则一次 Page Fault 可以直接分配并映射 2M 的大页,并且只需要一个 TLB entry 即可存储这 2M 内存的映射关系,这样可以大幅提升内存分配与地址翻译的速度。
目前内核提供了两种大页机制,一种是需要提前预留的静态大页形式,另一种是透明大页(THP, Transparent Huge Page) 形式。 3.1 静态大页首先来看静态大页,也叫做 HugeTLB。静态大页可以设置 cmdline 参数在系统启动阶段预留,比如指定大页 size 为 2M,一共预留 512 个这样的大页:
还可以在系统运行时动态预留,但该方式可能因为系统中没有足够的连续内存而预留失败。
预留特定 size 的大页:
预留特定 node 上的大页:
当预留的大页个数小于已存在的个数,则会释放多余大页(前提是未被使用) 。 编程中可以使用 mmap(MAP_HUGETLB) 申请内存。 这种大页的优点是一旦预留成功,就可以满足进程的分配请求,还避免该部分内存被回收;缺点是:
当然也可以使用开源 libhugetlbfs.so,这样无需修改应用程序 预留太多大页内存后,free 内存大幅减少,容易触发系统内存回收甚至 OOM 紧急情况下可以手动减少 nr_hugepages,将未使用的大页释放回系统;也可以使用 v5.7 引入的HugeTLB + CMA 方式,细节读者可以自行查阅。 3.2 透明大页再来看透明大页,在 THP always 模式下,会在 Page Fault 过程中,为符合要求的 vma 尽量分配大页进行映射;如果此时分配大页失败,比如整机物理内存碎片化严重,无法分配出连续的大页内存,那么就会 fallback 到普通的 4K 进行映射,但会记录下该进程的地址空间 mm_struct;然后 THP 会在后台启动khugepaged 线程,定期扫描这些记录的 mm_struct,并进行合页操作。因为此时可能已经能分配出大页内存了,那么就可以将此前 fallback 的 4K 小页映射转换为大页映射,以提高程序性能。整个过程完全不需要用户进程参与,对用户进程是透明的,因此称为透明大页。 虽然透明大页使用起来非常方便、智能,但也有一定的代价: 进程内存占用可能远大所需:因为每次Page Fault 都尽量分配大页,即使此时应用程序只读写几KB
因此 THP 还支持 madvise 模式,该模式需要应用程序指定使用大页的地址范围,内核只对指定的地址范围做 THP 相关的操作。这样可以更加针对性、更加细致地优化特定应用程序的性能,又不至于造成反向的负面影响。
四、mmap_lock 锁锁是内存管理中的一把知名的大锁,保护了诸如mm_struct 结构体成员、 vm_area_struct 结构体成员、页表释放等很多变量与操作。 mmap_lock 的实现是读写信号量, 当写锁被持有时,所有的其他读锁与写锁路径都会被阻塞。Linux 内核已经尽可能减少了写锁的持有场景以及时间,但不少场景还是不可避免的需要持有写锁,比如 mmap 以及 munmap 路径、mremap 路径和 THP 转换大页映射路径等场景。 应用程序应该避免频繁的调用会持有 mmap_lock 写锁的系统调用 (syscall), 比如有时可以使用 madvise(MADV_DONTNEED)释放物理内存,该参数下,madvise 相比 munmap 只持有 mmap_lock 的读锁,并且只释放物理内存,不会释放 VMA 区域,因此可以再次访问对应的虚拟地址范围,而不需要重新调用 mmap 函数。 另外对于 MADV_DONTNEED,再次访问还是会触发 Page Fault 分配物理内存并填充页表,该操作也有一定的性能损耗。 如果想进一步减少这部分损耗,可以改为 MADV_FREE 参数,该参数也只会持有 mmap_lock 的读锁,区别在于不会立刻释放物理内存,会等到内存紧张时才进行释放,如果在释放之前再次被访问则无需再次分配内存,进而提高内存访问速度。 一般 mmap_lock 锁竞争激烈会导致很多 D 状态进程(TASK_UNINTERRUPTIBLE),这些 D 进程都是进程组的其他线程在等待写锁释放。因此可以打印出所有 D 进程的调用栈,看是否有大量 mmap_lock 的等待。
内核社区专门封装了 mmap_lock 相关函数,并在其中增加了 tracepoint,这样可以使用 bpftrace 等工具统计持有写锁的进程、调用栈等,方便排查问题,确定优化方向。
五、跨numa内存访问在 NUMA 架构下,CPU 访问本地 node 内存的速度要大于远端 node,因此应用程序应尽可能访问本地 node 上的内存。可以通过 numastat 工具查看 node 间的内存分配情况:
查看单个进程在各个node上的内存分配情况:
5.1 绑 node可以通过 numactl 等工具把进程绑定在某个 node 以及对应的 CPU 上,这样该进程只会从该本地 node 上分配内存。 但这样做也有相应的弊端, 比如:该 node 剩余内存不够时,进程也无法从其他 node 上分配内存,只能期待内存回收后释放足够的内存,而如果进入直接内存回收会阻塞内存分配,就会有一定的性能损耗。 此外,进程组的线程数较多时,如果都绑定在一个 node 的 CPU 上,可能会造成 CPU 瓶颈,该损耗可能比远端 node 内存访问还大,比如 ngnix 进程与网卡就推荐绑定在不同的 node 上,这样虽然网卡收包时分配的内存在远端 node 上,但减少了本地 node 的 CPU 上的网卡中断,反而可以获得更好的性能提升。 5.2 numa balancing内核还提供了 numa balancing 机制,可以通过 /proc/sys/kernel/numa_balancing 文件或者cmdline参数 numa_balancing=进行开启。 该机制可以动态的将进程访问的 page 从远端 node 迁移到本地 node 上,从而使进程可以尽可能的访问本地内存。但该机制实现也有相应的代价, 在 page 的迁移是通过 Page Fault 机制实现的,会有相应的性能损耗;另外如果迁移时找不到合适的目标 node,可能还会把进程迁移到正在访问的 page 的 node 的 CPU 上,这可能还会导致 cpu cache miss,从而对性能造成更大的影响。因此需要根据业务进程的具体行为,来决定是否开启 numa balancing 功能。 六、内存映射mmapmmap内存映射的实现过程,总的来说可以分为三个阶段: (一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域 1、进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); 2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址 3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化 4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中 (二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系 5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。 6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。 7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。 8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。 (三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝 注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。 9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。 10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。 11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。 12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。 注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。 6.1mmap优点总结由上文讨论可知,mmap优点共有一下几点: 1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。 2、实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉。 3、提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。 同时,如果进程A和进程B都映射了区域C,当A第一次读取C时通过缺页从磁盘复制文件页到内存中;但当B再读C的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。 4、可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。 6.2mmap相关函数函数原型
返回说明 成功执行时,mmap()返回被映射区的指针。失败时,mmap()返回MAP_FAILED[其值为(void *)-1], error被设为以下的某个值:
参数 start:映射区的开始地址 length:映射区的长度 prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1 offset:被映射对象内容的起点 相关函数 int munmap( void * addr, size_t len ) 成功执行时,munmap()返回0。失败时,munmap返回-1,error返回标志和mmap一致; 该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小; 当映射关系解除后,对原来映射地址的访问将导致段错误发生。
一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。 七、虚拟内存空间7.1线性空间线性地址空间:是指Linux系统中从0x00000000到0xFFFFFFFF整个4GB虚拟存储空间。线性空间又分为用户空间和内核空间。 用户空间(进程地址空间) 用户空间是指从0x00000000到0xBFFFFFFF共3GB的线性地址空间,每个进程都有一个独立的3GB用户空间,所以用户空间由每个进程独有,但是内核线程没有用户空间,因为它不产生用户空间地址。另外子进程共享(继承)父进程的用户空间只是使用与父进程相同的用户线性地址到物理内存地址的映射关系,而不是共享父进程用户空间。运行在用户态和内核态的进程都可以访问用户空间。 linux采用虚拟内存管理技术,每一个进程都有一个3G大小的独立的进程地址空间,这个地址空间就是用户空间。每个进程的用户空间都是完全独立、互不相干的。进程访问内核空间的方式:系统调用和中断。 内核空间 内核空间表示运行在处理器最高级别的超级用户模式(supervisor mode)下的代码或数据,内核空间占用从0xC0000000到0xFFFFFFFF的1GB线性地址空间,内核线性地址空间由所有进程共享,但只有运行在内核态的进程才能访问,用户进程可以通过系统调用切换到内核态访问内核空间,进程运行在内核态时所产生的地址都属于内核空间。 内核空间又可分为以下几个线性空间: 1. 内核逻辑地址空间:是指从PAGE_OFFSET(3G)3G+896)之间的线性地址空间,是系统物理内存映射区,它映射了全部或部分(如果系统包含高端内存)物理内存。内核逻辑地址空间中的地址与RAM内存物理地址空间中对应的地址只差一个固定偏移量(3G),如果RAM内存物理地址空间从0x00000000地址编址,那么这个偏移量就是PAGE_OFFSET。 2. 高端线性地址空间:从high_memory(3G+896M)到0xFFFFFFFF之间的线性地址空间属于高端线性地址空间,其中VMALLOC_START~VMALLOC_END之间线性地址:(1)被vmalloc()函数用来分配物理上不连续但线性地址空间连续的高端物理内存,或者(2)被vmap()函数用来映射高端或低端物理内存,或者(3)由ioremap()函数来重新映射I/O物理空间。其中PKMAP_BASE开始的LAST_PKMAP(一般等于1024)页线性地址空间:被kmap()函数用来永久映射高端物理内存。FIXADDR_START开始的KM_TYPE_NR*NR_CPUS页线性地址空间:被kmap_atomic()函数用来临时映射高端物理内存,其他未用高端线性地址空间可以用来在系统初始化期间永久映射I/O地址空间。 7.2 物理空间低端内存(物理内存) 内核逻辑地址空间所映射物理内存就是低端内存(实际物理内存的大小,但是小于896),低端内存在Linux线性地址空间中始终有永久的一一对应的内核逻辑地址,系统初始化过程中将低端内存永久映射到了内核逻辑地址空间,为低端内存建立了虚拟映射页表。低端内存内物理内存的物理地址与线性地址之间的转换可以通过__pa(x)和__va(x)两个宏来进行,#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) __pa(x)将内核逻辑地址空间的地址x转换成对应的物理地址,相当于__virt_to_phys((unsigned long)(x)),__va(x)则相反,把低端物理内存空间的地址转换成对应的内核逻辑地址,相当于((void *)__phys_to_virt((unsigned long)(x)))。 高端内存(物理内存) 低端内存地址之上的物理内存是高端内存(物理内存896之上),高端内存在Linux线性地址空间中没有没有固定的一一对应的内核逻辑地址,系统初始化过程中不会为这些内存建立映射页表将其固定映射到Linux线性地址空间,而是需要使用高端内存的时候才为分配的高端物理内存建立映射页表,使其能够被内核使用,否则不能被使用。高端内存的物理地址于线性地址之间的转换不能使用上面的__pa(x)和__va(x)宏。 高端内存概念的由来:如上所述,Linux将4GB的线性地址空间划分成两部分,从0x00000000到0xBFFFFFFF共3GB空间作为用户空间由用户进程独占,这部分线性地址空间并没有固定映射到物理内存空间上;从0xC0000000到0xFFFFFFFF的第4GB线性地址空间作为内核空间,在嵌入式系统中,这部分线性地址空间除了映射物理内存空间之外还要映射处理器内部外设寄存器空间等I/O空间。0xC0000000~high_memory之间的内核逻辑地址空间专用来固定映射系统中的物理内存,也就是说0xC0000000~high_memory之间空间大小与系统的物理内存空间大小是相同的(当然在配置了CONFIG_DISCONTIGMEMD选项的非连续内存系统中,内核逻辑地址空间和物理内存空间一样可能存在内存孔洞),如果系统中的物理内存容量远小于1GB,那么内核线性地址空间中内核逻辑地址空间之上的high_memory~0xFFFFFFFF之间还有足够的空间来固定映射一些I/O空间。 可是,如果系统中的物理内存容量(包括内存孔洞)大于1GB,那么就没有足够的内核线性地址空间来固定映射系统全部物理内存以及一些I/O空间了,为了解决这个问题,在x86处理器平台设置了一个经验值:896MB,就是说,如果系统中的物理内存(包括内存孔洞)大于896MB,那么将前896MB物理内存固定映射到内核逻辑地址空间0xC0000000~0xC0000000+896MB(=high_memory)上,而896MB之后的物理内存则不建立到内核线性地址空间的固定映射,这部分内存就叫高端物理内存。此时内核线性地址空间high_memory~0xFFFFFFFF之间的128MB空间就称为高端内存线性地址空间,用来映射高端物理内存和I/O空间。 896MB是x86处理器平台的经验值,留了128MB线性地址空间来映射高端内存以及I/O地址空间,在嵌入式系统中可以根据具体情况修改这个阈值,比如,MIPS中将这个值设置为0x20000000B(512MB),那么只有当系统中的物理内存空间容量大于0x20000000B时,内核才需要配置CONFIG_HIGHMEM选项,使能内核对高端内存的分配和映射功能。什么情况需要划分出高端物理内存以及高端物理内存阈值的设置原则见上面的内存页区(zone)概念说明。 八、内存泄漏和内存溢出内存溢出(OOM,out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。 内存泄露(memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。 8.1如何判断内存泄露用 8.2如何判断内存溢出Heap Dump(堆转储文件)它是一个Java进程在某个时间点上的内存快照。Heap Dump是有着多种类型的。不过总体上heap dump在触发快照的时候都保存了java对象和类的信息。通常在写heap dump文件前会触发一次FullGC,所以heap dump文件中保存的是FullGC后留下的对象信息。 通过设置如下的JVM参数,可以在发生OutOfMemoryError后获取到一份HPROF二进制Heap Dump文件:
生成的文件会直接写入到工作目录。 注意:该方法需要JDK5以上版本。 转存堆内存信息后,需要对文件进行分析,从而找到OOM的原因。可以使用以下方式: mat:eclipse memory analyzer, 基于eclipse RCP的内存分析工具。 jhat:JDK自带的java heap analyze tool,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用。 8.3释放内存在Linux系统下,我们一般不需要去释放内存,因为系统已经将内存管理的很好。但是凡事也有例外,有的时候内存会被缓存占用掉,导致系统使用SWAP空间影响性能,例如当你在linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching。此时就需 要执行释放内存(清理缓存)的操作了。 释放内存操作:
数字含义:
九、内存性能参数9.1 缓存命中率缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。实际上,缓存是现在所有高并发系统必需的核心模块,主要作用就是把经常访问的数据(也就是热点数据),提前读入到内存中。这样,下次访问时就可以直接从内存读取数据,而不需要经过硬盘,从而加快应用程序的响应速度。 我们可以通过cachestat和cachetop命令进行查看:
这些指标从左到右依次表示:
READ_HIT 和 WRITE_HIT ,分别表示读和写的缓存命中率。除此之外,还可以使用
9.2Swap和NUMA当物理内存页不足的时候,
内存阈值(页最小阈值、页低阈值和页高阈值)可以通过内存域在 proc 文件系统中的接口
使用
Linux 本身支持两种类型的 Swap,即 Swap 分区和 Swap 文件。以 Swap 文件为例,开启方式如下
9.3测试工具 |
|