std :: string内存管理

时间:2011-04-15 21:52:18

标签: c++ string memory-management pthreads

我使用std :: string进行内存管理时遇到问题。

我有应用程序 - 具有分离线程的多线程服务器(我需要加入它们,它们将完成工作并退出)并且我发现在一段时间后内存使用率非常高。我已经开始尝试问题在哪里,我已经创建了演示问题的测试程序

#include <iostream>

#include <string>
#include <pthread.h>

pthread_t           thread[100];

using namespace std;

class tst {
    public:
        tst() {
            //cout << "~ Create" << endl;
        }
        ~tst() {
            //cout << "~ Delete" << endl;
        }
        void calc() {
            string TTT;
            for (int ii=0; ii<100000; ii++) {
                TTT+="abcdenbsdmnbfsmdnfbmsndbfmsndb ";
            }
        }
};

void *testThread (void *arg) {
    int cnt=*(int *) arg;
    cout << cnt << " ";
    tst *TEST=new tst;
    TEST->calc();
    delete TEST;
    pthread_exit((void *)0);
}

int main (int argc, char * const argv[]) {

cout << "---------------------------------------------------" << endl;
sleep(5);

    for (int oo=0; oo<100; oo++) {
        pthread_create(&thread[oo], NULL, testThread, &oo);
        pthread_detach(thread[oo]);
    }
    cout << endl;
    cout << "---------------------------------------------------" << endl;

    sleep(5);

    for (int oo=0; oo<100; oo++) {
        pthread_create(&thread[oo], NULL, testThread, &oo);
        pthread_detach(thread[oo]);
    }
    cout << endl;
    cout << "---------------------------------------------------" << endl;

    sleep(5);
    exit(0);
}

首先“---”后内存使用量为364KB,第二次为19MB后,第三次为33.5MB。 还有一个奇怪的事情 - 每个运行显示不同的内存使用,但大多数最后的内存使用量比第二个“---”之后多50%。

我预计如果类TEST(tst)被删除,那么字符串也会释放它的内存 - 我发现线程不会这样做 - 这就是为什么我正在创建新的tst,运行它然后删除

在我的程序中导致一个大问题,因为我在每个线程中使用了很少的字符串,并且一段时间后内存使用量超过了gig; - (

有没有选项如何在字符串后“清空”内存?

我尝试过TTT =“”或TTT.clear()而没有任何改变。

...我需要使用线程 - 它将在多浦机器上运行,其中线程是使用它的“全功率”的唯一选择(据我所知)

5 个答案:

答案 0 :(得分:6)

calc()函数退出后,calc中字符串使用的内存将被释放。删除每个tst对象与它无关,因为没有类成员。

我认为您看到的是可能有200个线程正在运行。由于您分离了所有线程并且从未加入它们,因此您无法保证在开始下一个100之前前100个实际完成。

另外,因为你通过追加同时在其他线程中分配内存来反复扩展这些字符串,我想你的堆碎片确实真的 真的糟糕。假设一个线程刚刚释放了256字节字符串的内存。其他线程已经在前面运行,并且它们都不再需要256字节的字符串。这个空间现在只是浪费了,但仍然分配给该计划。

一些可能有用的选项:

  • 在将数据放入字符串之前使用.reserve(your_largest_expected_string_size)。这有助于避免碎片问题,因为几乎所有字符串的大小都相同。
  • 自定义字符串分配器类。你可以写自己的。
  • 您可以使用jemalloc或tcmalloc等替换堆分配器。
  • 对于处理来来去去的客户端的服务器应用程序,您可以使用每客户端内存池获得出色的性能。在一个块中分配一个大内存池。使所有分配使用池(通过STL分配器参数)以及客户端退出时释放整个池。

答案 1 :(得分:5)

我怀疑你看到了内存碎片问题,而不是内存泄漏本身。由于您没有等待线程退出,因此您有多达200个线程同时尝试分配内存。再加上std::string的默认内存分配策略,每当需要增长时,它会使当前分配加倍,并且您可能很快就会占用地址空间。

可以帮助缓解问题的一个非常简单的事情是使用reserve()为字符串预分配内存。在这种情况下,您的字符串是(31 * 100,000)+ 1个字节长,因此您可以修改calc(),如下所示:

string TTT;

// We know how big the string will get, so pre-alloc memory for it to avoid the 
// inefficiencies of the default allocation strategy as the string grows.

TTT.reserve(3100001);

for (int ii=0; ii<100000; ii++) {
    TTT+="abcdenbsdmnbfsmdnfbmsndbfmsndb ";
}

这将在一个连续的块中为字符串分配一次内存,并避免你现在遭受的大部分碎片。在Valgrind下快速测试这一变化表明,原始代码在整个过程的生命周期内总共分配了大约1.5 GB的内容。但修改后的版本总共分配了大约620 MB。

另外值得注意的是,Valgrind 显示任何内存泄漏。如果你认为你的程序中存在内存问题,那么尝试一下是个好主意:Valgrind home

答案 2 :(得分:2)

一旦calc函数退出,字符串就应该释放,因为它是一个局部变量。

您使用什么来跟踪内存使用情况?您可能会看到内存碎片而不是增加内存使用量。这是一个可以证明你的记忆是否变得支离破碎的工具:http://hashpling.org/asm/

我不熟悉pthreads,所以我不知道是否存在线程或与API相关的其他内容未被解除分配的问题。

答案 3 :(得分:2)

根据new和delete(或free()和malloc()的实现),删除/释放后,您的内存可能不会返回给操作系统。内存分配器不需要这样做。内存可能仍会被后续的新内存回收,或者在内部内存碎片整理或垃圾回收后,内存使用量可能会再次降低。

答案 4 :(得分:0)

通过将'线程例程'放在另一个范围内来解决问题 - 如果(1){..} 因为我在几个论坛上发现 -​​ 线程没有在退出时调用析构函数然后在这种情况下(没有添加if块)每个创建的线程将为“int cnt”分配内存并且永远不会被释放

void *testThread (void *arg) {
    if (1) {
        int cnt=*(int *) arg;
        cout << cnt << " ";
        tst *TEST=new tst;
        TEST->calc();
        delete TEST;
    }
    pthread_exit((void *)0);
}

...我在项目中发现了一些小错误,但这是主要问题