Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >内存lru file比cache大的一种场景介绍

内存lru file比cache大的一种场景介绍

原创
作者头像
cdh
修改于 2024-09-15 05:01:50
修改于 2024-09-15 05:01:50
93000
代码可运行
举报
文章被收录于专栏:笔记+笔记+
运行总次数:0
代码可运行

在定位一个线上问题时发现Active(file)+Inactive(file)要比cached统计值大很多,看起来不太符合预期,正常情况下Active(file)+Inactive(file)的统计值都会同时计算到cached里,也就是一般cached的值会比Active(file)+Inactive(file)要大。

/proc/meminfo输出Cached信息内核统计方式如下,从前面meminfo信息看buffer的值并不大,也没有使用swap分区

从前面的截图可以看到AnonPages的值要比Active(anon)+Inactive(anon)大,推测是有部分anon page被统计到lru file page里,但是没有统计到lru anon中去。

搜下内核代码确实有相关的逻辑会将内存从LRU active annon移到lru inactive file的情况(但是这部分内存不会统计到cache里,这也是导致meminfo统计到的cache值比inactive file + active file小的原因):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec)
{
        if (PageAnon(page) && PageSwapBacked(page) &&
            !PageSwapCache(page) && !PageUnevictable(page)) {
                bool active = PageActive(page);

                del_page_from_lru_list(page, lruvec,
                                       LRU_INACTIVE_ANON + active); //这里从LRU的active anon移除
                ClearPageActive(page);
                ClearPageReferenced(page);
                /*
                 * lazyfree pages are clean anonymous pages. They have
                 * SwapBacked flag cleared to distinguish normal anonymous
                 * pages
                 */
                ClearPageSwapBacked(page);
                add_page_to_lru_list(page, lruvec, LRU_INACTIVE_FILE);//把page加到lru INACTIVE FILE上

                __count_vm_events(PGLAZYFREE, hpage_nr_pages(page));
                count_memcg_page_event(page, PGLAZYFREE);
                update_page_reclaim_stat(lruvec, 1, 0);
        }
}

strace下业务进程看下业务进程都是如何使用内存的,看到有大量的madvise MADV_FREE的系统调用:

网上找了个madvise实例验证确认下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# cat madvise-sample.c
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

bool cont = true;
//static const ptrdiff_t len = 1 << 25; // 32 MB
static const ptrdiff_t len = (1*1024*1024*1024);
void
handle_signal(int sig)
{
        if (sig != SIGINT) {
                return;
        }

        cont ^= true;
}

void
wait()
{
        cont = false;
        signal(SIGINT, handle_signal);

        printf("CTRL+C to continue\n");

        while (!cont) {
                sleep(1);
        }
}

int
main(void)
{
        char *start, *end;
        void* pb;

        pb = sbrk(0);
        if (pb == (void*)-1) {
                perror("sbrk");
                return 1;
        }

        start = (char*)pb;
        end = start + len;

        // "allocate" mem by increasing the program break
        //
        if (!~brk(end)) {
                perror("brk");
                return 1;
        }
        printf("allocate mem\r\n");
        wait();

        // "touch" the memory so that we get it really utilized - at this point,
        // we should see the faults taking place, and both RSS and active anon
        // going up
        //
        for (; start < end;) {
                *(start++) = 123;
        }
        printf("using allocate mem\r\n");
        wait();

        // let the kernel know that we don't really need half of the memory we
        // allocated anymore - while this will not change RSS, it'll definitely
        // change active and inactive.
        //
/*        if (!~madvise(pb + (len >> 1), (len >> 1), MADV_FREE)) {
                perror("madvise");
                return 1;
        }*/
        if (!~madvise(pb  , len, MADV_FREE)) {
                perror("madvise");
                return 1;
        }

        printf("madvise MADV_FREE mem\r\n");

        wait();

        return 0;
}

compile it
gcc -O2 -static -o sample ./madvise-sample.c

run it in a terminal that has the current proc in a cgroup
mkdir /sys/fs/cgroup/memory/test
echo $$ > /sys/fs/cgroup/memory/test/cgroup.procs
./sample

in another terminal, observe memory.stat for that cgroup
cat /sys/fs/cgroup/memory/test/memory.stat

use CTRL+C to make sample advance - see memory.stat as you do it.

验证效果:

1.开始执行测试程序时只申请了虚拟内存,所以看到内存Active(anon)并没有涨

2. 开始使用申请的内存后Active(anon)上涨

