为什么此应用没有像预期的那样消耗那么多内存
Why this app doesn't consume as much memory as expected
我写了一个简单的应用程序来测试内存消耗。在此测试应用程序中,我创建了四个进程来持续消耗内存,除非进程退出,否则这些进程不会释放内存。
我预计此测试应用程序会消耗最多的 RAM 内存,并导致其他应用程序变慢或崩溃。但结果与预期并不相同。下面是代码:
#include <stdio.h>
#include <unistd.h>
#include <list>
#include <vector>
using namespace std;
unsigned short calcrc(unsigned char *ptr, int count)
{
unsigned short crc;
unsigned char i;
//high cpu-consumption code
//implements the CRC algorithm
//CRC is Cyclic Redundancy Code
}
void* ForkChild(void* param){
vector<unsigned char*> MemoryVector;
pid_t PID = fork();
if (PID > 0){
const int TEN_MEGA = 10 * 10 * 1024 * 1024;
unsigned char* buffer = NULL;
while(1){
buffer = NULL;
buffer = new unsigned char [TEN_MEGA];
if (buffer){
try{
calcrc(buffer, TEN_MEGA);
MemoryVector.push_back(buffer);
} catch(...){
printf("An error was throwed, but caught by our app!n");
delete [] buffer;
buffer = NULL;
}
}
else{
printf("no memory to allocate!n");
try{
if (MemoryVector.size()){
buffer = MemoryVector[0];
calcrc(buffer, TEN_MEGA);
buffer = NULL;
} else {
printf("no memory ever allocated for this Process!n");
continue;
}
} catch(...){
printf("An error was throwed -- branch 2,"
"but caught by our app!n");
buffer = NULL;
}
}
} //while(1)
} else if (PID == 0){
} else {
perror("fork error");
}
return NULL;
}
int main(){
int children = 4;
while(--children >= 0){
ForkChild(NULL);
};
while(1) sleep(1);
printf("exiting main processn");
return 0;
}
顶部命令
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2775 steve 20 0 1503m 508 312 R 99.5 0.0 1:00.46 test 2777 steve 20 0 1503m 508 312 R 96.9 0.0 1:00.54 test 2774 steve 20 0 1503m 904 708 R 96.6 0.0 0:59.92 test 2776 steve 20 0 1503m 508 312 R 96.2 0.0 1:00.57 test
虽然 CPU 很高,但内存百分比仍为 0.0。怎么可能??
自由命令
free shared buffers cached Mem: 3083796 0 55996 428296
可用内存超过 3G RAM 中的 4G。
有谁知道为什么这个测试应用程序无法按预期工作?
Linux 使用乐观内存分配:在实际写入该页之前,它不会物理分配该页内存。因此,您可以分配比可用内存多得多的内存,而不会增加系统的内存消耗。
如果你想强制系统分配(提交)一个物理页面,那么你必须写入它。
以下行不会发出任何写入,因为它是 unsigned char
的默认初始化,这是一个无操作:
buffer = new unsigned char [TEN_MEGA];
如果要强制提交,请使用零初始化:
buffer = new unsigned char [TEN_MEGA]();
将注释变成答案:
- Linux 不会为进程分配内存页,直到它写入它们(写入时复制)。
- 此外,您不会在任何地方写入缓冲区,因为
unsigned char
的默认构造函数不执行任何初始化,并且new[]
默认初始化所有项。
fork()
在父项中返回 PID,在子项中返回 0。您书面ForkChild
将执行父级的所有工作,而不是子项。
标准new
运算符永远不会返回 null;如果它无法分配内存,它将抛出(但由于过度使用,它实际上也不会在 Linux 中这样做)。这意味着您在分配后对buffer
的测试毫无意义:它总是要么接受第一个分支,要么永远不会到达测试。如果你想要一个空返回,你需要写new (std::nothrow) ...
.包括<new>
才能正常工作。
但是你的程序实际上正在做你期望它做的事情。正如答案所指出的(@Michael Foukarakis的答案),未使用的内存不会被分配。在top
程序的输出中,我注意到virt
列对于运行程序的每个进程都有大量内存。后来谷歌搜索了一下,我看到了这是什么:
VIRT -- Virtual Memory Size (KiB). The total amount of virtual memory used by the task.
它包括所有代码、数据和共享库,以及已换出的页面和已映射但未使用的页面。
如您所见,您的程序实际上确实为自己生成内存,但以页面的形式存储为虚拟内存。我认为这是一件明智的事情。
此维基页面的片段
页、内存页或虚拟页 -- 固定长度的连续虚拟内存块,它是以下各项的最小数据单位:
- 操作系统为程序执行的内存分配;以及
- 在主内存和任何其他辅助存储(如硬盘驱动器)之间传输。
。因此,程序可以寻址比计算机中物理存在的更多的(虚拟)RAM。虚拟内存是一种方案,它给用户一种使用大块连续内存空间(甚至可能比真实内存更大的)的错觉,而实际上他们的大部分工作都在辅助存储(磁盘)上。作业的固定大小块(页)或可变大小块根据需要读入主内存。
资料来源:
- http://www.computerhope.com/unix/top.htm
- https://stackoverflow.com/a/18917909/2089675
- http://en.wikipedia.org/wiki/Page_(computer_memory)
如果你想吞噬大量内存:
int mb = 0;
char* buffer;
while (1) {
buffer = malloc(1024*1024);
memset(buffer, 0, 1024*1024);
mb++;
}
我使用了这样的东西来确保在进行一些文件 I/O 时序测量时文件缓冲区缓存为空。
正如其他答案已经提到的,您的代码在分配缓冲区后永远不会写入缓冲区。这里 memset 用于写入缓冲区。
- 可视化 使用 VS Code 查找C++应用程序中的内存泄漏
- 应用程序内存使用量减少
- 在C++应用程序中使用 Tensorflow:如何释放 GPU 内存
- 任务管理器 - C/C++ 应用程序 - 分配的地址空间与已用内存
- OpenGL GLFW 应用程序 - 客户端内存限制
- 使用没有堆内存分配的接口的框架应用程序?
- 我们如何访问 Node.js 应用程序之外的内存?
- 使用 IPC 共享内存的应用程序是否可以访问彼此的代码?
- 使用 valgrind 的地块测量 c++ 应用程序的最大内存使用量(堆栈和堆)
- 为什么Linux报告了我的应用程序的内存使用情况
- 是否可以锁定应用程序中常见控件使用的内存?
- 如何以编程方式获取应用程序的内存使用情况
- 32 位应用程序内存不足
- CRT 检测到应用程序在堆缓冲区(新建/删除)类结束后写入内存
- 在本机 Android 应用中处理内存不足
- Android OpenCV 应用程序的内存问题
- 将 python 嵌入我的应用程序时内存泄漏
- C++应用程序如何使用比 RSS 内存更多的堆
- 此英特尔至强 mov 指令如何损坏我的应用内存?
- 无限分配内存应用程序崩溃