views:

177

answers:

4

I've just written some threaded code which handles huge objects. Even when I clean up everything from an object (without deleting it), it keep eating GBs of RAM. FYI, I reproduced the issue in a smaller environment.

This code creates a structure and an object containing a vector of pointers to the structure. Then I fill the object with some pointers to the structure (created with new). I suppose that the object should own only the size of the pointers, not the size of all pointed structures, but when I run the code, the size of the object uses 300mb.

When I delete all members of the vector and then clean the vector, the memory occupied (but unused now ) remains high. The only way to free this memory seems to be deleting the whole object which contained the vector. Why does the vector keep so much occupied RAM if it was only a vector of pointer? How can I free it without having to delete and re-create the object?

The function -> void f_create_heapCleaner_string creates a string then deletes it. Sometimes the heap is cleaned by this trick.

#include <string>
#include <malloc.h>

#include <vector>
#include <iostream>
using namespace std;

struct struct_test_struct {
    string s_internal_data;
};

struct struct_test_struct* BASEDATA_struct_test_struct;

class objhandle_TestStruct__class {
public:
    vector<struct struct_test_struct *> v_pointer_TestStruct;

    unsigned int ui_v_size;

    objhandle_TestStruct__class() {
        ui_v_size = 3000;
    }

    void f_create_heapCleaner_string() {
        string * s_tmp = new string();
        (*s_tmp).assign(1000000, '*');
        (*s_tmp) = "";
        delete s_tmp;
    }

    void f_create_vector(unsigned int ui_size_str) {
        cout << "  f_create_vector() start " << endl;
        malloc_stats();
        for (unsigned int ui = 0; ui < ui_v_size; ui++) {
            struct struct_test_struct * tmp_newstruct = new struct_test_struct();
            (*tmp_newstruct).s_internal_data.assign((ui_size_str + ui), '*');
            v_pointer_TestStruct.push_back(tmp_newstruct);
        }
        cout << "  f_create_vector() end " << endl;
        malloc_stats();
        }

    void f_delete_vector_content() {
            cout << "  f_delete_vector_content() start " << endl;
        malloc_stats();
        for (unsigned int ui = 0; ui < ui_v_size; ui++) {
            delete v_pointer_TestStruct[ui];
        }
        f_create_heapCleaner_string();
        cout << "  f_delete_vector_content() end " << endl;
        malloc_stats();
    }

    void f_clear_vector() {
        cout << "  f_clear_vector() start " << endl;
        malloc_stats();
        v_pointer_TestStruct.clear();
            f_create_heapCleaner_string();
        cout << "  f_clear_vector() end " << endl;
        malloc_stats();
    }

    void f_RUN_FULL_TEST(unsigned int ui_size_str) {
        cout << " .... start test with string size of = " << ui_size_str << endl;
        f_create_vector(ui_size_str);
        f_delete_vector_content();
        f_clear_vector();

    }
};

int main(int argc, char**argv) {
    objhandle_TestStruct__class * ptr_objhandle_TestStruct__class = new         objhandle_TestStruct__class();
    (*ptr_objhandle_TestStruct__class).f_RUN_FULL_TEST(100000);
    (*ptr_objhandle_TestStruct__class).f_RUN_FULL_TEST(10000);
    cout << " DELETE OBJECT start " << endl;
    malloc_stats();
    delete ptr_objhandle_TestStruct__class;
    cout << " DELETE OBJECT finished " << endl;
    malloc_stats();

    return 0;
}

--- compile with: g++ -o a test.cc

then ./a

Output:

    .... start test with string size of = 100000
  f_create_vector() start
Arena 0:
system bytes     =     135168
in use bytes     =         48
Total (incl. mmap):
system bytes     =     135168
in use bytes     =         48
max mmap regions =          0
max mmap bytes   =          0
  f_create_vector() end
Arena 0:
system bytes     =  309997568
in use bytes     =  309972064
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =  309972064
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() start
Arena 0:
system bytes     =  309997568
in use bytes     =  309972064
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =  309972064
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() end
Arena 0:
system bytes     =  309997568
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() start
Arena 0:
system bytes     =  309997568
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() end
Arena 0:
system bytes     =  309997568
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
 .... start test with string size of = 10000
  f_create_vector() start
Arena 0:
system bytes     =  309997568
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
  f_create_vector() end
