【问题标题】:why memory allocated by program doesn't decrease?为什么程序分配的内存不会减少?
【发布时间】:2013-12-23 17:58:56
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 你是如何测量内存使用的?
  • 您的测量可能有缺陷。 x 在堆栈上分配。您不会(也不能使用您显示的代码)调用 delete 。
  • 在任务管理器->进程中
  • 尝试使用 new 分配内存。那么使用删除应该没问题。
  • 您需要了解虚拟内存和物理内存之间的区别。任务管理器很可能会向您展示(众多衡量标准之一)物理内存消耗。在不了解虚拟内存的情况下,我可以给您的唯一答案是没有内存泄漏,您可以放心,堆栈分配会像宣传的那样工作。

标签: c++ memory-management


【解决方案1】:

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

为什么会这样?

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

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

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

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

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

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

请注意,堆栈通常不能无限增长(通常,默认情况下限制为兆字节左右)。因此,分配如此庞大的具有静态存储时长的数组通常不是一个好主意。


1在windows下,有一个专门的说法:保护页
2原则上可以,但是没有简单的操作系统告诉何时可以安全地丢弃页面而不需要编译器插入额外的系统调用的方法,并且简单地保留页面并在最坏的情况下将它们换出工作正常并且容易得多。

【讨论】:

    【解决方案2】:

    对于任何大量的内存分配,一定要使用 new 和 delete;假设任何超过 1Kb 或任何可能使用超过 1Kb 内存的东西——如果这要进入一个类,你可能需要使用本地静态变量来计算实例数(int my_class::static_variable;)并为类的每个实例提供一个唯一的 id 和一个内存分配标志,以便您可以跟踪谁获得了分配的内存,谁没有分配以及谁的内存被释放 - 然后您将观察到 Task 中内存消耗的预期增加和减少经理;我跑题了。与磁盘驱动器虚拟内存分配无关。

    总的来说,在内存管理方面,使用这样的静态大小的数组是非常不负责任的,即使保证不会发生内存泄漏,这仅仅是因为你很可能不会需要立即整体分配所有内存,除非您正在进入一个遍历数组中每个条目的循环。

    【讨论】:

      猜你喜欢
      • 2020-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多