我使用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()而没有任何改变。
...我需要使用线程 - 它将在多浦机器上运行,其中线程是使用它的“全功率”的唯一选择(据我所知)
答案 0 :(得分:6)
calc()
函数退出后,calc
中字符串使用的内存将被释放。删除每个tst
对象与它无关,因为没有类成员。
我认为您看到的是可能有200个线程正在运行。由于您分离了所有线程并且从未加入它们,因此您无法保证在开始下一个100之前前100个实际完成。
另外,因为你通过追加同时在其他线程中分配内存来反复扩展这些字符串,我想你的堆碎片确实真的 真的糟糕。假设一个线程刚刚释放了256字节字符串的内存。其他线程已经在前面运行,并且它们都不再需要256字节的字符串。这个空间现在只是浪费了,但仍然分配给该计划。
一些可能有用的选项:
.reserve(your_largest_expected_string_size)
。这有助于避免碎片问题,因为几乎所有字符串的大小都相同。答案 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)
void *testThread (void *arg) {
if (1) {
int cnt=*(int *) arg;
cout << cnt << " ";
tst *TEST=new tst;
TEST->calc();
delete TEST;
}
pthread_exit((void *)0);
}
...我在项目中发现了一些小错误,但这是主要问题