std::string memory management
I have problem with memory management with std::string.
I have application - multithread server with detached threads (i done need to join them, they will do the job and exit) and i found that after a while memory usage comes quite high. I've started to experiment where is the problem and i've created test program which demonstrating the problem
#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);
}
after first "---" the memory usage is 36开发者_运维问答4KB, after second its 19MB, after third is 33.5MB. there is 1 strange thing as well - each run showing different memory usage but mostly the last memory usage is about 50% more then after the second "---".
i've expected that if the class TEST (tst) is deleted then the string will release its memory as well - i found that threads will not do that - that's why i'm creating new tst, running it and then delete.
in my program this causing a big problem because i'm using there few strings in each thread and after a while the memory usage is over gig ;-(
is there any option how to 'empty' the memory after string?
i've tried TTT="" or TTT.clear() without any change.
...i need to use threads - it will be running on multicpu machine where threads is the only option to use it's 'full power' (as i know)
The memory used by the string in calc()
will be released after the calc
function exits. Deleting each tst
object has nothing to do with it because there are no class members.
What I think you are seeing is that there are potentially 200 threads running. Since you detach all of the threads and never join them, you have no guarantee the first 100 have actually finished before you start the next 100.
Also, because you are extending these strings over and over again by appending while at the same time allocating memory in other threads, I imagine your heap fragmentation is really really really bad. Say that one thread has just released memory for a 256 byte string. The other threads have run on ahead and none of them need a 256 byte string anymore. That space is now just wasted, but is still allocated to the program.
Some options that could help:
- Use
.reserve(your_largest_expected_string_size)
before placing data into your string. This will help avoid the fragmentation problem because almost all strings will be the same size. - A customized string allocator class. You could write your own.
- You could replace your heap allocator with something like jemalloc or tcmalloc.
- For a server application that handles clients that come and go you can get great performance using a per-client memory pool. Allocate a large memory pool in one block. Make all allocations use the pool (via the STL allocator parameters) and when the client exits free the entire pool.
I suspect that you're seeing an issue with memory fragmentation, rather than a memory leak, per se. Since you're not waiting for threads to exit, you've got as many as 200 threads all trying to allocate memory at the same time. Add to that the default memory allocation strategy for std::string
, which doubles the current allocation each time it needs to grow, and you're probably chewing up your address space pretty quickly.
One very simple thing you can do to help mitigate the problem is to preallocate the memory for the strings by using reserve()
. In this case your strings are (31 * 100,000) + 1 bytes long, so you could modify calc()
as follows:
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 ";
}
This would allocate the memory for the strings once, in a single contiguous block, and avoid much of the fragmentation that you're suffering now. A quick test of this change under Valgrind shows that the original code does allocations of about 1.5 GB total over the lifetime of the process; but the modified version does allocations of about 620 MB total.
Also worth noting that Valgrind did not reveal any memory leaks. It's always a good idea to try it out if you think you have a memory problem in your program: Valgrind home.
The string should be deallocating as soon as the calc function exits because it's a local variable.
What are you using to track your memory usage? You might be seeing memory fragmentation rather than increasing memory usage. Here's a tool that can demonstrate if your memory is getting fragmented: http://hashpling.org/asm/
I'm not familiar with pthreads, so I can't tell if there might be an issue there with the threads or something else related to the API not being deallocated.
Depending on the implementation of new and delete (or free() and malloc()) your memory might not given back to the OS after a delete/free. A memory allocator is not required to do so. The memory might still be reclaimed by a subsequent new, or after a internal memory defragmentation or garbage collection the memory usage might decrease again.
problem solved by putting 'threaded routine' in another scope - if (1) {..} because as i found on few forums - threads not calling destructors at exit then in this occasion (without added if block) every created thread will allocate memory for 'int cnt' and is never deallocated
void *testThread (void *arg) {
if (1) {
int cnt=*(int *) arg;
cout << cnt << " ";
tst *TEST=new tst;
TEST->calc();
delete TEST;
}
pthread_exit((void *)0);
}
...i found few more little bugs in my project but this was the main issue
精彩评论