3. 调用madvise MADV_FREE后内存会被从Active(anon)移到Inactive(file)上,并且此时去看测试进程的内存的rss占用并不会降低。

执行drop cache并不会释放这部分内存,进程退出后这部分内存会自动释放回收,另外当系统内存紧张也就是出现低于水位线时该部分内存也会有机会被回收

MADV_FREE特性在linux 4.5内核版本才开始生效,这里用如下程序测试下在centos7 3.10内核和tllinux 6.6内核的区别:

代码语言:txt
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define SIZE_3GB (3ull * 1024 * 1024 * 1024)

int main() {
    void *addr = mmap(NULL, SIZE_3GB, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }


    for (size_t i = 0; i < SIZE_3GB; i += sysconf(_SC_PAGESIZE)) {
        ((char *)addr)[i] = 42;
    }
    printf("begin sleep\r\n");
    sleep(20);
    printf("end sleep\r\n");

    if (madvise(addr, SIZE_3GB, MADV_FREE) == -1) {
        perror("madvise");
        exit(1);
    }


    while (1) {
        sleep(1);
    }

    return 0;
}
~                      

在3.10.0-1160.119.1.el7.x86_64内核上测试,当程序使用madvise MADV_FREE后可以看到应用程序的rRssAnon直接被释放,系统可用内存统计MemAvailable也符合预期

在高版本内核比如5.4或者6.6内核当应用程序调用madvise MADV_FREE

后应用程序的RssAnon并没有减少,也就是内存并没有真正的归还给操作系统。

如果使用的是golang且版本是Go.12-Go1.15那么经常会碰到在高版本内核跟低版本内核内存统计表现不同的现象,相关介绍参考:

https://github.com/golang/go/issues/42330

https://go-review.googlesource.com/c/go/+/135395

