拨开由问题《linux下malloc最大可申请的内存》带来的重重疑云
今天阅读相关书籍的时候看到 "进程中堆的最大申请数量" 这一问题,我们知道使用malloc分配内存是在堆Heap里面分配的,如果一台机器一共有8GB物理内存,空闲5GB,那么我们使用malloc( )就一定能够申请到这5GB内存吗?理论上来说确实如此,因为这些内存未被其它进程使用。但实际测试出来结果却可能令人疑惑。
本文测试环境如下:
1 qi@qi:~$ uname -a
2 Linux qi 5.4.0-89-generic #100~18.04.1-Ubuntu SMP Wed Sep 29 10:59:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
一、首先需要考虑的几个问题
- 我们使用malloc( )申请到的是物理内存吗?
- 使用malloc( )能申请到的只有8GB的物理内存吗?
- malloc( )申请到的内存大小全都可以被用来memset( )吗?
以上三个问题,正是本次所要讨论的内容。现在假定认为以上三个陈述均正确,那么我们可以用以下程序测试malloc( )可以申请的内存大小:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 unsigned long long int maximum = 0;
6
7 int main (int argc, char* argv[])
8 {
9 unsigned int block_size[] = {1024*1024, 1024, 1};
10 int i, count;
11
12 for( int i=0; i<3; i++) {
13 for(count=1;; count++) {
14 void *block = malloc(maximum + block_size[i]*count);
15 if( block ) {
16 //memset(block, 0, maximum + block_size[i]*count);
17 free(block);
18 maximum = maximum + block_size[i]*count;
19 } else {
20 break;
21 }
22 }
23 }
24
25 printf("maximum malloc size is %llu bytes \n", maximum);
26
27 return 0;
28 }
运行以上程序,得到输出为:
1 root@qi:/home/qi/test_park/elf_load# ./main
2 maximum malloc size is 24587279333 bytes
可以看到,以上测试程序最大申请到了 22.9GB 的内存,但是我的机器上实际内存有多少呢?如下:
1 qi@qi:~/test_park/elf_load$ free
2 total used free shared buff/cache available
3 Mem: 8011016 2373760 3517884 719508 2119372 4654640
4 Swap: 15999996 0 15999996
很明显,机器上最大的物理内存也没到8GB,如果你了解swap 交换空间,可能会说Mem项和Swap项的total加起来似乎正好是22.9GB,但是另外一个问题有来了,那就是这些内存或者交换空间并不是全部空闲,包括系统内核和系统界面等等也要占用一部分物理内存,所以我们看到Mem项的 "available"的可用内存只有大约4.5GB,所以结果就是,malloc( )申请到的内存数量是远远大于我们实际的物理内存的。既然malloc( )函数的实际输出和我们的预期不相符,那是不是我们哪里用错了呢?不妨使用"man malloc"查看对其的官方解释:
1 NOTES
2 By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the mem‐
3 ory really is available. In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer. For more
4 information, see the description of /proc/sys/vm/overcommit_memory and /proc/sys/vm/oom_adj in proc(5), and the Linux kernel source file Documenta‐
5 tion/vm/overcommit-accounting.
果不其然,Note中说明了就算malloc( )返回非NULL指针也不能保证该指针指向的内存区域全都可以被该进程使用。那么为什么会这样呢?后面有提示,首先涉及到的最重要的一个设置就是 "/proc/sys/vm/overcommit_memory" 这一个文件,使用 "man proc" 找到有关其的说明:
1 /proc/sys/vm/overcommit_memory
2 This file contains the kernel virtual memory accounting mode. Values are:
3
4 0: heuristic overcommit (this is the default)
5 1: always overcommit, never check
6 2: always check, never overcommit
7
8 In mode 0, calls of mmap(2) with MAP_NORESERVE are not checked, and the default
9 check is very weak, leading to the risk of getting a process "OOM-killed".
10
11 In mode 1, the kernel pretends there is always enough memory, until memory actually
12 runs out. One use case for this mode is scientific computing applications that
13 employ large sparse arrays. In Linux kernel versions before 2.6.0, any nonzero
14 value implies mode 1.
15
16 In mode 2 (available since Linux 2.6), the total virtual address space that can be
17 allocated (CommitLimit in /proc/meminfo) is calculated as
18
19 CommitLimit = (total_RAM - total_huge_TLB) *
20 overcommit_ratio / 100 + total_swap
可以看到,如果该文件内容为0,mmap(malloc的内部调用)将不检查,有导致使用不存在内存的风险,如果文件内容为1,则malloc( )可以申请的内存可以非常大,我的机器上经过测试可以达到90T,如果该文件内容为2,那么所有可以申请的内存为 "CommitLimit",具体可以通过公式或者 "cat /proc/meminfo | grep Limit"查看大小。那么这就能说通为什么上面的程序可以malloc( )出22GB多的内存了,查看 "/proc/sys/vm/overcommit_memory" 果不其然,内容为0:
1 root@qi:/home/qi/test_park/elf_load# cat /proc/sys/vm/overcommit_memory
2 0
以上回答了第2个问题中的一部分,那就是某些设置下,malloc( )可以申请到超出机器物理内存的大小,为什么说是一部分呢,因为可申请的内存不仅和上述设定相关,还和机器的swap space相关,如果你不了解或者没听过( 事实上在你给你机器装Linux系统的时候应该碰到过,那就是磁盘分区的时候会有一个swap设定)swap空间,只需要知道它是一种挂载在物理硬盘上,用来存放一些不太频繁使用的内存,是一种低速的物理内存的扩展,当物理内存不够用时,原先一些物理内存中不常访问的内容会被转移到这里以让出空间给其它进程。所以swap空间也可以被malloc( )申请到。
由此,第2个问题得到了全部的解答。这个时候你可能会说,第1个问题应该也有答案了,因为malloc( )不仅申请了8GB的物理内存,还申请了15GB的swap硬盘空间作为扩展内存,甚至还可以申请大约90TB的不存在的内存,所以第一个问题就解决了吗?
其实对,但也不全对,因为malloc( )这个时候申请了内存,但没有完全申请,这就涉及到一个叫做 "Lazy Allocation" 的东东,类似于fork的写时复制机制,当你使用malloc( )时,系统并没有真正从物理内存中分配,而是等到进程要操作时才提供allocation,这也就解释了我们刚开头申请了22.9GB的内存都还没有报段错误的原因。只有当你access这个内存区域的时候才会真正分配,所以我们可以大胆的在程序里面加上memset,把上面贴出的代码的memset那一行取消掉注释,然后再运行。如果你不想等太久,可以像我这样:
1 root@qi:/home/qi/test_park/elf_load# echo 0 > /proc/sys/vm/overcommit_memory
2 root@qi:/home/qi/test_park/elf_load# swapoff -a
3 root@qi:/home/qi/test_park/elf_load# echo 2 > /proc/sys/vm/overcommit_memory
以上命令是把交换空间禁用,这样就可以减少可使用的内存了,关闭交换空间后,如果/proc/sys/vm/overcommit_memory内容为0,那么你可以malloc( )的内存大小应该为8GB左右,但是不是每一个字节都可以memset,大可以测试一下,会发现memset了6~7GB的内存空间后程序报错异常退出,这是因为这个时候可使用的内存也就这么大,这种情况下虽然使用malloc( )申请到的内存是不安全的。如果/proc/sys/vm/overcommit_memory内容为2,那么这个时候可申请的内存就得看 "CommitLimit" 了,在我的机器上测试是只能申请1.5GB左右,这种情况下无论如何也不会访问非法内存区域了,但是一个缺点是不能使用全部的空闲内存,只能修改相应的设置。
那么该如何知道实际可用的内存大小呢?一种解决方案是查看 "/proc/meminfo" 中的available memory,乘个安全系数再来申请。
以上,三个问题全都被解决,离专业的linuxer又近了一步~
拨开由问题《linux下malloc最大可申请的内存》带来的重重疑云的更多相关文章
- [Android Memory] Linux下malloc函数和OOM Killer
http://www.linuxidc.com/Linux/2010-09/28364.htm Linux下malloc函数主要用来在用户空间从heap申请内存,申请成功返回指向所分配内存的指针,申请 ...
- Linux下查看内核、CPU、内存及各组件版本的命令和方法
Linux下查看内核.CPU.内存及各组件版本的命令和方法 Linux查看内核版本: uname -a more /etc/*release ...
- Linux下如何查看哪个进程占用内存多?
1.top top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器 可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者 ...
- 方法:Linux 下用JAVA获取CPU、内存、磁盘的系统资源信息
CPU使用率: InputStream is = null; InputStreamReader isr = null; BufferedReader brStat = null; StringTok ...
- Linux 下修改Tomcat使用的JVM内存大小
我的服务器的配置: # OS specific support. $var _must_ be set to either true or false. JAVA_OPTS="-Xms10 ...
- linux下使用free命令查看实际内存占用(可用内存)
转:http://blog.is36.com/linux_free_command_for_memory/ linux下在终端环境下可以使用free命令看到系统实际使用内存的情况,一般用free -m ...
- Linux下jenkins改端口、解决内存溢出、版本升级
1.新版本的jenkins修改端口新版本jenkins的配置文件在/etc/sysconfig/jenkinsvi /etc/sysconfig/jenkins找到JENKINS_PORT=" ...
- Linux 下安装sql server 时 2G内存限制的最新(2019-08-15) 解决方案
关于 sqlserver 在linux下安装时有最小内存限制的问题,网上有很多类似的说明,那些操作都是正确的,如果不成功可能 “姿势”不对. 需要注意的是:不能使用最新版本!!! 不能在线下载!!! ...
- Linux下使用java获取cpu、内存使用率
原文地址:http://www.voidcn.com/article/p-yehrvmep-uo.html 思路如下:Linux系统中可以用top命令查看进程使用CPU和内存情况,通过Runtime类 ...
随机推荐
- Java(27)集合二List
作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228435.html 博客主页:https://www.cnblogs.com/testero ...
- 基于Apache Hudi 的CDC数据入湖
作者:李少锋 文章目录: 一.CDC背景介绍 二.CDC数据入湖 三.Hudi核心设计 四.Hudi未来规划 1. CDC背景介绍 首先我们介绍什么是CDC?CDC的全称是Change data Ca ...
- Coursera Deep Learning笔记 改善深层神经网络:超参数调试 正则化以及梯度相关
笔记:Andrew Ng's Deeping Learning视频 参考:https://xienaoban.github.io/posts/41302.html 参考:https://blog.cs ...
- Java版人脸检测详解下篇:编码
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- Python使用阿里云OSS服务
Python使用阿里云OSS服务 前言: 在远程搭建了一个平台,通过改远程平台进行数据的采集,需要将数据内容传送至本地进行处理:为了实现该功能,考虑了阿里云的OSS对象储存的服务. 40G包月只需1元 ...
- Java:TreeMap类小记
Java:TreeMap类小记 对 Java 中的 TreeMap类,做一个微不足道的小小小小记 概述 前言:之前已经小小分析了一波 HashMap类.HashTable类.ConcurrentHas ...
- 第3次 Beta Scrum Meeting
本次会议为Beta阶段第3次Scrum Meeting会议 会议概要 会议时间:2021年6月2日 会议地点:「腾讯会议」线上进行 会议时长:0.5小时 会议内容简介:对完成工作进行阶段性汇报:对下一 ...
- 热身训练4 Article
Article 在这个学期即将结束时,DRD开始写他的最后一篇文章. DRD使用著名的Macrohard的软件World来写他的文章. 不幸的是,这个软件相当不稳定,它总是崩溃. DRD需要在他的文章 ...
- camera HSYNC:VSYNC
HSYNC:行锁存,换行信号VSYNC:祯锁存,换页信号 320×240的屏,每一行需要输入320个脉冲来依次移位.锁存进一行的数据,然后来个HSYNC 脉冲换一行:这样依次输入240行之后换行同时来 ...
- Linux零基础之shell基础编程入门
从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...