1.内存管理目标rom-ram
(1)为啥要做内存管理
- 计算机不可能所以用户进程和系统所需要的全部程序和数据放入主存,所以操作系统必须对内存空间进行合理有效的分配
- 内存管理功能
- 内存分配回收:主存储器(物理内存)的分配和管理
- 地址转换:将逻辑地址转换成相应的物理地址
- 内存扩充:利用虚拟存储技术,从逻辑上扩充主存
- 存储保护:保证各进程在各自的存储空间内运行,互不干扰
(2)存储两大类:内存和外存,rom和ram都是内存
- ROM全称 Read Only Memory 只读存储器或者固化存储器
- 在系统停止供电的时间仍然可以保持数据,但是电脑硬盘不是ROM内存,硬盘是外存
- 只能读取,用来存储和保存永久数据,不能随意更新,在任何时候都可以读取,即使是断电,ROM也能够保存数据
- 常见的计算机的ROM内的存储是BIOS程序、机器码和出场信息等
- Basic Input Output System 是在通电引导阶段运行硬件初始化,为操作系统提供运行服务的固件,时开始时运行的第一个程序
- RAM全称Random Access Memory随机存取存储器
- 掉电之后就丢失数据,常见的RAM就是计算机的内存,RAM有两大类
- 静态RAM(Static RAM/SRAM)
- 速度非常快,是目前读写最快的存储设备但是非常昂贵
- 只在要求很苛刻的地方使用,比如CPU的一级缓存,二级缓存,三级缓存是SRAM
- 动态RAM(Dynamic RAM/DRAM)
- 保留数据的时间很短,速度也比SRAM慢,不过比任何的ROM都要快
- 价格上说DRAM相比SRAM要便宜很多,比如常见的计算机内存就是DRAM
2.计算机内存的惰性分配
- 计算机的物理内存上每字节都有唯一的物理地址
- 进程直接使用物理内存空间有很多问题
- 进程malloc分配一块很大的连续的内存空间,可能会出现有足够多的空闲物理内存,却没有足够大的连续空闲内存
- 四处分散的内存块就是内存碎片,这样就浪费掉了
- 进程读写内存的安全性问题,物理内存本身是不限制访问的,任何地址都可以读写
- 进程执行错误指令可以修改其他进程的数据,包括制度数据,这样不安全
(1)什么是虚拟内存
Linux给每个创建的进程分配一个【独立且连续】的虚拟地址空间就是虚拟内存,超级大的字节数组(32位和64位系统不一样)。计算器处理器的地址有32位和64位两种,对应的虚拟地址空间大小分别位2的32次幂字节和2的64次幂字节。它使得应用程序认为拥有【一个连续完整的地址空间】的可用内存。
实际上进程用了多少空间,操作系统就会给他划出多少空间,所有的进程共享一块物理内存,每隔进程只把目前需要的虚拟内存映射到物理内存上。
(2)什么是内存映射
- 不是所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存
- 分配后的物理内存,是通过内存映射来管理的,即将不同进程的虚拟地址和不同内存的物理地址映射起来
- 内存映射,其实就是将虚拟内存地址映射到物理内存地址
- 总结:
- CPU要访问虚拟内存地址,要经过地址翻译成物理地址才能访问,通过内存管理单元(memory management unit)MMU
- MMU是一种硬件电路,速度很快,主要工作是进行内存管理(CPU并不直接和物理内存打交道)
- MMU来翻译成对应的物理内存地址,利用存放在主存中的页表来动态翻译虚拟地址,该表的内容由操作系统管理
(3)什么是页表
- 一种特殊的数据结构,进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,靠用页表来记录
- 页表每个表就是一个记录,好比数据库的一个表,有多个字段存储不同的信息
- 当前进程访问某个虚拟地址,就会先去看页表,如果发现对应的数据不在物理内存中,则发生缺页异常
- 注意:
- 使用malloc申请内存时是延时分配,并未真实分配物理内存,到真正开始使用malloc申请的物理内存时
- 发现没有记录才会申请,出现Page Fault缺页异常(进程从用户态切换到内核态),这就是内存的惰性分配机制
(4)触发Page Fault的原因
- 没有建立Virtual Address 虚拟地址->Physical Address的映射
- 访问的地址在物理内存中不存在,要从磁盘/swap分区读入才能使用,因为磁盘太慢,所以性能影响比较大
- 访问的地址内存非法,可能会导致进程宕掉
(5)总结
- 一个运行中的进程,不是所有的虚拟地址在物理内存中都有对应的页
- 当进程还没有开始运行的时候,程序的代码段和数据段实际上都放在磁盘中
- 当进程开始运行的时候,进程需要从内存中读出这段程序的代码
- 进程去寻找页表,页表中的地址不在物理内存上,因此就会发生缺页异常
3.内存管理-分而治之思想
1字节(Byte)=8位(bit)
1KB( Kilobyte,千字节)=1024Byte
1MB( Megabyte,兆字节)=1024KB
-
在x86系统中,物理地址中每4KB作为一页,以页为单位映射
-
应用程序使用虚拟内存,通过MMU找映射表,把连续的虚拟内存空间,映射到分散的物理内存空间
-
映射表中的每一个页表项,指向4KB物理地址,4GB空间可以分割为1024 * 1024个页表项
-
32位处理器的操作系统创建的进程分配4GB虚拟内存,每页指向4KB物理地址,共有4GB/4KB个页表项 = 1024 * 1024个页表项
-
映射表自身是需要保存在物理内存中
- 换算:页表中每一个表项大小是32bit,占据4Byte,那需要1024 * 1024 * 4Byte = 4MB来存储映射表
-
有些程序用不了那么多,但系统也分配了4MB来存储这个映射表,造成浪费
-
-
解决方案(称为多级页表)
- 大表拆小表,单一大映射表折成1024个体积更小的映射表
- 共使用1024个映射表,每一个映射表中只有1024个表项
- 每一个小的映射表也称作页表,所以一共有1024个页表
- 一个页表中共1024个页表项,每一个页表项占用4个字节,一个页表占用4KB的物理存储内存空间(4Byte * 1024条)
-
疑惑:一个页表占用4KB,那1024个页表一共占用4MB的物理内存空间,不也造成浪费了?
-
总数是没错,但是程序现在不一定要1024个页表,可以根据情况选择 部分小页表使用
-
比如:
-
微信程序的一共就需要
10 MB
的空间,那使用3
个页表就足够,加上页目录,一共需要16 KB
的空间 -
计算方式
- 一个页表 有1024 个页表项,每一个页表项指向一个 4KB 的物理页,指向 4MB 的物理内存空间(1024 * 4 KB)
- 10MB 的程序 ,向上取整,只需要3个页表即可
-
-
总结:
- 一个页表项占用4Byte的物理存储内存空间
- 一个页表项指向存储4KB的物理内存空间
- 一个页表占用4KB的物理存储内存空间
- 一个页表指向存储4MB的物理内存空间
-
页目录表
- 一个页表中的一个表项来指向了每一个物理页
- 4GB内存的共有1024个页表,而页目录表就是管理指向这1024个页表
- 页目录的字段属性和页表是一样的,所以页目录中共有1024个表项,指向1024个页表的物理地址
- 总结:
- 在页目录表中,每一个表项指向一个页表的开始的物理地址
- 常规每个应用程序都有自己的页目录和页表
- 从页目录表到页表,这样多层级控制方式,称为多级页表
4.内存不够的情况处理
(1)操作系统内存不够怎么办?
- 回收内存:把不常用的内存淘汰掉,比如LRU算法回收内存
- 内存置换:把不常用的内存通过交换分区直接写道
- 杀死进程:内存紧张时系统还会通过OOM(Out of Memory),会选择杀掉一些进程释放掉一些内存
(2)什么是交换分区Swap
- 进程占用内存很大,会导致内存消耗完,为解决该问题操作系统中运用Swap技术,拿部分硬盘空间来充当内存使用
(3)Swap换页机制
- 操作系统把物理内存中放不下的数据临时放到磁盘上,等到需要的时候在放回到物理内存中,提供超过物理内存容量的空间
- 访问数据被Swap换出
- 物理内存是有限资源,运行多进程时并不是每隔进程都活跃,系统会启动内存页面置换
- 将长时间未使用物理内存页帧放到swap分区,让出资源给其他进程
- 当存在于swap分区的页面被访问时就会触发Page Fault缺页异常,从而在置换回物理内存
(4)Swap预取机制
- 换页过程涉及的磁盘IO操作,读取耗时比较多,因此操作系统会引入预取机制进行优化
- 当发生换入操作时,预测还有哪些页会被访问,提前将它们一并换入物理页内存,减少发生缺页异常的次数
(5)swap配置应用场景
- kubernetes集群一般要关闭swap
- Java 的应用一般也是要关闭swap,像Rocketmq、ElasticSearch 等
- Java应用会用到堆,开启了swap就会部分存储到磁盘上
- 程序在 GC 的时候会遍历所有堆的内存,如果这部分内存是被 swap 到磁盘上,GC遍历的时候就会有磁盘IO影响性能
(6)内存页面置换算法
- 进程访问的页面不在内存中而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区
- 选择调出页面的算法就称为页面置换算法,决定应该换出哪个页面
- 页面的换入换出需要有磁盘的I/O,会有较大的开销,好的页面置换算法追求更少的缺页率
(7)常见内存页面置换算法分类
-
最佳置换算法(OPT)
-
选择的被淘汰页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率
-
性能最好,但没法预知哪个页未来不被访问,无法实现
-
- 先进先出页面置换算法(FIFO)
- 优先淘汰最早进入内存的页面,即再内存中驻留时间最久的页面。
- 实现简单,但算法性能差
- 最近最久未使用置换算法(LRU)
- 选择最近最长时间未访问过的页面进行淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问
- 算法性能好,但是开销大,需要硬件支持
- 时钟置换算法(CLOCK)
- 性能和开销较均衡的算法,比LRU差一些,与FIFO差不多
5.内存扩容swap配置实战
(1)swap配置参数:swappiness
-
参数可以从0-100进行设置,默认值swappiness=60,表示内存使用率超过100-60=40%时开始使用交换分区
-
swappiness=0的时候表示最大限度使用物理内存,尽量不用 swap空间,有些云服务器厂商直接设置为0
- 注意:把swappiness设置成0不会禁止swap, 是最大限度不用, 不是一定不用,想要禁止,就不要开启swap
-
swappiness=100的时候表示积极的使用swap分区,把内存上的数据及时的搬运到swap空间
-
命令
- 查看
cat /proc/sys/vm/swappiness
- 临时修改
echo 10 > /proc/sys/vm/swappiness
- 永久调整
vim /etc/sysctl.conf
加上vm.swappiness=10
- 查看
-
配置建议
-
根据不同的应用,有不同的配置,比如有些性能要求高中间件,由于硬盘io较慢,会要求禁用 swap,
-
分配太多的Swap空间会浪费磁盘空间,而Swap空间太少,则系统内存不够就会发生错误
-
redhat官方建议:
物理内存小于等于2GB的swap应设置为物理内存的2倍
物理内存大于2GB小于等于8G时swap应设置为等同与物理内存的大小
物理内存大于8GB时swap应设置为大于等于4GB但不超过8GB
-
生产环境 最好是设置swappiness=10,不要设置swappiness=0
-
(2)查看是否有开启swap
free -h
如果说0,则说明没开启
- Linux 支持两种类型的 Swap: Swap分区 和 Swap文件
(3)命令
- dd:用指定大小的块 拷贝一个文件,并在拷贝的同时进行指定的转换,下面是参数
- **if = 文件名:**输入文件名,缺省为标准输入,即指定源文件<if = input file>
- **of = 文件名:**输出文件名,缺省为标准输出,即指定目的文件<of = output file>
- **bs = byte:**同事设置读入/输出的块大小为bytes个字节,可代替ibs和obs
- **count = blocks:**仅拷贝blocks个块,块大小等于指定的字节数
- bs是每次读或写的大小,即一个块的大小,count是读写块的数量
(4)案例
- 增加swap分区文件大小,创建一个大小为256M的文件(256 * 1024KB = 262144)
dd if=/dev/zero of=/swapfile bs=1024 count=262144
# /dev/zero 是一个输入设备,用它来初始化文件
# if -> input_file输入文件 of -> output_file输出文件 bs -> block_size块大小 count -> 计数
- 把创建的文件变成swap文件
mkswap /swapfile
- 启动这个swap文件
swapon /swapfile
- 【可选】编辑 /etc/fstab文件,末行添加下面命令,每次开机时自动加载swap文件
vim /etc/fstab
/swapfile swap swap default 0 0
- 【可选】如果不需要swap之后,也可以卸载
swapoff /swapfile
- 查看swap文件位置
swapon -s
- free -h 查看
6.Linux的free命令详解
(1)free命令格式
- 显示Linux系统中空闲的、已用的物理内存、swap内存和被内核使用的buffer
- free [参数]
参数 | 说明 |
---|---|
-b | Byte为单位显示内存使用情况 |
-k | KB为单位显示内存使用情况 |
-m | MB为单位显示内存使用情况 |
-g | GB为单位显示内存使用情况 |
-h | 自动化更好的可读性方式显示 |
-w | 将buffer和cache差分单独的两个列显示 |
-s | 持续观察内存使用状况,比如每隔2s显示一次 free -s 2 |
-t | 显示内存总和列 free -th |
(2)显示结果说明
数值 | 解释 |
---|---|
total | 总计物理内存 + Swap的大小 |
used | 已使用物理内存 + Swap的大小 |
free | 可用的物理内存 + Swap的大小 |
shared | 被共享使用的物理内存大小 |
buff/cache | 磁盘的缓冲和缓存大小,buffers是块设备做的缓冲大小,cached是给文件做缓存大小 |
available | 可以被程序使用的内存大小 |
**注意:**free是真正未被使用的物理内存数量,available是应用程序的角度看到可用内存数量,Linux未了提升读写性能,会消耗一部分内存资源缓存磁盘数据,就是buffer和cache,没有足够的free内存可以用,内核就会从buffer和cache中回收内存。
- available = free + buff/cache
- **total = used + free + buff/cache **
7.Linux的top命令详解
- top命令能够实时显示系统中各个进程的资源占用状况,包括进程ID、内存占用率、CPU占用率等
- 格式top [参数] ,按1后显示各个cpu明细情况
————————————————————————————————————————————————————————————
# 任务队列信息,CPU 1 5 15分钟负载
top - 23:52:24 up 5 days, 7:10, 1 user, load average: 0.08, 0.03, 0.05
————————————————————————————————————————————————————————————
# 任务进程信息 total 总进程数,running运行着进程数,sleeping睡眠进程数,stopped停止进程数,zombie僵尸进程数
Tasks: 106 total, 1 running, 104 sleeping, 0 stopped, 1 zombie
————————————————————————————————————————————————————————————
# us用户态占用CPU的百分比,sy内核态占用CPU的百分比, id — 空闲CPU百分比
# wa等待IO占用CPU的百分比, hi硬中断(Hardware IRQ)占用CPU的百分比, si软中断(Software Interrupts)占用CPU百分比
%Cpu0 : 0.3 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
————————————————————————————————————————————————————————————
# 内存状态 total物理内存总量,free空闲内存总量, used使用中的内存总量, buff/cache缓存的内存量
KiB Mem : 7733012 total, 6023384 free, 878228 used, 831400 buff/cache
————————————————————————————————————————————————————————————
# swap交换分区信息 total交换区总量, free空闲交换区总量, used使用的交换区总量, avail进程可用内存空间
KiB Swap: 0 total, 0 free, 0 used. 6605468 avail Mem
————————————————————————————————————————————————————————————
# 进程的状态信息 PID 进程id,USER进程所有者,PR进程优先级 是动态优先级 ;NI nice负值表示高优先级,静态优先级一般不变
# VIRT 进程使用的虚拟内存总量 VIRT=SWAP+RES,申请过的内存没有真正分配物理内存也会计算在内,进程的虚拟内存比常驻内存大得多
# RES 进程实际使用的、未被换出的物理内存大小,即不包括 Swap 和共享内存,单位kb
# SHR共享内存大小,单位kb
# S进程状态,D=不可中断的睡眠状态 R=运行 S=睡眠 T=停止 Z=僵尸进程了;%CPU上次更新到现在的CPU时间占用百分比
# %MEM 进程使用的物理内存百分比;TIME+ 进程使用的CPU时间总计,单位1/100秒; COMMAND 进程名称(命令名/命令行)
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9476 root 10 -10 129360 12988 10236 S 1.7 0.2 23:02.91 AliYunDunMonito
8054 polkitd 20 0 2836776 566736 15756 S 0.3 7.3 12:38.53 mysqld
9364 root 10 -10 42380 4556 3036 S 0.3 0.1 1:12.27 AliYunDunUpdate
9466 root 10 -10 100876 8572 6492 S 0.3 0.1 6:09.75 AliYunDun
VIRT virtual memory usage
RES resident memory usage
SHR shared memory