https://blog.csdn.net/EDDYCJY/article/details/113750201

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
kubernetes上报Pod已用内存不准问题分析
经常有业务反馈在使用容器云平台过程中监控展示的业务使用内存不准,分析了下kubernetes采集Pod内存使用的实现原理以及相应的解决思路,本文所贴代码基于3.10内核
cdh
2020/06/02
18.3K6
docker cgroup 技术之memory(首篇)
内核使用cgroup对进程进行分组,并限制进程资源和对进程进行跟踪。内核通过名为cgroupfs类型的虚拟文件系统来提供cgroup功能接口。cgroup有如下2个概念:
charlieroro
2020/03/24
1.6K0
docker cgroup 技术之memory(首篇)
Linux中进程内存与cgroup内存的统计
在Linux内核,对于进程的内存使用与Cgroup的内存使用统计有一些相同和不同的地方。
用户9732312
2022/05/13
2.8K0
Linux:/proc/meminfo参数详细解释
/proc/meminfo是了解Linux系统内存使用状况的主要接口,我们最常用的”free”、”vmstat”等命令就是通过它获取数据的 ,/proc/meminfo所包含的信息比”free”等命令要丰富得多,然而真正理解它并不容易,比如我们知道”Cached”统计的是文件缓存页,manpage上说是“In-memory cache for files read from the disk (the page cache)”,那为什么它不等于[Active(file)+Inactive(file)]?AnonHugePages与AnonPages、HugePages_Total有什么联系和区别?很多细节在手册中并没有讲清楚,本文对此做了一点探究。
233333
2023/07/24
1.8K0
Linux:/proc/meminfo参数详细解释
Linux内存占用常用的几个分析方法,你确定都知道?
系统内存是硬件系统中必不可少的部分,定时查看系统内存资源运行情况,可以帮助我们及时发现内存资源是否存在异常占用,确保业务的稳定运行。
lyb-geek
2022/11/18
3.9K0
Linux内存占用常用的几个分析方法,你确定都知道?
Linux-3.14.12内存管理笔记【构建内存管理框架(5)】
前面已经分析了内存管理框架的构建实现过程,有部分内容未完全呈现出来,这里主要做个补充。
233333
2019/10/08
6660
Linux-3.14.12内存管理笔记【构建内存管理框架(5)】
Linux内存(手动释放cache)
项目的扩容申请了一台机器,到手之后看一下机器的指标,看到内存使用情况是这样的。 1、查看内存 free $ free -h total used free shared buffers cached Mem: 125G 89G 36G 92K 212M 74G -/+ buffers/cache: 14G 111G Swap
用户1225216
2018/03/05
10.9K0
【图解Linux内核】Page Cache
这些问题,很可能是由于Page Cache管理不到位引起的,因为Page Cache管理不当除了会增加系统I/O吞吐外,还会引起业务性能抖动。
JavaEdge
2021/10/18
5.9K0
聊聊 Linux 的内存统计
本文主要分析 Linux 系统内存统计的一些指标以及进程角度内存使用监控的一些方法。
程序员小强
2020/01/17
6.6K1
聊聊 Linux 的内存统计
计算 Linux 内存使用率方法及C实现
通过获取Linux中的 /proc/stat 文件中的内容可以获取系统内存的详细信息:
宋天伦
2020/07/17
9K1
揭开K8s适配CgroupV2内存虚高的迷局
在Almalinux替换CentOS的过程中,我们通过kubectl top nodes命令观察到了两个相同规格的节点(只有cgroup版本不同)。在分别调度两个相同的Pod后,我们预期它们的内存使用量应该相近。然而,我们发现使用了cgroupv2的节点的内存使用量比使用了cgroupv1的节点多了约280Mi。
zouyee
2023/11/15
7540
揭开K8s适配CgroupV2内存虚高的迷局
物理内存充足,但是为什么用代码总申请不到内存呢?
某次遇到一个客户尝试用 Java (其实跟具体用什么语言没关系)申请使用 4G 的内存申请,机器(ECS)总内存是 8G,free 的内存也超过 4G,按道理是 OK 的,但总是直接 OOM。
程序猿石头
2020/11/10
1.8K0
物理内存充足,但是为什么用代码总申请不到内存呢?
宋宝华:论Linux的页迁移(Page Migration)完整版
对于用户空间的应用程序,我们通常根本不关心page的物理存放位置,因为我们用的是虚拟地址。所以,只要虚拟地址不变,哪怕这个页在物理上从DDR的这里飞到DDR的那里,用户都基本不感知。那么,为什么要写一篇论述页迁移的文章呢?
Linux阅码场
2020/08/18
4.8K0
宋宝华:论Linux的页迁移(Page Migration)完整版
Linux性能调优之内存负载调优的一些笔记
「 原谅和忘记就意味着扔掉了我们获得的最贵经验 -------《人生的智慧》叔本华」
山河已无恙
2023/01/30
2.6K0
Linux性能调优之内存负载调优的一些笔记
Android Mememinfo Vs proc/meminfo
Free RAM: 1091614 kB(65910 cached pss + 514808 cached kernel + 510896 free)
用户9732312
2022/05/13
1.3K0
物理内存管理之zone详解
上一次说过了物理内存由node,zone,page三级结构来描述。而node是根据当前的系统是NUMA还是UMA系统。假设我们当前是UMA系统架构,则只有一个node。
DragonKingZhu
2020/04/13
3.6K0
[linux][memory]hugetlb和hugepage技术分析
前言: 乍一看,hugetlb和hugepage还挺像的,好像都是所谓的“大页”。然而,却很难说出来它们的差异。作者也是花了写时间翻翻代码,写了几个测试的例子,加上用工具据实测了几个关键参数,才明白。 分析: 1,page fault 用户大多数情况下申请内存的方法: a,使用malloc函数族,其实是glibc封装了brk/mmap。这种情况下分配的是虚拟内存,并没有直接分配物理内存。 b,调用brk分配,这种情况很少见,并只分配虚拟内存。 c,使用mmap,分配出来虚拟内存。如果flags带有MAP
皮振伟
2018/04/09
8.2K0
[linux][memory]hugetlb和hugepage技术分析
kmem accounting 对cgroup memory.usage_in_bytes统计的影响
当往memory cgroup所在的目录下文件memory.kmem.limit_in_bytes写入值时,内核会调用mem_cgroup_write:
cdh
2020/06/02
4.7K1
【Linux 内核 内存管理】物理内存组织结构 ③ ( 内存管理系统三级结构 | 内存节点描述 | 内存节点 pglist_data 结构体 | pglist_data 结构体源码 )
Linux 内核中 , 内存节点 ( Node ) 是 " 内存管理 " 的 最顶层的结构 , 下层分别是 区域 和 页 ;
韩曙亮
2023/03/30
1.9K0
【Linux 内核 内存管理】物理内存组织结构 ③ ( 内存管理系统三级结构 | 内存节点描述 | 内存节点 pglist_data 结构体 | pglist_data 结构体源码 )
Tina_Linux_内存优化_开发指南
硬件平台: 全志R/V/F/MR/H 系列芯片。软件平台: Tina v3.5 及后续版本。
韦东山
2023/02/25
6780
推荐阅读
相关推荐
kubernetes上报Pod已用内存不准问题分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验