mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-25 20:56:42 +08:00
361 lines
27 KiB
HTML
361 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>09 Page Cache:为什么我的容器内存使用量总是在临界点.md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/00 开篇词 一个态度两个步骤,成为容器实战高手.md.html">00 开篇词 一个态度两个步骤,成为容器实战高手</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/01 认识容器:容器的基本操作和实现原理.md.html">01 认识容器:容器的基本操作和实现原理</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/02 理解进程(1):为什么我在容器中不能kill 1号进程?.md.html">02 理解进程(1):为什么我在容器中不能kill 1号进程?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/03 理解进程(2):为什么我的容器里有这么多僵尸进程?.md.html">03 理解进程(2):为什么我的容器里有这么多僵尸进程?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/04 理解进程(3):为什么我在容器中的进程被强制杀死了?.md.html">04 理解进程(3):为什么我在容器中的进程被强制杀死了?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/05 容器CPU(1):怎么限制容器的CPU使用?.md.html">05 容器CPU(1):怎么限制容器的CPU使用?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/06 容器CPU(2):如何正确地拿到容器CPU的开销?.md.html">06 容器CPU(2):如何正确地拿到容器CPU的开销?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/07 Load Average:加了CPU Cgroup限制,为什么我的容器还是很慢?.md.html">07 Load Average:加了CPU Cgroup限制,为什么我的容器还是很慢?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/08 容器内存:我的容器为什么被杀了?.md.html">08 容器内存:我的容器为什么被杀了?</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/容器实战高手课/09 Page Cache:为什么我的容器内存使用量总是在临界点.md.html">09 Page Cache:为什么我的容器内存使用量总是在临界点</a>
|
||
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/10 Swap:容器可以使用Swap空间吗?.md.html">10 Swap:容器可以使用Swap空间吗?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/11 容器文件系统:我在容器中读写文件怎么变慢了.md.html">11 容器文件系统:我在容器中读写文件怎么变慢了</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/12 容器文件Quota:容器为什么把宿主机的磁盘写满了?.md.html">12 容器文件Quota:容器为什么把宿主机的磁盘写满了?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md.html">13 容器磁盘限速:我的容器里磁盘读写为什么不稳定</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/14 容器中的内存与IO:容器写文件的延时为什么波动很大?.md.html">14 容器中的内存与IO:容器写文件的延时为什么波动很大?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/15 容器网络:我修改了procsysnet下的参数,为什么在容器中不起效?.md.html">15 容器网络:我修改了procsysnet下的参数,为什么在容器中不起效?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/16 容器网络配置(1):容器网络不通了要怎么调试.md.html">16 容器网络配置(1):容器网络不通了要怎么调试</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/17 容器网络配置(2):容器网络延时要比宿主机上的高吗.md.html">17 容器网络配置(2):容器网络延时要比宿主机上的高吗</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/18 容器网络配置(3):容器中的网络乱序包怎么这么高?.md.html">18 容器网络配置(3):容器中的网络乱序包怎么这么高?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/19 容器安全(1):我的容器真的需要privileged权限吗.md.html">19 容器安全(1):我的容器真的需要privileged权限吗</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/20 容器安全(2):在容器中,我不以root用户来运行程序可以吗?.md.html">20 容器安全(2):在容器中,我不以root用户来运行程序可以吗?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐01 案例分析:怎么解决海量IPVS规则带来的网络延时抖动问题?.md.html">加餐01 案例分析:怎么解决海量IPVS规则带来的网络延时抖动问题?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐02 理解perf:怎么用perf聚焦热点函数?.md.html">加餐02 理解perf:怎么用perf聚焦热点函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐03 理解ftrace(1):怎么应用ftrace查看长延时内核函数?.md.html">加餐03 理解ftrace(1):怎么应用ftrace查看长延时内核函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐04 理解ftrace(2):怎么理解ftrace背后的技术tracepoint和kprobe?.md.html">加餐04 理解ftrace(2):怎么理解ftrace背后的技术tracepoint和kprobe?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐05 eBPF:怎么更加深入地查看内核中的函数?.md.html">加餐05 eBPF:怎么更加深入地查看内核中的函数?</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐06 BCC:入门eBPF的前端工具.md.html">加餐06 BCC:入门eBPF的前端工具</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/加餐福利 课后思考题答案合集.md.html">加餐福利 课后思考题答案合集</a>
|
||
</li>
|
||
<li>
|
||
|
||
<a href="/专栏/容器实战高手课/结束语 跳出舒适区,突破思考的惰性.md.html">结束语 跳出舒适区,突破思考的惰性</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>09 Page Cache:为什么我的容器内存使用量总是在临界点</h1>
|
||
<p>你好,我是程远。</p>
|
||
<p>上一讲,我们讲了 Memory Cgroup 是如何控制一个容器的内存的。我们已经知道了,如果容器使用的物理内存超过了 Memory Cgroup 里的 memory.limit_in_bytes 值,那么容器中的进程会被 OOM Killer 杀死。</p>
|
||
<p>不过在一些容器的使用场景中,比如容器里的应用有很多文件读写,你会发现整个容器的内存使用量已经很接近 Memory Cgroup 的上限值了,但是在容器中我们接着再申请内存,还是可以申请出来,并且没有发生 OOM。</p>
|
||
<p>这是怎么回事呢?今天这一讲我就来聊聊这个问题。</p>
|
||
<h2>问题再现</h2>
|
||
<p>我们可以用这里的代码做个容器镜像,然后用下面的这个脚本启动容器,并且设置容器 Memory Cgroup 里的内存上限值是 100MB(104857600bytes)。</p>
|
||
<pre><code># !/bin/bash
|
||
docker stop page_cache;docker rm page_cache
|
||
if [ ! -f ./test.file ]
|
||
then
|
||
dd if=/dev/zero of=./test.file bs=4096 count=30000
|
||
echo "Please run start_container.sh again "
|
||
exit 0
|
||
fi
|
||
echo 3 > /proc/sys/vm/drop_caches
|
||
sleep 10
|
||
docker run -d --init --name page_cache -v $(pwd):/mnt registry/page_cache_test:v1
|
||
CONTAINER_ID=$(sudo docker ps --format "{{.ID}}\t{{.Names}}" | grep -i page_cache | awk '{print $1}')
|
||
echo $CONTAINER_ID
|
||
CGROUP_CONTAINER_PATH=$(find /sys/fs/cgroup/memory/ -name "*$CONTAINER_ID*")
|
||
echo 104857600 > $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||
cat $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||
</code></pre>
|
||
<p>把容器启动起来后,我们查看一下容器的 Memory Cgroup 下的 memory.limit_in_bytes 和 memory.usage_in_bytes 这两个值。</p>
|
||
<p>如下图所示,我们可以看到容器内存的上限值设置为 104857600bytes(100MB),而这时整个容器的已使用内存显示为 104767488bytes,这个值已经非常接近上限值了。</p>
|
||
<p>我们把容器内存上限值和已使用的内存数值做个减法,104857600–104767488= 90112bytes,只差大概 90KB 左右的大小。</p>
|
||
<p><img src="assets/1192c3b6430dc1de84199e9da153502c.png" alt="img" /></p>
|
||
<p>但是,如果这时候我们继续启动一个程序,让这个程序申请并使用 50MB 的物理内存,就会发现这个程序还是可以运行成功,这时候容器并没有发生 OOM 的情况。</p>
|
||
<p>这时我们再去查看参数 memory.usage_in_bytes,就会发现它的值变成了 103186432bytes,比之前还少了一些。那这是怎么回事呢?</p>
|
||
<p><img src="assets/79ed17b066762a5385bf70758d4de87b.png" alt="img" /></p>
|
||
<h2>知识详解:Linux 系统有那些内存类型?</h2>
|
||
<p>要解释刚才我们看到的容器里内存分配的现象,就需要先理解 Linux 操作系统里有哪几种内存的类型。</p>
|
||
<p>因为我们只有知道了内存的类型,才能明白每一种类型的内存,容器分别使用了多少。而且,对于不同类型的内存,一旦总内存增高到容器里内存最高限制的数值,相应的处理方式也不同。</p>
|
||
<h2>Linux 内存类型</h2>
|
||
<p>Linux 的各个模块都需要内存,比如内核需要分配内存给页表,内核栈,还有 slab,也就是内核各种数据结构的 Cache Pool;用户态进程里的堆内存和栈的内存,共享库的内存,还有文件读写的 Page Cache。</p>
|
||
<p>在这一讲里,我们讨论的 Memory Cgroup 里都不会对内核的内存做限制(比如页表,slab 等)。所以我们今天主要讨论与用户态相关的两个内存类型,RSS 和 Page Cache。</p>
|
||
<h3>RSS</h3>
|
||
<p>先看什么是 RSS。RSS 是 Resident Set Size 的缩写,简单来说它就是指进程真正申请到物理页面的内存大小。这是什么意思呢?</p>
|
||
<p>应用程序在申请内存的时候,比如说,调用 malloc() 来申请 100MB 的内存大小,malloc() 返回成功了,这时候系统其实只是把 100MB 的虚拟地址空间分配给了进程,但是并没有把实际的物理内存页面分配给进程。</p>
|
||
<p>上一讲中,我给你讲过,当进程对这块内存地址开始做真正读写操作的时候,系统才会把实际需要的物理内存分配给进程。而这个过程中,进程真正得到的物理内存,就是这个 RSS 了。</p>
|
||
<p>比如下面的这段代码,我们先用 malloc 申请 100MB 的内存。</p>
|
||
<pre><code> p = malloc(100 * MB);
|
||
if (p == NULL)
|
||
return 0;
|
||
</code></pre>
|
||
<p>然后,我们运行 top 命令查看这个程序在运行了 malloc() 之后的内存,我们可以看到这个程序的虚拟地址空间(VIRT)已经有了 106728KB(~100MB),但是实际的物理内存 RSS(top 命令里显示的是 RES,就是 Resident 的简写,和 RSS 是一个意思)在这里只有 688KB。</p>
|
||
<p><img src="assets/fce68702702bd94539357134ba32ab09.png" alt="img" /></p>
|
||
<p>接着我们在程序里等待 30 秒之后,我们再对这块申请的空间里写入 20MB 的数据。</p>
|
||
<pre><code> sleep(30);
|
||
memset(p, 0x00, 20 * MB)
|
||
</code></pre>
|
||
<p>当我们用 memset() 函数对这块地址空间写入 20MB 的数据之后,我们再用 top 查看,这时候可以看到虚拟地址空间(VIRT)还是 106728,不过物理内存 RSS(RES)的值变成了 21432(大小约为 20MB), 这里的单位都是 KB。</p>
|
||
<p><img src="assets/8ca5fda34f50166cbc48f6aab93479ed.png" alt="img" /></p>
|
||
<p>所以,通过刚才上面的小实验,我们可以验证 RSS 就是进程里真正获得的物理内存大小。</p>
|
||
<p>对于进程来说,RSS 内存包含了进程的代码段内存,栈内存,堆内存,共享库的内存, 这些内存是进程运行所必须的。刚才我们通过 malloc/memset 得到的内存,就是属于堆内存。</p>
|
||
<p>具体的每一部分的 RSS 内存的大小,你可以查看 /proc/[pid]/smaps 文件。</p>
|
||
<h3>Page Cache</h3>
|
||
<p>每个进程除了各自独立分配到的 RSS 内存外,如果进程对磁盘上的文件做了读写操作,Linux 还会分配内存,把磁盘上读写到的页面存放在内存中,这部分的内存就是 Page Cache。</p>
|
||
<p>Page Cache 的主要作用是提高磁盘文件的读写性能,因为系统调用 read() 和 write() 的缺省行为都会把读过或者写过的页面存放在 Page Cache 里。</p>
|
||
<p>还是用我们这一讲最开始的的例子:代码程序去读取 100MB 的文件,在读取文件前,系统中 Page Cache 的大小是 388MB,读取后 Page Cache 的大小是 506MB,增长了大约 100MB 左右,多出来的这 100MB,正是我们读取文件的大小。</p>
|
||
<p><img src="assets/19e5319f0ac8821986efb005de8235df.png" alt="img" /></p>
|
||
<p>在 Linux 系统里只要有空闲的内存,系统就会自动地把读写过的磁盘文件页面放入到 Page Cache 里。那么这些内存都被 Page Cache 占用了,一旦进程需要用到更多的物理内存,执行 malloc() 调用做申请时,就会发现剩余的物理内存不够了,那该怎么办呢?</p>
|
||
<p>这就要提到 Linux 的内存管理机制了。 Linux 的内存管理有一种内存页面回收机制(page frame reclaim),会根据系统里空闲物理内存是否低于某个阈值(wartermark),来决定是否启动内存的回收。</p>
|
||
<p>内存回收的算法会根据不同类型的内存以及内存的最近最少用原则,就是 LRU(Least Recently Used)算法决定哪些内存页面先被释放。因为 Page Cache 的内存页面只是起到 Cache 作用,自然是会被优先释放的。</p>
|
||
<p>所以,Page Cache 是一种为了提高磁盘文件读写性能而利用空闲物理内存的机制。同时,内存管理中的页面回收机制,又能保证 Cache 所占用的页面可以及时释放,这样一来就不会影响程序对内存的真正需求了。</p>
|
||
<h3>RSS & Page Cache in Memory Cgroup</h3>
|
||
<p>学习了 RSS 和 Page Cache 的基本概念之后,我们下面来看不同类型的内存,特别是 RSS 和 Page Cache 是如何影响 Memory Cgroup 的工作的。</p>
|
||
<p>我们先从 Linux 的内核代码看一下,从 mem_cgroup_charge_statistics() 这个函数里,我们可以看到 Memory Cgroup 也的确只是统计了 RSS 和 Page Cache 这两部分的内存。</p>
|
||
<p>RSS 的内存,就是在当前 Memory Cgroup 控制组里所有进程的 RSS 的总和;而 Page Cache 这部分内存是控制组里的进程读写磁盘文件后,被放入到 Page Cache 里的物理内存。</p>
|
||
<p><img src="assets/a55c7d3e74e17yy613d83e220f93223e.png" alt="img" /></p>
|
||
<p>Memory Cgroup 控制组里 RSS 内存和 Page Cache 内存的和,正好是 memory.usage_in_bytes 的值。</p>
|
||
<p>当控制组里的进程需要申请新的物理内存,而且 memory.usage_in_bytes 里的值超过控制组里的内存上限值 memory.limit_in_bytes,这时我们前面说的 Linux 的内存回收(page frame reclaim)就会被调用起来。</p>
|
||
<p>那么在这个控制组里的 page cache 的内存会根据新申请的内存大小释放一部分,这样我们还是能成功申请到新的物理内存,整个控制组里总的物理内存开销 memory.usage_in_bytes 还是不会超过上限值 memory.limit_in_bytes。</p>
|
||
<h2>解决问题</h2>
|
||
<p>明白了 Memory Cgroup 中内存类型的统计方法,我们再回过头看这一讲开头的问题,为什么 memory.usage_in_bytes 与 memory.limit_in_bytes 的值只相差了 90KB,我们在容器中还是可以申请出 50MB 的物理内存?</p>
|
||
<p>我想你应该已经知道答案了,容器里肯定有大于 50MB 的内存是 Page Cache,因为作为 Page Cache 的内存在系统需要新申请物理内存的时候(作为 RSS)是可以被释放的。</p>
|
||
<p>知道了这个答案,那么我们怎么来验证呢?验证的方法也挺简单的,在 Memory Cgroup 中有一个参数 memory.stat,可以显示在当前控制组里各种内存类型的实际的开销。</p>
|
||
<p>那我们还是拿这一讲的容器例子,再跑一遍代码,这次要查看一下 memory.stat 里的数据。</p>
|
||
<p>第一步,我们还是用同样的脚本来启动容器,并且设置好容器的 Memory Cgroup 里的 memory.limit_in_bytes 值为 100MB。</p>
|
||
<p>启动容器后,这次我们不仅要看 memory.usage_in_bytes 的值,还要看一下 memory.stat。虽然 memory.stat 里的参数有不少,但我们目前只需要关注"cache"和"rss"这两个值。</p>
|
||
<p>我们可以看到,容器启动后,cache,也就是 Page Cache 占的内存是 99508224bytes,大概是 99MB,而 RSS 占的内存只有 1826816bytes,也就是 1MB 多一点。</p>
|
||
<p>这就意味着,在这个容器的 Memory Cgroup 里大部分的内存都被用作了 Page Cache,而这部分内存是可以被回收的。</p>
|
||
<p><img src="assets/b5c30dec6f760fbafd81de264b323dfe.png" alt="img" /></p>
|
||
<p>那么我们再执行一下我们的mem_alloc 程序,申请 50MB 的物理内存。</p>
|
||
<p>我们可以再来查看一下 memory.stat,这时候 cache 的内存值降到了 46632960bytes,大概 46MB,而 rss 的内存值到了 54759424bytes,54MB 左右吧。总的 memory.usage_in_bytes 值和之前相比,没有太多的变化。</p>
|
||
<p><img src="assets/c6835495bd44817cb5c60069d491c38a.png" alt="img" /></p>
|
||
<p>从这里我们发现,Page Cache 内存对我们判断容器实际内存使用率的影响,目前 Page Cache 完全就是 Linux 内核的一个自动的行为,只要读写磁盘文件,只要有空闲的内存,就会被用作 Page Cache。</p>
|
||
<p>所以,判断容器真实的内存使用量,我们不能用 Memory Cgroup 里的 memory.usage_in_bytes,而需要用 memory.stat 里的 rss 值。这个很像我们用 free 命令查看节点的可用内存,不能看"free"字段下的值,而要看除去 Page Cache 之后的"available"字段下的值。</p>
|
||
<h2>重点总结</h2>
|
||
<p>这一讲我想让你知道,每个容器的 Memory Cgroup 在统计每个控制组的内存使用时包含了两部分,RSS 和 Page Cache。</p>
|
||
<p>RSS 是每个进程实际占用的物理内存,它包括了进程的代码段内存,进程运行时需要的堆和栈的内存,这部分内存是进程运行所必须的。</p>
|
||
<p>Page Cache 是进程在运行中读写磁盘文件后,作为 Cache 而继续保留在内存中的,它的目的是为了提高磁盘文件的读写性能。</p>
|
||
<p>当节点的内存紧张或者 Memory Cgroup 控制组的内存达到上限的时候,Linux 会对内存做回收操作,这个时候 Page Cache 的内存页面会被释放,这样空出来的内存就可以分配给新的内存申请。</p>
|
||
<p>正是 Page Cache 内存的这种 Cache 的特性,对于那些有频繁磁盘访问容器,我们往往会看到它的内存使用率一直接近容器内存的限制值(memory.limit_in_bytes)。但是这时候,我们并不需要担心它内存的不够, 我们在判断一个容器的内存使用状况的时候,可以把 Page Cache 这部分内存使用量忽略,而更多的考虑容器中 RSS 的内存使用量。</p>
|
||
<h2>思考题</h2>
|
||
<p>在容器里启动一个写磁盘文件的程序,写入 100MB 的数据,查看写入前和写入后,容器对应的 Memory Cgroup 里 memory.usage_in_bytes 的值以及 memory.stat 里的 rss/cache 值。</p>
|
||
<p>欢迎在留言区写下你的思考或疑问,我们一起交流探讨。如果这篇文章让你有所收获,也欢迎你分享给更多的朋友,一起学习进步。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/容器实战高手课/08 容器内存:我的容器为什么被杀了?.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/容器实战高手课/10 Swap:容器可以使用Swap空间吗?.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997792ab3d3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|