首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++新/删除:从指针数组解构对象可能不会释放内存?

C++新/删除:从指针数组解构对象可能不会释放内存?
EN

Stack Overflow用户
提问于 2016-12-21 02:49:49
回答 2查看 441关注 0票数 0

我有一个奇怪的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时的前一个字符串大小,否则内存不会释放。

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-21 03:04:20

现在检查这个进程的内存使用情况(RSS),

这给出了操作系统为进程分配了多少内存。

使用delete删除对象(这也适用于malloc()编辑的free()ed内存,但就这个答案而言,我只引用newdelete)不一定将释放的内存返回到操作系统。您不一定会在流程的RSS中看到这一点。

显示的代码似乎是有序的。最后,它勤奋地delete的所有newed对象。然而,这并不能保证进程实际上会丢弃分配的内存,并将其返回到操作系统。通常,释放的内存量会被放入可用内存块的内部池中,这是由C++库维护的。当通过new创建对象时,只有在可用内存池中找不到足够大的连续内存块时,才能从操作系统中获得额外的内存。

换句话说,显示的代码没有什么问题。

传统的操作系统通常将内存分配给正在运行的进程,以中等到较大的块;一次从几千字节到几兆字节不等。创建一个小对象(几字节长)通常会导致从操作系统分配一个这样的块,其中的一小部分被用来构造新对象,然后将剩下的内存池放置在一个可用内存池中,并用于构造稍后被new编辑的任何对象。

相反,对象的delete将其内存量放置在可用内存池中。如果有足够数量的对象被delete编辑,从而释放包含一个或多个内存页的连续块,那么这些页面可能会被释放回操作系统,但这并不典型,而且很少见。一旦进程分配了内存,它就会被进程吞噬,直到它终止为止。

票数 0
EN

Stack Overflow用户

发布于 2016-12-21 03:02:09

在用户级别上分配的内存(newmalloc)与操作系统为进程分配的内存之间存在差异。C++库本身有一层代码,操作系统提供的进程中有内存管理代码(在Windows上称为NT )。这意味着当您使用deletefree释放内存时,这一中间层代码可能会立即将该内存释放给操作系统,也可能不会释放,因为您希望再次分配它。这是有意义的,因为这很有可能会提高整体性能。

这种行为是特定于系统的。在Windows上,它可能看起来是一种方式,比方说,在iOS上是非常不同的。

还请注意,在应用程序中存储指针的方式(即在vectorlist中的指针数组中)并不重要。OS对此一无所知。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41254339

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档