前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >值得收藏的查询进程占用内存情况方法汇总

值得收藏的查询进程占用内存情况方法汇总

作者头像
腾讯云数据库 TencentDB
发布2020-02-14 18:40:48
1.7K0
发布2020-02-14 18:40:48
举报
文章被收录于专栏:腾讯云数据库(TencentDB)

| 作者:杨一迪,腾讯云数据库后台开发工程师,主要负责腾讯云PostgreSQL、CynosDB等产品后台开发工作。


现网运维过程中,常有用户咨询实例的内存使用情况,故而和大家一起分享我对于内存占用情况的理解,共同进步。

1

简述

查看进程占用内存情况的方式比较多,包括top命令、/proc/${pid}/smaps文件统计、cgroup统计等。但不同方式的查询结果具体代表什么含义,这里通过一个测试程序,简单验证下这三种查询方式如何反映进程的内存使用情况。想看结论的直接看文末的总结。本文有任何错误,欢迎在留言区讨论指导。

1

测试程序

为了验证进程的私有内存、共享内存使用情况,写了个简单的http server,主要代码如下。

1. 申请私有内存

申请一个指定大小的数组,其中g_str为全局变量,不会在接口返回时销毁。

代码语言:javascript
复制
func expandGlobalVar(writer http.ResponseWriter, request *http.Request) {    type Request struct {        Length    int    }    data, err := ioutil.ReadAll(request.Body)    if err != nil {        log.Printf("ioutil.ReadAll failed. err: %v", err)        writer.Write([]byte("io failed"))        return    }    req := &Request{}    json.Unmarshal(data, req)    g_str = make([]byte, req.Length)    for i:=0;i<req.Length;i++{        g_str[i]='a'    }    curLength, curCap := len(g_str), cap(g_str)    writer.Write([]byte(fmt.Sprintf("req length: %d, length: %d, cap: %d", req.Length, curLength, curCap)))    return}
代码语言:javascript
复制
2. 挂载共享内存文件

仅挂载共享内存文件,还未读取共享内存,此时并没有申请共享内存。

代码语言:javascript
复制
func mmapAttach(writer http.ResponseWriter, request *http.Request) {    data, err := ioutil.ReadAll(request.Body)    if err != nil {        log.Printf("ioutil.ReadAll failed. err: %v", err)        writer.Write([]byte("io failed"))        return    }    type Request struct {        Filename    string    }    req := &Request{}    json.Unmarshal(data, req)    mmapsFile, err = mmap.Open(req.Filename)    if err != nil {        writer.Write([]byte(err.Error()))    }    return}

3. 读取共享内存

读取指定长度的共享内存文件,此时会申请共享内存。

代码语言:javascript
复制
func mmapRead(writer http.ResponseWriter, request *http.Request) {    data, err := ioutil.ReadAll(request.Body)    if err != nil {        log.Printf("ioutil.ReadAll failed. err: %v", err)        writer.Write([]byte("io failed"))        return    }    type Request struct {        Start    int64        Length    int64    }    req := &Request{}    json.Unmarshal(data, req)
    buf := make([]byte, req.Length)    length, err := mmapsFile.ReadAt(buf, req.Start)    if err != nil {        log.Printf("readat error. err: ", err)        writer.Write([]byte("readat error"))        return    }    log.Printf("length: %d", length)    return}
代码语言:javascript
复制
4. 测试步骤

1)启动2个http server

代码语言:javascript
复制
~/code/httpMock/bin/httpMock -p 1001 &~/code/httpMock/bin/httpMock -p 1002 &

2)分别申请50M的私有内存

代码语言:javascript
复制
curl -d '{"Length":50000000}' http://127.0.0.1:1001/expandGlobalVarcurl -d '{"Length":50000000}' http://127.0.0.1:1002/expandGlobalVar

3)分别申请100M、200M的共享内存,其中有100M由进程共享

代码语言:javascript
复制
curl -d '{"Filename":"/root/code/httpMock/mmap_files/log"}' http://127.0.0.1:1001/mmapAttachcurl -d '{"Filename":"/root/code/httpMock/mmap_files/log"}' http://127.0.0.1:1002/mmapAttachcurl -d '{"Start": 0, "Length":100000000}' http://127.0.0.1:1001/mmapReadcurl -d '{"Start": 0, "Length":200000000}' http://127.0.0.1:1002/mmapRead

1

测试结果

1. /proc/${pid}/smaps

smaps文件记录了进程中各个内存段的使用情况,按照上述测试步骤,可观察到smaps中的内存变化情况如下:

1)启动http server后,Rss占用3M左右

2)申请50M的私有内存后,可以看到私有内存所在的内存段,Rss/Pss分别占用50M左右

3)分别申请100M、200M的共享内存,其中有100M由进程共享。申请后私有内存段扩充到100M,Rss增加量=私有内存增加量+共享内存增加量,Pss=私有内存+共享内存/共享进程数。

