docker stats和/proc/pid/status内存统计的差异问题

发布于:2025-06-28 ⋅ 阅读:(19) ⋅ 点赞:(0)

最近在做任务的压测操作,我们有几个任务由于打印日志的代码问题,导致频繁的打日志,由于我们的任务运行在Docker容器中,所以使用docker stats进行容器内存的监控统计操作。

具体的操作流程如下:

1.首先使用docker stats进行内存和cpu使用的监控

CONTAINER ID   NAME                   CPU %     MEM USAGE / LIMIT     MEM %     NET I/O   BLOCK I/O    PIDS
e39d5e04cac6   br-flink-taskmanager   31.80%    7.568GiB / 31.25GiB   24.22%    0B / 0B   0B / 320MB   682

可以看到内存占用是7.5G左右,但是我们任务的整个内存限制为6G,这里超了一个多G,在其他的时候监控,还会发现这里的内存占用到了20G的情况。这里我们还怀疑使用的logback出现了内存泄漏的情况。。。。。

2.查看docker容器的内存监控信息:cat /sys/fs/cgroup/memory/docker/{容器id}/memory.stat 

cache 2599772160
rss 5964361728
rss_huge 0
mapped_file 339968
swap 0
pgpgin 8397732
pgpgout 6306879
pgfault 2801823
pgmajfault 0
inactive_anon 0
active_anon 5964361728
inactive_file 443101184
active_file 2156646400
unevictable 0
hierarchical_memory_limit 9223372036854771712
hierarchical_memsw_limit 9223372036854771712
total_cache 2599772160
total_rss 5964361728
total_rss_huge 0
total_mapped_file 339968
total_swap 0
total_pgpgin 0
total_pgpgout 0
total_pgfault 0
total_pgmajfault 0
total_inactive_anon 0
total_active_anon 5964361728
total_inactive_file 443101184
total_active_file 2156646400
total_unevictable 0

以上是docker容器中监控看到的,发现cache和rss相关的参数,可以看到进程实际使用的物理内存rss约为5.9G,而cache占用了约2.5G内存,总的加起来约等于7G多,所以会不会docker stats统计的是rss + cache的和。

memory.stat的参数解释

统计 描述
cache 页缓存,包括 tmpfs(shmem),单位为字节
rss 匿名和 swap 缓存,不包括 tmpfs(shmem),单位为字节
mapped_file memory-mapped 映射的文件大小,包括 tmpfs(shmem),单位为字节
pgpgin 存入内存中的页数
pgpgout 从内存中读出的页数
swap swap 用量,单位为字节
active_anon 在活跃的最近最少使用(least-recently-used,LRU)列表中的匿名和 swap 缓存,包括 tmpfs(shmem),单位为字节
inactive_anon 不活跃的 LRU 列表中的匿名和 swap 缓存,包括 tmpfs(shmem),单位为字节
active_file 活跃 LRU 列表中的 file-backed 内存,以字节为单位
inactive_file 不活跃 LRU 列表中的 file-backed 内存,以字节为单位
unevictable 无法再生的内存,以字节为单位
hierarchical_memory_limit 包含 memory cgroup 的层级的内存限制,单位为字节
hierarchical_memsw_limit 包含 memory cgroup 的层级的内存加 swap 限制,单位为字节
 3.通过一番搜索,找到了可以通过/proc/sys/vm/drop_caches文件来进行缓存的清除操作

echo 1 > /proc/sys/vm/drop_caches # 仅清除页面缓存
echo 2 > /proc/sys/vm/drop_caches # 清除目录项和inode
echo 3 > /proc/sys/vm/drop_caches # 清除页面缓存、目录项以及inode

我先执行:echo 1 > /proc/sys/vm/drop_caches,然后再docker stats观察内存占用

