Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible memory leak using C++ string

Consider the following C++ program:

#include <cstdlib> // for exit(3)
#include <string>
#include <iostream>
using namespace std;

void die()
{
    exit(0);
}

int main()
{
    string s("Hello, World!");
    cout << s << endl;
    die();
}

Running this through valgrind shows this (some output trimmed for brevity):

==1643== HEAP SUMMARY:
==1643==     in use at exit: 26 bytes in 1 blocks
==1643==   total heap usage: 1 allocs, 0 frees, 26 bytes allocated
==1643==
==1643== LEAK SUMMARY:
==1643==    definitely lost: 0 bytes in 0 blocks
==1643==    indirectly lost: 0 bytes in 0 blocks
==1643==      possibly lost: 26 bytes in 1 blocks
==1643==    still reachable: 0 bytes in 0 blocks
==1643==         suppressed: 0 bytes in 0 blocks

As you can see, there's a possibility that 26 bytes allocated on the heap were lost. I know that the std::string class has a 12-byte struct (at least on my 32-bit x86 arch and GNU compiler 4.2.4), and "Hello, World!" with a null terminator has 14 bytes. If I understand it correctly, the 12-byte structure contains a pointer to the character string, the allocated size, and the reference count (someone correct me if I'm wrong here).

Now my questions: How are C++ strings stored with regard to the stack/heap? Does a stack object exist for a std::string (or other STL containers) when declared?

P.S. I've read somewhere that valgrind may report a false positive of a memory leak in some C++ programs that use STL containers (and "almost-containers" such as std::string). I'm not too worried about this leak, but it does pique my curiosity regarding STL containers and memory management.

like image 919
pr1268 Avatar asked Nov 22 '10 21:11

pr1268


5 Answers

Calling exit "terminates the program without leaving the current block and hence without destroying any objects with automatic storage duration".

In other words, leak or not, you shouldn't really care. When you call exit, you're saying "close this program, I no longer care about anything in it." So stop caring. :)

Obviously it's going to leak resources because you never let the destructor of the string run, absolutely regardless of how it manages those resources.

like image 184
GManNickG Avatar answered Nov 04 '22 19:11

GManNickG


Others are correct, you are leaking because you are calling exit. To be clear, the leak isn't the string allocated on the stack, it is memory allocated on the heap by the string. For example:

struct Foo { };

int main()
{
    Foo f;
    die();
}

will not cause valgrind to report a leak.

The leak is probable (instead of definite) because you have an interior pointer to memory allocated on the heap. basic_string is responsible for this. From the header on my machine:

   *  A string looks like this:
   *
   *  @code
   *                                        [_Rep]
   *                                        _M_length
   *   [basic_string<char_type>]            _M_capacity
   *   _M_dataplus                          _M_refcount
   *   _M_p ---------------->               unnamed array of char_type
   *  @endcode
   *
   *  Where the _M_p points to the first character in the string, and
   *  you cast it to a pointer-to-_Rep and subtract 1 to get a
   *  pointer to the header.

They key is that _M_p doesn't point to the start of the memory allocated on the heap, it points to the first character in the string. Here is a simple example:

struct Foo
{
    Foo()
    {
        // Allocate 4 ints.
        m_data = new int[4];
        // Move the pointer.
        ++m_data;
        // Null the pointer
        //m_data = 0;
    }
    ~Foo()
    {
        // Put the pointer back, then delete it.
        --m_data;
        delete [] m_data;
    }
    int* m_data;
};

int main()
{
    Foo f;
    die();
}

This will report a probable leak in valgrind. If you comment out the lines where I move m_data valgrind will report 'still reachable'. If you uncomment the line where I set m_data to 0 you'll get a definite leak.

The valgrind documentation has more information on probable leaks and interior pointers.

like image 30
jmnas Avatar answered Nov 04 '22 19:11

jmnas


Of course this "leaks", by exiting before s's stack frame is left you don't give s's destructor a chance to execute.

As for your question wrt std::string storage: Different implementations do different things. Some allocate some 12 bytes on the stack which is used if the string is 12 bytes or shorter. Longer strings go to the heap. Other implementations always go to the heap. Some are reference counted and with copy-on-write semantics, some not. Please turn to Scott Meyers' Effective STL, Item 15.

like image 40
dennycrane Avatar answered Nov 04 '22 19:11

dennycrane


gcc STL has private memory pool for containers and strings. You can turn this off ; look in valgrind FAQ

http://valgrind.org/docs/manual/faq.html#faq.reports

like image 1
pm100 Avatar answered Nov 04 '22 19:11

pm100


I would avoid using exit() I see no real reason to use that call. Not sure if it will cause the process to stop instantly without cleaning up the memory first although valgrind does still appear to run.

like image 1
CashCow Avatar answered Nov 04 '22 19:11

CashCow