我有一个奇怪的C++新/删除问题,在某些情况下,我从指针数组中删除(解构)一个对象,但是内存似乎没有释放。请参阅下面的代码和评论。
如果运行从步骤1到步骤6的所有步骤,则在步骤4或步骤5之后不会释放内存。
如果运行步骤1、步骤2、步骤3、步骤4和步骤6(注释和跳过步骤5),内存不会在步骤4或步骤6之后释放。
如果运行步骤-3,步骤-4和步骤-6(注释和跳过步骤-1,步骤-2和步骤-5),内存可以按预期在步骤-4或步骤-6之后释放。
如果运行从步骤1到步骤6的所有步骤,但在步骤3,新字符串大小大于步骤1的前一个字符串大小,则内存可以按预期在步骤4或步骤5或步骤6之后释放。
因此,一般来说,如果不运行步骤1和步骤2,一切看起来都很好。但是,如果在步骤1和步骤2之前运行步骤1和步骤2,就会发生这样的情况:除非新的字符串大小大于步骤1时的前一个字符串大小,否则内存不会释放。
#include <iostream>
#include <list>
#include <unistd.h>
struct MyClass {
std::string str;
MyClass() {}
~MyClass() {}
};
int main(int argc, char* argv[]) {
std::list<MyClass*> mylist;
// Step-1: create 100 MyClass pointer array,
// then construct 20 MyClass objects with 1MB's string for each
// and put them into a list
MyClass** pt1 = new MyClass*[100];
for (int i = 0; i < 20; ++i) {
std::string tmp_str(1024*1024, 'a'); // 1MB
pt1[i] = new MyClass();
pt1[i]->str = tmp_str;
mylist.push_back(pt1[i]);
}
std::cout << "Step-1: creating done: " << mylist.size() << std::endl;
sleep(10); // now check the memory usage of this process, it should use
// about 20MB memory
// Step-2: delete all the MyClass objects from the list,
// then delete pt1
while (1) {
std::list<MyClass*>::iterator it = mylist.begin();
if (it == mylist.end())
break;
delete *it;
mylist.erase(it);
}
delete [] pt1;
pt1 = NULL;
std::cout << "Step-2: deleting done, left: " << mylist.size()
<< std::endl;
sleep(10); // now check the memory usage (RSS) of this process,
// it should reduce about 20MB memory
// Step-3: create another 100 MyClass pointer array,
// then construct 10 MyClass objects with 1MB's string for each
// and put them into a list
MyClass** pt2 = new MyClass*[100];
for (int i = 0; i < 10; ++i) {
std::string tmp_str2(1024*1024, 'b');
pt2[i] = new MyClass();
pt2[i]->str = tmp_str2;
mylist.push_back(pt2[i]);
}
std::cout << "Step-3: creating done: " << mylist.size() << std::endl;
sleep(10); // now check the memory usage (RSS) of this process
// it should use about 10MB memory
// Step-4: delete 4 MyClass objects from the list, NOT all of them
int j = 0;
while (1) {
std::list<MyClass*>::iterator it = mylist.begin();
if (it == mylist.end() || ++j == 5)
break;
delete *it;
mylist.erase(it);
}
std::cout << "Step-4: deleting done, left: " << mylist.size()
<< std::endl;
sleep(10); // now check the memory usage (RSS) of this process,
// we expect it should reduce about 4MB memory,
// but it still uses about 10MB memory and seems
// no memory is freed.
// Step-5: delete all the left MyClass objects from the list
while (1) {
std::list<MyClass*>::iterator it = mylist.begin();
if (it == mylist.end())
break;
delete *it;
mylist.erase(it);
}
std::cout << "Step-5: deleting done, left: " << mylist.size()
<< std::endl;
sleep(10); // now check the memory usage (RSS) of this process,
// we expect it should reduce about 10MB memory,
// but it still uses about 10MB memory and seems
// no memory is freed.
// Step-6: delete pt2
delete [] pt2;
pt2 = NULL;
std::cout << "Step-6: deleting array done" << std::endl;
sleep(10); // now check the memory usage (RSS) of this process,
// if we run Step-5 before, then the memory will reduce
// about 10MB,
// but if we didn't run Step-5 before, then the memory will
// still be about 10MB and seems no memory is freed.
return 0;
}发布于 2016-12-21 03:04:20
现在检查这个进程的内存使用情况(RSS),
这给出了操作系统为进程分配了多少内存。
使用delete删除对象(这也适用于malloc()编辑的free()ed内存,但就这个答案而言,我只引用new和delete)不一定将释放的内存返回到操作系统。您不一定会在流程的RSS中看到这一点。
显示的代码似乎是有序的。最后,它勤奋地delete的所有newed对象。然而,这并不能保证进程实际上会丢弃分配的内存,并将其返回到操作系统。通常,释放的内存量会被放入可用内存块的内部池中,这是由C++库维护的。当通过new创建对象时,只有在可用内存池中找不到足够大的连续内存块时,才能从操作系统中获得额外的内存。
换句话说,显示的代码没有什么问题。
传统的操作系统通常将内存分配给正在运行的进程,以中等到较大的块;一次从几千字节到几兆字节不等。创建一个小对象(几字节长)通常会导致从操作系统分配一个这样的块,其中的一小部分被用来构造新对象,然后将剩下的内存池放置在一个可用内存池中,并用于构造稍后被new编辑的任何对象。
相反,对象的delete将其内存量放置在可用内存池中。如果有足够数量的对象被delete编辑,从而释放包含一个或多个内存页的连续块,那么这些页面可能会被释放回操作系统,但这并不典型,而且很少见。一旦进程分配了内存,它就会被进程吞噬,直到它终止为止。
发布于 2016-12-21 03:02:09
在用户级别上分配的内存(new或malloc)与操作系统为进程分配的内存之间存在差异。C++库本身有一层代码,操作系统提供的进程中有内存管理代码(在Windows上称为NT )。这意味着当您使用delete或free释放内存时,这一中间层代码可能会立即将该内存释放给操作系统,也可能不会释放,因为您希望再次分配它。这是有意义的,因为这很有可能会提高整体性能。
这种行为是特定于系统的。在Windows上,它可能看起来是一种方式,比方说,在iOS上是非常不同的。
还请注意,在应用程序中存储指针的方式(即在vector或list中的指针数组中)并不重要。OS对此一无所知。
https://stackoverflow.com/questions/41254339
复制相似问题