[root@br-apm-009 ~]# docker stats
CONTAINER ID   NAME                   CPU %     MEM USAGE / LIMIT     MEM %     NET I/O   BLOCK I/O    PIDS
e39d5e04cac6   br-flink-taskmanager   0.68%     7.624GiB / 31.25GiB   24.40%    0B / 0B   0B / 320MB   681
[root@br-apm-009 ~]# echo 1 > /proc/sys/vm/drop_caches
[root@br-apm-009 ~]# docker stats
CONTAINER ID   NAME                   CPU %     MEM USAGE / LIMIT     MEM %     NET I/O   BLOCK I/O        PIDS
e39d5e04cac6   br-flink-taskmanager   0.69%     5.534GiB / 31.25GiB   17.71%    0B / 0B   8.19kB / 320MB   682

如上可以看到清理缓存内存掉到了5.5G左右了,所以docker stats统计的时候会加上cache/buffer相关的内存。

4.为了验证进程的实际内存是不是真的占用这么多,直接去观察/proc/pid/status中是否真的是占用了5.5G多。

[root@br-apm-009 ~]# ps -ef | grep taskmanager
bonree   35098 35077 99 11:03 ?        05:43:35 /usr/lib/jvm/java-11-openjdk-amd64/bin/java -Xmx4429185024 -Xms4429185024 -XX:MaxDirectMemorySize=671088640 -XX:MaxMetaspaceSize=268435456 -XX:+IgnoreUnrecognizedVMOptions -Dlog.file=/opt/flink/log/flink--taskexecutor-77-br-apm-009.log -Dlog4j.configuration=file:/opt/flink/conf/log4j-console.properties -Dlog4j.configurationFile=file:/opt/flink/conf/log4j-console.properties -Dlogback.configurationFile=file:/opt/flink/conf/logback-console.xml -classpath /opt/flink/lib/flink-cep-1.19.1.jar:/opt/flink/lib/flink-connector-files-1.19.1.jar:/opt/flink/lib/flink-csv-1.19.1.jar:/opt/flink/lib/flink-json-1.19.1.jar:/opt/flink/lib/flink-scala_2.12-1.19.1.jar:/opt/flink/lib/flink-table-api-java-uber-1.19.1.jar:/opt/flink/lib/flink-table-planner-loader-1.19.1.jar:/opt/flink/lib/flink-table-runtime-1.19.1.jar:/opt/flink/lib/ingester.jar:/opt/flink/lib/flink-dist-1.19.1.jar:::: org.apache.flink.runtime.taskexecutor.TaskManagerRunner --configDir /opt/flink/conf -D taskmanager.memory.network.min=536870912b -D taskmanager.cpu.cores=25.0 -D taskmanager.memory.task.off-heap.size=0b -D taskmanager.memory.jvm-metaspace.size=268435456b -D external-resources=none -D taskmanager.memory.jvm-overhead.min=536870912b -D taskmanager.memory.framework.off-heap.size=134217728b -D taskmanager.memory.network.max=536870912b -D taskmanager.memory.framework.heap.size=134217728b -D taskmanager.memory.managed.size=536870912b -D taskmanager.memory.task.heap.size=4294967296b -D taskmanager.numberOfTaskSlots=25 -D taskmanager.memory.jvm-overhead.max=536870912b -D blob.server.port=6430 -D query.server.port=6431
[root@br-apm-009 ~]# cat /proc/35098/status
Name:   java
Umask:  0022
State:  S (sleeping)
Tgid:   35098
Ngid:   0
Pid:    35098
PPid:   35077
TracerPid:      0
Uid:    2048    2048    2048    2048
Gid:    2048    2048    2048    2048
FDSize: 1024
Groups: 2048
VmPeak:  8330360 kB
VmSize:  8330360 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:   5807356 kB
VmRSS:   5769756 kB
RssAnon:         5742464 kB
RssFile:           27292 kB
RssShmem:              0 kB
VmData:  8140832 kB
VmStk:       140 kB
VmExe:         8 kB
VmLib:     18112 kB
VmPTE:     15028 kB
VmSwap:        0 kB
Threads:        683
SigQ:   0/127928
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 2000000101005ccf
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
NoNewPrivs:     0
Seccomp:        2
Speculation_Store_Bypass:       thread force mitigated
Cpus_allowed:   00000000,0000ffff
Cpus_allowed_list:      0-15
Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list:      0
voluntary_ctxt_switches:        192
nonvoluntary_ctxt_switches:     7

