c++ - 为什么程序分配的内存不会减少?

标签 c++ memory-management

有一条规则告诉局部变量在调用函数结束后被删除。

我尝试调用该函数(在 C++ 中)

void DoIt()
{   
    double x[100000];
}

并调查了在创建数组 x 时程序分配的内存增加了一些 KBytes。但是在调用函数结束后内存不会减少。函数 delete 也会给出运行时错误。

那么,为什么程序分配的内存在结束调用函数后并没有减少呢?有没有办法像上面那样删除局部变量?

最佳答案

令人惊讶的是,您看到内存使用量上升,因为您没有初始化(或接触)数组。另一方面,之后您在任务管理器中看不到内存使用率下降也就不足为奇了。不过,这没什么好担心的。

为什么会这样?

当您像这样声明一个数组时,它具有自动存储持续时间。这意味着堆栈上的空间是为它分配的(形式上,C++ 语言不知道“堆栈”这样的东西,但这就是所有实现的方式——至少我听说过的所有实现——都是这样工作的)。

在堆栈上分配空间只是指针递减某个值。只要您不尝试读取或写入指向的内存,您几乎可以对指针执行任何操作而不会发生任何事情(好吧,当然会发生某事,但不会发生什么特别的事情)。

在低级别上,内存以页面(通常为 4 KB)为单位进行管理,页面以某种不透明的方式将一系列具有某些访问权限的虚拟地址映射到物理 RAM。操作系统确保您永远不会知道这一点。现在,操作系统故意将堆栈最后一页之后的页面设置为具有无效访问模式,因此无论何时您尝试从该页面读取或写入值,都会生成错误1。发生这种情况时,操作系统会检查是否已超过堆栈的最大大小(在这种情况下,您的程序将终止)。如果不是这种情况,操作系统会提交一个新页面,将其添加到您的工作集中并让您的程序继续。

这样做的好处是您可以拥有非常多的线程和每个单独线程的非常大的理论堆栈大小,但您只需为使用的部分付费。

现在,当您分配十万个 double 的数组时会发生什么,只是堆栈指针向下移动了 800,000 字节的值(假设“通常”大小为 8 字节对于 double)。如果您也初始化该数组,或者如果您触及您在该数组之后声明的任何其他变量,则会发生页面错误,操作系统将启动并分配堆栈空间。 Process Explorer 将显示它。

一旦提交,它就不会再消失2,任务管理器会一直显示它。但是,一旦函数返回,堆栈指针就会弹出回到之前的位置,因此您可以回收该内存。

请注意,堆栈通常不能无限增长(通常默认限制为 1 MB 左右)。因此,分配如此巨大的具有静态存储持续时间的数组通常不是一个好主意。


1在Windows下,对此有一个专门的称呼:guard page
2原则上,它可以,但是操作系统没有简单的方法来判断何时可以安全地丢弃页面而不需要编译器插入额外的系统调用,并且简单地保留页面并在最坏的情况下将它们换掉就可以了,而且要容易得多。

关于c++ - 为什么程序分配的内存不会减少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20748761/

相关文章:

c++ - 使用 CUDA 和 Maya API 时命名空间发生冲突

c++ - 正确的 BOOST_FOREACH 用法?

exception - System.AccessViolationException : Attempted to read or write protected memory

c - 数组和 malloc 之间的区别

c++ - 作业 : C++ Templates With Vectors

c++ - Golang 中 binary.write 的 C++ 等价物是什么?

python - 如何使用带有 clang 绑定(bind)的 python 获取 C++ header 中的方法列表?

java - 每次都创建字符串文字吗?

ios - 何时释放 CGMutablePathRef

c++ - 结构数组的动态分配