Search code examples
c++containersstd

Dequeue adding and deleting show constant memory


I have a question related to memory in C++ on Linux platform. Here is my source code:

#include <deque>
#include <iostream>
#include <vector>
#include <unistd.h>               // for linux
#include <fstream>

using namespace std;

class dog
{
public:
    dog(size_t size): _size(size),
        _values_1_f(size),
        _values_2_f(size),
        _values_1_uc(_size),
        _values_2_uc(_size)
    {
    }

    virtual ~dog();

    std::vector<unsigned char> values_1_f() const;

private:

    const size_t _size;
    std::vector<unsigned char>                  _values_1_f;
    std::vector<char>                           _values_2_f;

    std::vector<unsigned char>                  _values_1_uc;
    std::vector<unsigned char>                  _values_2_uc;
};


int32_t printMem()
{
    // Open the /proc/self/statm file
    std::ifstream statm_file("/proc/self/statm");

    if (!statm_file) {
        std::cerr << "Error opening /proc/self/statm" << std::endl;
        return 1;
    }

    // Read the contents of the file
    long pages, resident, shared, text, lib, data, dt;
    statm_file >> pages >> resident >> shared >> text >> lib >> data >> dt;

    statm_file.close();

    // Get the page size
    long page_size_kb = sysconf(_SC_PAGESIZE) / 1024;

    // Calculate memory usage in KB
    long total_memory = pages * page_size_kb;
    long resident_memory = resident * page_size_kb;
    long shared_memory = shared * page_size_kb;
    long text_memory = text * page_size_kb;
    long data_memory = data * page_size_kb;

    // Print the memory usage
    std::cout << "Total memory: " << total_memory << " KB" << std::endl;
    std::cout << "Resident memory: " << resident_memory << " KB" << std::endl;
    std::cout << "Shared memory: " << shared_memory << " KB" << std::endl;
    std::cout << "Text memory: " << text_memory << " KB" << std::endl;
    std::cout << "Data memory: " << data_memory << " KB" << std::endl;
}

int main()
{
    std::deque<dog *> _dogs;

    while(1)
    {
        for(int i = 0; i < 800000; i++)
        {
            _dogs.push_back(new dog(800));
        }

        std::cout << "After memory allocation"<< std::endl<< std::endl;;

        printMem();
        std::cout << std::endl<< std::endl;;


        for (auto i = _dogs.begin(); i != _dogs.end() ; ) {
            delete *i;
            i = _dogs.erase(i); // erase returns the next iterator
        }

        _dogs.clear();

        sleep(10);
        std::cout << "After memory clean"<< std::endl;
        printMem();

        std::cout << std::endl<< std::endl;;
    }

    return 0;
}

dog::~dog()
{

}

std::vector<unsigned char> dog::values_1_f() const
{
    return _values_1_f;
}

I try to allocate, in the while loop, a set of dog and push them in the _dogs dequeue. After allocation I try to deallocate the previously allocated memory. Here is my problem. Both the call to printMem print the same message (so it seems no memory has been cleaned, even having a look at htop), but multiple iterations of the while loop do not make the memory grow up.

So my question is: do you have any idea why this happens? If the delete fails, the memory should grow up while performing iterations. If the delete succeds, the memory after the delete should be much lower that after the new operation. Any hint will be appreciated. If you have some link explaining my error, please share them, I'm quite lost in programming....


Solution

  • The way you measure memory usage only gauges how much memory the OS accounts for the process. Allocating and deallocating memory from the OS is expensive (see for example System calls overhead) and only works in full memory pages (4 kiB). Therefore the memory allocator caches small allocations for reuse.

    You can investigate the state of the glibc allocator via malloc_stats(). The internals the allocator are explained in its wiki. In particular this section:

    "freeing" memory does not actually return it to the operating system for other applications to use. The free() call marks a chunk of memory as "free to be reused" by the application, but from the operating system's point of view, the memory still "belongs" to the application.