Arena 0:
system bytes     =  309997568
in use bytes     =   40094656
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =   40094656
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() start
Arena 0:
system bytes     =  309997568
in use bytes     =   40094656
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =   40094656
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() end
Arena 0:
system bytes     =  250077184
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  250077184
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() start
Arena 0:
system bytes     =  250077184
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  250077184
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() end
Arena 0:
system bytes     =  250077184
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  250077184
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
 DELETE OBJECT start
Arena 0:
system bytes     =  250077184
in use bytes     =      32832
Total (incl. mmap):
system bytes     =  250077184
in use bytes     =      32832
max mmap regions =          0
max mmap bytes   =          0
 DELETE OBJECT finished
Arena 0:
system bytes     =     135168
in use bytes     =          0
Total (incl. mmap):
system bytes     =     135168
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

Thank you, Francesco

-------------- edit using and object container between the object and the structure, this release the memory when deleted.. struct struct_test_struct { string s_internal_data; };

class objstruct_test_struct_OWNER {
    public:
    vector<struct struct_test_struct *> v_pointer_TestStruct;
};
class objhandle_TestStruct__class {
public:

    class objstruct_test_struct_OWNER * ptr_OBJ;

    unsigned int ui_v_size;

    objhandle_TestStruct__class() {
        ui_v_size = 3000;
    }
.........
    void f_create_vector(unsigned int ui_size_str) {
.....
        ptr_OBJ = new objstruct_test_struct_OWNER();
        cout << "  f_create_vector() start " << endl;
        malloc_stats();
        for (unsigned int ui = 0; ui < ui_v_size; ui++) {
            struct struct_test_struct * tmp_newstruct = new struct_test_struct();
            (*tmp_newstruct).s_internal_data.assign((ui_size_str + ui), '*');
            (*ptr_OBJ).v_pointer_TestStruct.push_back(tmp_newstruct);
        }
.........
    void f_clear_vector() {
.........
  delete ptr_OBJ;
 .........

by this way the program works, this is the output

 .... start test with string size of = 100000
  f_create_vector() start
Arena 0:
system bytes     =     135168
in use bytes     =         64
Total (incl. mmap):
system bytes     =     135168
in use bytes     =         64
max mmap regions =          0
max mmap bytes   =          0
  f_create_vector() end
Arena 0:
system bytes     =  309997568
in use bytes     =  309972080
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =  309972080
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() start
Arena 0:
system bytes     =  309997568
in use bytes     =  309972080
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =  309972080
max mmap regions =          0
max mmap bytes   =          0
  f_delete_vector_content() end
Arena 0:
system bytes     =  309997568
in use bytes     =      32848
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32848
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() start
Arena 0:
system bytes     =  309997568
in use bytes     =      32848
Total (incl. mmap):
system bytes     =  309997568
in use bytes     =      32848
max mmap regions =          0
max mmap bytes   =          0
  f_clear_vector() end
Arena 0:
system bytes     =     135168
in use bytes     =         32
Total (incl. mmap):
system bytes     =     135168
in use bytes     =         32
max mmap regions =          1
max mmap bytes   =    1007616
 .... start test with string size of = 10000
  f_create_vector() start
Arena 0:
system bytes     =     135168
in use bytes     =         64
Total (incl. mmap):
system bytes     =     135168
in use bytes     =         64
max mmap regions =          1
max mmap bytes   =    1007616
  f_create_vector() end
Arena 0:
system bytes     =   40161280
in use bytes     =   40094816
Total (incl. mmap):
system bytes     =   40161280
in use bytes     =   40094816
max mmap regions =          1
max mmap bytes   =    1007616
  f_delete_vector_content() start
Arena 0:
system bytes     =   40161280
in use bytes     =   40094816
Total (incl. mmap):
system bytes     =   40161280
in use bytes     =   40094816
max mmap regions =          1
max mmap bytes   =    1007616
  f_delete_vector_content() end
Arena 0:
system bytes     =   40161280
in use bytes     =      32848
Total (incl. mmap):
system bytes     =   40161280
in use bytes     =      32848
max mmap regions =          1
max mmap bytes   =    1007616
  f_clear_vector() start
Arena 0:
system bytes     =   40161280
in use bytes     =      32848
Total (incl. mmap):
system bytes     =   40161280
in use bytes     =      32848
max mmap regions =          1
max mmap bytes   =    1007616
  f_clear_vector() end
Arena 0:
system bytes     =    1138688
in use bytes     =         32
Total (incl. mmap):
system bytes     =    1138688
in use bytes     =         32
max mmap regions =          1
max mmap bytes   =    1007616
 DELETE OBJECT start
Arena 0:
system bytes     =    1138688
in use bytes     =         32
Total (incl. mmap):
system bytes     =    1138688
in use bytes     =         32
max mmap regions =          1
max mmap bytes   =    1007616
 DELETE OBJECT finished
Arena 0:
system bytes     =    1138688
in use bytes     =          0
Total (incl. mmap):
system bytes     =    1138688
in use bytes     =          0
max mmap regions =          1
max mmap bytes   =    1007616

my issue that i'm only showing here is that objects seems to keep allocated memory for themselves till are not deleted, so seems that only way to free memory for my appliance is to place data into another sub-object and deleting it..

------------------- finally.. i've found that when the program keep a portion of memory mapped for future utilization, this is re-used as we all know and that's ok.. the problem on my multithreaded program was that malloc create these "Arenas" and when 2 malloc are called in the same moment inside a big object malloc create another "Arena" reserving a new map of memory for it.. in my program i finally end having no free ram, with 4 "Arenas" having mapped more than 3gb of ram each, but really using less than 100mb each! so the problem was both memory mapping (~impossible to free manually) and threaded access to memory wich multiply this unused ram into these "Arenas" , so i create a mutex_lock to all thread when accessing to these objects, so these are kept into the same Arena without "wasting" memory (mapped but not used) into multiple arenas..

i hope i have explained a bit my issue and resolution.. hope this could help someone else ;) thank you again, Francesco

----- i still testing.. i've seen also this http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html they say exactly what my issue was.. "In ptmalloc2 memory can never move from one arena to another. This can lead to huge amounts of wasted space. " and create a different memory allocator wich should help..

+1  A: 

Without the source code to malloc_stats, it is hard to know exactly what you are reporting.

One thing to consider however, is that if you are reporting system pages, those pages will not be freed even after you free the memory used by C/C++ (using delete or free). The reason being is that when you allocate memory using new/malloc, the C runtime library will request a segment of system memory and then subdivide that block of memory to satisfy your request. The same is true when you allocate a HUGE chunk of memory. the C RTL will allocate a large block of system memory, add that memory to its list of known blocks of available C RTL memory and then return you a pointer to a huge block of memory.

The key here is that for most standard allocators (not all and maybe not even the one you are using), when you free the memory, that memory isn't going to be returned to the operating system but kept in the list of available memory for future news and mallocs.

So if you are showing free memory in terms of system pages, you won't see that number go down when you do your free.

Torlack
i've seen that memory goes down with i free it and then i create a string and delete it, malloc seems to release the heap memory,malloc_stats come from malloc.h (i'm running centos 5 linux) and it show resource utilizations.. i've already compare that with /proc/self/stat and it's trustable, i've not used my /proc/self/stat parser here to not increase too much the codeanyway what you are saying it's half true, i've never seen the program keep memory when i use the trick of string new/delete only when i have a vector
Francesco
or map inside of an object it keep memory and never release it, even when memory is required for other objects in the program...
Francesco
+1  A: 

Take a look at Boost's shared pointer. Try to implement it in your code, which will eliminate your need for deletes. Basically, change your vector to:

vector<boost::shared_ptr<struct struct_test_struct> > v_pointer_TestStruct;

And

boost::shared_ptr<struct_test_struct> tmp_newstruct( new struct_test_struct() );

...and you can simply removed this code entirely:

    for (unsigned int ui = 0; ui < ui_v_size; ui++) {
        delete v_pointer_TestStruct[ui];
    }

Since the call to v_pointer_TestStruct.clear(); will do this internally from now on. This will basically handle all your memory allocations (for this struct at least) for you.

Gianni
+1  A: 

You need to make sure you understand EXACTLY what malloc_stats is returning.

I can't do better than point you at this great post. Snippet below..

Well, system bytes are, obviously, bytes reserved for the system. This means that they're available for the OS and the Rung 2 and below libraries, but not for your programs, which typically run on Rung 4 of the Prerog Ladder. In use bytes is, unfortunately, a typo in the library; it should be in use_r_ bytes, and it means bytes left free in the user space (Rung 5 and above) of the current program.

Roddy
A: 

Executing vector.clear() sets vector.size() == 0, but does not guarantee anything about vector.capacity(). In other words, std::vector is allowed by the Standard to keep its memory allocated, just in case you will fill it with data again.

To make vector release its memory, use Clear-and-minimize idiom:

vector<struct struct_test_struct *> tmp_empty_vector;
v_pointer_TestStruct.swap(tmp_empty_vector);
atzz
didn't worked, only placing the vector into another object than deleting it seems to work till now..
Francesco