其中/proc/pid/status中相关参数的解释如下:

Name:		进程的名称,例如"java"或"bash"。
State:		进程的状态,例如"running"或"sleeping"。
Tgid:		进程组ID,即进程的ID号。
Pid:		进程的ID号。
PPid:		父进程的ID号。
TracerPid:	跟踪进程的ID号。
Uid:		进程的用户ID号。
Gid:		进程的组ID号。
FDSize:	进程的文件描述符数。
Groups:	进程所属的组ID号列表。
VmPeak:	进程的虚拟内存峰值,即进程使用的最大内存大小。
VmSize:	进程的虚拟内存大小,即进程实际使用的内存大小。
VmLck:		进程的虚拟内存锁定大小,即进程被锁定的内存大小。
VmHWM:		进程的虚拟内存高水位线,即进程使用的最大内存大小。
VmRSS:		进程的实际内存大小,即进程在物理内存中的大小。
RssAnon:	进程的非映射内存大小,即进程的匿名内存大小。
RssFile:	进程的映射文件大小,即进程的文件映射内存大小。
RssShmem:	进程的共享内存大小,即进程的共享内存大小。
VmData:	进程的数据段内存大小,即进程使用的数据段内存大小。
VmStk:		进程的堆栈内存大小,即进程使用的堆栈内存大小。
VmExe:		进程的可执行文件大小,即进程使用的可执行文件大小。
VmLib:		进程的库文件大小,即进程使用的库文件大小。
VmPTE:		进程的页表项大小,即进程使用的页表项大小。
Threads:	进程的线程数。
SigQ:		进程的信号队列大小。
SigPnd:	进程的等待信号列表。
ShdPnd:	进程的等待共享内存列表。
SigBlk:	进程的阻塞信号列表。
SigIgn:	进程的忽略信号列表。
SigCgt:	进程的当前信号掩码。
CapInh:	进程的继承能力。
CapPrm:	进程的 permitted 能力。
CapEff:	进程的有效能力。
Cpus_allowed:进程可以使用的CPU列表。
Mems_allowed:进程可以使用的内存列表。
Voluntary_ctxt_switches:进程主动进行的上下文切换次数。
Nonvoluntary_ctxt_switches:进程被动进行的上下文切换次数。

如上可以看到vmRss是进程实际占用的内存大小,大小约为5.7G,和我们上面分析的实际内存占用是符合的。

5.最后还有一个问题为什么进程会占用额外的cache/buffer部分内存呢?

Buffer 的具体职责

  • 在当前的系统实现里,buffer主要是设计用来在系统对块设备进行读写时作为缓存来使用。这意味着对块的操作会使用buffer进行缓存,比如我们在格式化文件系统的时候。
  • 但是一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,cache的内容会被改变,而buffer则用来将cache的page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。
  • 这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。

Cache 的具体职责

  • cache主要用来作为文件系统上的文件数据的缓存来用,当进程对文件有read/write操作的时候。包括将文件映射到内存的系统调用mmap,就会用到cache。
  • 因为cache被作为文件类型的缓存来用,所以事实上也负责了大部分的块设备文件的缓存工作。

cache/buffer部分的内存在系统内存吃紧的时候会释放出来的。

参考文章:

https://blog.csdn.net/xs_chang/article/details/132602282

https://blog.csdn.net/jiangbb8686/article/details/102539805

https://blog.csdn.net/songguangfan/article/details/121475879

https://blog.csdn.net/kunyus/article/details/104617426


网站公告

今日签到

点亮在社区的每一天
去签到