结论:smaps中记录了进程的各个内存段,其中Rss=私有内存+共享内存,Pss=私有内存+共享内存/共享进程数,Rss中的共享内存会被重复计算。

2. top命令

top命令返回了物理内存和共享内存的使用情况,按上述测试步骤,可观察到top命令结果变化如下:

1)启动2个http server后,RES私有内存占用3M左右,与smaps的RSS一致:

2)分别申请50M的私有内存,RES扩充到50M左右:

3)分别申请100M、200M的共享内存后,RES与smaps中的Rss类似,扩充了150M和250M左右,SHR扩充了100M和200M:

结论:top命令结果中,RES代表私有内存+共享内存,SHR代表共享内存,单位都为KB。top命令的RES与smaps中的RSS基本一致

3. cgroup memory子系统

cgroup memory子系统中,memory.usage_in_bytes记录了cgroup组中的进程的内存使用情况,memory.stat记录了各类内存的详细使用情况,按上述测试步骤,可观察到cgroup统计结果变化如下:

1)在同一cgroup组中启动http server,注意需要通过cgexec启动,保证进程启动时就在cgroup组中。启动后rss为2M左右,等于(3M-2M)2,即私有内存量进程数,与top命令、smaps计算的私有内存量基本一致:

代码语言:javascript
复制
cgdelete memory:httpMockcgcreate -g memroy:httpMockcgexec -g memory:httpMock ~/code/httpMock/bin/httpMock -p 1001 &cgexec -g memory:httpMock ~/code/httpMock/bin/httpMock -p 1002 &

2)分别申请50M的私有内存后,两进程共扩充100M左右:

3)分别申请100M、200M的共享内存后,内存使用量与top命令和smaps中统计的私有内存用量基本一致:

结论:cgroup中的memory.usage_in_bytes和memory.stat的rss字段,统计的是进程的私有内存

4. cgroup的内存限制与page cache

当系统读取文件时,会在系统缓存中缓存文件内容,以减少硬盘IO。这部分内存缓存,会统计到cgroup.stat中的cache字段。而在多个cgroup组都有读取相同文件时,这部分缓存只会统计到第一个读该文件的cgroup组中。经过验证,这部分缓存不会触发oom,在缓存+内存占用达到内存限制时,会回收系统缓存。验证过程如下:

1)启动http server后,加载共享文件并读取,可看到占用了100M的cache:

2)调整内存上限,使其低于cache+rss,触发了缓存回收:

[ 调整内存上限前,系统buf+cache为509M ]

[ 调整上限后触发缓存回收 ]

3)尝试将内存上限调整到已使用内存以下,调整失败:

1

总结

1)smaps中记录了进程占用的各个内存段,每个内存段中的Rss表示私有内存+共享内存大小,其中共享内存被多个进程占用时会被重复计算; 2)smaps中的Pss会将共享内存部分按共享进程数进行均摊,Pss表示私有内存+共享内存/共享进程数,因此计算一组进程占用的内存总数时,累加Pss的结果更准确; 3)smaps中的Shared_Clean/Shared_dirty表示共享内存大小 4)top命令的RES表示私有内存+共享内存大小,单位为KB,其中共享内存被多个进程占用时会被重复计算; 5)top命令的SHR表示共享内存大小,单位为KB; 6)cgroup的memory.stat中cache表示系统page cache大小,在进程读取文件时,文件会缓存到系统内存,这部分缓存的内存就会记到cache中; 7)cgroup的memory.stat中rss表示私有内存大小,不包括共享内存部分; 8)cgroup的memroy.usage_in_bytes表示内存使用量,主要包括memory.stat的cache和rss; 9)cgroup的内存限制,主要限制rss大小,当rss+cache>内存上限时会优先触发cache的回收。

综上所述,当我们考虑进程的内存使用量时,如果关注是否会触发oom,则主要看memory.stat的rss部分即可,但rss并不能反映共享内存的使用情况;如果要关注进程的私有内存+共享内存占用情况,则可以主要看smaps中的Pss。

参考资料:

cgroup:https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt linux /proc/pid/smaps各字段含义:https://blog.csdn.net/u010902721/article/details/46446031

往期推荐

(点击图片即可跳转阅读)

开年大礼包 

↓↓更多惊喜优惠请点这儿~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯云数据库 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查看进程占用内存情况的方式比较多,包括top命令、/proc/${pid}/smaps文件统计、cgroup统计等。但不同方式的查询结果具体代表什么含义,这里通过一个测试程序,简单验证下这三种查询方式如何反映进程的内存使用情况。想看结论的直接看文末的总结。本文有任何错误,欢迎在留言区讨论指导。
    • 1. 申请私有内存
      • 3. 读取共享内存
        • 1. /proc/${pid}/smaps
          • 2. top命令
            • 3. cgroup memory子系统
            • 4. cgroup的内存限制与page cache
              • 参考资料:
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档