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

时间:2013-12-23 17:58:57

标签: c++ memory-management

有一条规则告诉在调用函数结束后删除local variables

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

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

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

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

2 个答案:

答案 0 :(得分:2)

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

为什么会这样?

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

在堆栈上分配空间只是一个指针递减一些值。只要你不尝试读取或写入指向的内存,你几乎可以对指针做任何事情而没有发生任何事情(好吧,当然发生了什么,但不是很壮观)。

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

这样做的好处是,每个线程都可以拥有非常大量的线程和非常大的理论堆栈大小,但您只需支付使用的费用。

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

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

请注意,堆栈通常无法无限增长(通常,默认情况下限制为兆字节左右)。因此,分配具有静态存储持续时间的巨大阵列通常不是一个好主意。

<小时/> 1 在Windows下,有一个特殊术语:保护页面
2 原则上,可以,但操作系统没有简单的方法可以告诉您何时丢弃页面是安全的,而不需要编译器插入额外的系统调用,并且简单地保持页面并在最坏的情况下交换它们工作正常并且更容易。

答案 1 :(得分:0)

绝对使用new和delete进行任何大量的内存分配;让我们说任何超过1Kb的东西或任何可以使用超过1Kb内存的东西 - 如果进入一个类,你可能需要运行一个本地静态变量来计算实例数(int my_class: :static_variable;)并为类的每个实例提供一个唯一的id和一个内存分配标志,以便您可以跟踪谁分配了内存,谁不知道以及谁的内存被释放 - 然后你会观察任务管理器中内存消耗的预期增加和减少;我离题了。与磁盘驱动器虚拟内存分配无关。

总的来说,使用像这样的静态大小的阵列是非常不负责任的,当谈到内存管理时,即使确保不会出现内存泄漏本身,只是因为很可能你赢了& #39; t需要立即全部分配的内存,除非你潜入循环遍历数组中的每个条目。