std :: string内存管理

5 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);
}
Run Code Online (Sandbox Code Playgroud)

在第一个"---"之后,内存使用量是364KB,第二个是19MB,第三个是33.5MB.还有一个奇怪的事情 - 每个运行显示不同的内存使用,但大多数最后的内存使用量比第二个"---"后大约多50%.

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

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

是否有任何选项如何在字符串后"清空"内存?

我已经尝试过TTT =""或TTT.clear()而没有任何改变.

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

Zan*_*ynx 6

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

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

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

一些可能有用的选项:

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

  • @tominko:当我试图在我的回答中说明时,线程使堆碎片更加糟糕.每线程自定义内存分配器池或线程感知全局分配器可以帮助您解决问题. (2认同)

Eri*_*ski 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 ";
}
Run Code Online (Sandbox Code Playgroud)

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

另外值得一提的是,Valgrind的也没有透露任何内存泄漏.如果你认为你的程序中存在内存问题,那么尝试一下是个好主意: Valgrind home.


小智 2

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

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

我对 pthread 不熟悉,所以我无法判断线程是否存在问题或与未释放 API 相关的其他问题。