正文
overcommit_memory的简单学习
背景
前几天一个测试环境启动失败.
总是有如下的提示:
Native memory allocation (mmap) failed to map
12288 bytes for committing reserved memory.
当时看free 其实内存剩余总量还是有的.
但是JVM启动总是失败.
当时没有考虑太多.改了下参数就启动起来了
但是今天发现有项目现场有一些被动调用的jvm也出现了crash
所以感觉有必要整理一下这一块的内容.
内存管理的基本知识
现代操作系统一般采用段页式管理
段用来区分内存的类型和用途(数据段,代码段,堆区,栈区等等)
页便于进行内存位置定位等.
操作系统又是一个多用户多任务的系统.
多任务的情况下, 内存里面会存储较多进程的内存信息.
此时进行内存隔离就是很关键的
基于页内存的分配, 可以讲虚拟内存和物理内存进行映射
可以将不连续的物理内存实现分配连续的虚拟内存空间.
内存虽然比磁盘要快, 但是比CPU还是慢了1-2个数量级.
所以很多时候, 用户/程序 想要申请的内存, 操作系统并不会直接给物理内存
而仅是会进行一些虚拟内存的地址分配, 当真正需要这块物理内存时在进行初始化和写入.
这一块也是 cow 写时复制 实现快速京城fork的一种思路.
另外也会出现程序认为申请的内存和实际申请的物理内存不一致的情况.
cow虽然会加快内存的分配过程
但是无法解决初始化和写入的过程
也就是基于此, 当真正需要物理内存时, 系统会触发一个page fault的操作.用来初始化物理内存
如果缺页过多, 就会导致性能的衰退(中断影响了业务的进行)
swap内存也是一样的问题,
只不过进程fork时, COW是通过已有的内存复制一份,再执行写入
swap的写入写出会从磁盘读取必须的信息, 性能损耗更大.
fork产生的中断是 minfault swap内存产生的中断是 majorfault
overcommit_memory导致的问题
echo 0|1|2 > /proc/sys/vm/overcommit_memory
overcommit_memory 其实有三个数值. 0 1 2
0 一般是尽量进行内存的分配.启发式的内存分配. 能分配则分配, 不能分配则报错.
1 可以超过物理内存+swap内存的数据量进行分配.
2 不允许超过物理内存+swap内存的数据量进行分配.
回到最开始的问题上来:
环境因为有数据库, 为了保证性能选择的参数为 2
这就导致:
虽然可以最佳化性能的. 避免因为内存超售导致出现OOM的情况
但是 JVM 的预申请堆区的机制就会导致内存分配失败, 直接JVM启动不起来导致宕机.
echo 1 之后就可以成功启动.
但是这样的话的风险是 存在OOM killer的风险, 需要关注操作系统的资源使用情况.
设置方式
第一种:
编辑/etc/sysctl.conf
修改后者是修改 vm.overcommit_memory=1
然后sysctl -p 使配置文件生效
第二种:
sysctl -w vm.overcommit_memory=1
第三种
echo 1 > /proc/sys/vm/overcommit_memory
关于overcommit与swap
swap 其实仅是为了过度.
保证内存不够用时可以有部分磁盘空间用于存储不常用的物理内存内的信息
但是swap会严重降低应用程序, 数据库的性能, 导致磁盘IO飙升
会产生错误的判断
现在内存比较便宜了, 建议设置合适的内存, 关闭swap. 提高性能.
关于 overcommit. 没有任何资源是足够
适当的超售不一定全是坏事, 但是要保证核心资源必须足够.
比如redis的 dump 的save 进程. 如果redis的核心进程使用超过一半的内存
并且不允许超售时就会导致redis dump线程的crash 严重时导致数据丢失.
但是如果允许超售, dump保存时需要一方面写入一盘, 一方面内存又swap到磁盘中去了一部分
此时会出现IO争抢, 导致dump进程变慢, 影响吞吐.
所以关于性能, 没有资源时只能够妥协,有了资源时才可以全都要.