简体   繁体   English

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

[英]why memory allocated by program doesn't decrease?

There is a rule telling that local variables are deleted after end of calling function. 有一条规则告诉在调用函数结束后删除local variables

I tried to call the function (in C++) 我试着调用该函数(在C ++中)

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

and investigated that at the moment when creating array x memory allocated by program increases by some KBytes . 并研究了在创建由程序分配的数组x内存的那一刻增加了几KBytes However after end of calling function that memory does not decrease. 但是在调用函数结束后,内存不会减少。 Also function delete gives runtime error. 函数delete也会给出运行时错误。

So, why memory allocated by program doesn't decrease after the end calling function? 那么,为什么程序分配的内存在结束调用函数之后不会减少? Is there any way to delete local variable like above? 有没有办法删除上面的局部变量?

It is surprising that you see memory usage going up, since you do not initialize (or touch) the array. 令人惊讶的是,您会看到内存使用率上升,因为您没有初始化(或触摸)阵列。 On the other hand, it is not surprising at all that you do not see memory usage going down in Task Manager afterwards. 另一方面,之后在任务管理器中看不到内存使用量下降也就不足为奇了。 That's nothing to worry about, though. 不过,这没什么值得担心的。

Why is that so? 为什么会这样?

When you declare an array like this, it has automatic storage duration. 声明这样的数组时,它具有自动存储持续时间。 This means that space on the stack is allocated for it (formally, the C++ language knows of no such thing as a "stack", but this is how all implementations -- at least all implementations I ever heard of -- work like). 这意味着堆栈上的空间是为它分配的(正式地说,C ++语言不知道“堆栈”这样的东西,但这就是所有实现 - 至少我听说过的所有实现 - 的工作方式)。

Allocating space on the stack is simply a pointer decrement by some value. 在堆栈上分配空间只是一个指针递减一些值。 As long as you do not try to read or write to the memory pointed to, you can pretty much do anything to a pointer without anything happening (well, of course something happens, but not something spectacular). 只要你不尝试读取或写入指向的内存,你几乎可以对指针做任何事情而没有发生任何事情(好吧,当然会发生一些事情,但不是很壮观)。

On a low level, memory is managed in pages (usually 4 kilobytes) which map a range of virtual addresses with some access rights to physical RAM in some opaque way. 在较低级别上,内存以页面(通常为4千字节)进行管理,这些页面以一些不透明的方式映射一系列虚拟地址以及对物理RAM的一些访问权限。 The operating system makes sure that you never get to know that. 操作系统确保您永远不会知道这一点。 Now, operating systems deliberately set the page following the stack's last page to having an invalid access mode, so whenever you try to read or write a value from that page, a fault is generated 1 . 现在,操作系统故意将堆栈最后一页之后的页面设置为具有无效访问模式,因此每当您尝试从该页面读取或写入值时,都会生成错误1 When that happens, the OS checks whether the stack's maximum size has been exceeded (in which case your program is terminated). 发生这种情况时,操作系统会检查是否已超出堆栈的最大大小(在这种情况下,程序将被终止)。 If that isn't the case, the OS commits a new page, adding it to your working set and lets your program continue. 如果不是这样,操作系统会提交一个新页面,将其添加到您的工作集中,然后让您的程序继续运行。

The advantage of that is that you can have very large numbers of threads and very large theoretical stack sizes for each individual thread, but you only pay for what you use. 这样做的好处是,对于每个单独的线程,您可以拥有非常大量的线程和非常大的理论堆栈大小,但您只需支付使用的费用。

Now, what happens when you allocate an array of a hundred thousand double s is simply that the stack pointer is moved down by a value of 800,000 bytes (assuming the "usual" size of 8 bytes for a double ). 现在,当你分配一个十万double s的数组时会发生什么只是堆栈指针向下移动800,000字节的值(假设double的“通常”大小是8字节)。 If you initialize the array as well or if you touch any other variable that you declare after this array, said page fault will occur, the OS will be kicked off and will allocate the stack space. 如果您也初始化数组,或者如果您触摸此数组之后声明的任何其他变量,则会发生页面错误,操作系统将启动并分配堆栈空间。 Process Explorer will show it. Process Explorer将显示它。

Once committed, it doesn't go away any more 2 , and Task Manager will keep showing it. 一旦提交,它就不再消失2 ,任务管理器将继续显示它。 However, as soon as the function returns, the stack pointer pops back to where it was before, so you recycle that memory. 但是,只要函数返回,堆栈指针就会弹回到之前的位置,因此您可以回收该内存。

Do note that the stack can generally not grow indefinitely (often, it is by default limited to a megabyte or so). 请注意,堆栈通常无法无限增长(通常,默认情况下限制为兆字节左右)。 Therefore, allocating such huge arrays with static storage duration is generally not a good idea. 因此,分配具有静态存储持续时间的这种巨大阵列通常不是一个好主意。


1 Under Windows, there is a special term for this: guard page 1在Windows下,有一个特殊术语:保护页面
2 In principle, it could , but there is no easy way for the OS to tell when it's safe to throw away the pages without requiring the compiler to insert extra syscalls, and simply keeping the pages around and swapping them out in the worst case works fine and is a lot easier. 2原则上,它可以 ,但操作系统没有简单的方法可以告诉何时丢弃页面是安全的,而不需要编译器插入额外的系统调用,只需保留页面并在最坏的情况下将它们交换出来很好,很容易。

Definitely use new and delete for any significant amount of memory allocation; 绝对使用new和delete进行任何大量的内存分配; let's say for anything over 1Kb or anything that could use more than 1Kb of memory -- if this is going into a class, you probably need to run with a local static variable to count the number of instances (int my_class::static_variable;) and give each instance of the class a unique id and a memory allocation flag so that you can track who gets memory allocated, who doesn't and who's memory gets deallocated -- then you will observe the expected increases and decreases in memory consumption in Task Manager; 让我们说任何超过1Kb或任何可能使用超过1Kb内存的东西 - 如果这是进入一个类,你可能需要运行一个本地静态变量来计算实例数(int my_class :: static_variable;)并为该类的每个实例提供一个唯一的id和一个内存分配标志,以便您可以跟踪谁分配内存,谁不分配以及谁的内存被释放 - 然后您将观察任务中内存消耗的预期增加和减少经理; I digress. 我离题了。 Nothing to do with disk drive Virtual Memory allocations. 与磁盘驱动器虚拟内存分配无关。

Overall, using a staticly sized array like that is pretty irresponsible, when it comes to memory management, even if it is guaranteed that there won't be a memory leak, per se, simply because in all likelihood you won't need all of the memory allocated en bloc immediately, unless you are diving into a loop that iterates through every entry in the array. 总的来说,使用像这样的静态大小的阵列是非常不负责任的,当谈到内存管理时,即使保证不存在内存泄漏本身,也只是因为很可能你不需要全部直接分配整块的内存,除非你潜入循环遍历数组中的每个条目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM