Why is this trivial code leaking memory?

Hi everyone,

I'm trying to figure out why the following snippet

1
2
3
4
5
6
#include <vector>
int main(){
        std::vector<int>* ptv = new std::vector<int>{};
//      delete ptv;
        return 0;
}


if checked with Valgrind, produces the following output:


==2546== HEAP SUMMARY:
==2546== in use at exit: 17,794 bytes in 158 blocks
==2546== total heap usage: 172 allocs, 14 frees, 22,538 bytes allocated
==2546==
==2546== LEAK SUMMARY:
==2546== definitely lost: 24 bytes in 1 blocks
==2546== indirectly lost: 0 bytes in 0 blocks
==2546== possibly lost: 0 bytes in 0 blocks
==2546== still reachable: 4,096 bytes in 1 blocks
==2546== suppressed: 13,674 bytes in 156 blocks




Those 24 bytes would correspond to 6 integers, but I can't figure out where are they, since the vector is initialised with no elements.
Last edited on
The vector is lost. Not the contents of the vector, but the vector object itself.

On your system, it is likely that std::vector is a class type which consists of three pointers, 8 bytes each:
- a pointer to allocated storage;
- a pointer to the end of allocated storage;
- a pointer to the last element constructed within the managed storage.
Last edited on
Thanks @mbozzi for your answer. Please, tell me if my understanding, based on the vector class presented in C++ PPP by Stroustrop, is correct:

- a pointer to allocated storage;

You're referring to something allocated on the heap with new

- a pointer to the end of allocated storage;

This is the pointer to one-past-the-last element ?


- a pointer to the last element constructed within the managed storage.

I've never seen such a thing in my vector class, could you please be more specific about this point?
Last edited on
- a pointer to the last element constructed within the managed storage
My mistake, this would typically point one past the last element.

You're referring to something allocated on the heap with new

Typically yes. Although std::vector uses an allocator to obtain memory.

> - a pointer to the end of allocated storage;
This is the pointer to one-past-the-last element?

Recall that std::vector has a capacity and a size. This pointer (call it end) is set up so that end - start yields the capacity, not the size.

We can diagram a vector with a capacity of 8 and a size of 6 roughly like this. [x] is an element; [ ] is a memory cell which could contain an element:
[x]  [x]  [x]  [x]  [x]  [x]  [ ]  [ ]
 ^start                        ^finish  ^end

In the diagram start points to the beginning of the allocated memory, finish points one-past-the-last element, and end points one-past-the-end of the allocated memory.

Therefore the vector's size may be computed as finish - start; its capacity as end - start.

I'm not sure what the vector class in PPP looks like. However because PPP is a beginner's book it may be simplified somewhat compared to a production implementation in a standard library.
Last edited on
I'm not sure what the vector class in PPP looks like.

I believe it is basically a std::vector modified to always do bounds checking when accessing elements.
@mbozzi

PPP uses two integers, size and capacity, instead of two pointers (plus of course a pointer to the allocated storage on the heap).

As far as I understand, your finish is exactly what it's called size, while end represents the capacity, right?

PPP is a beginner's book it may be simplified somewhat compared to a production implementation in a standard library.

Sure, that 's true. Indeed, I found the stl implementation: https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a01069_source.html#l00070

It's a bit hard to read for me, but I've catched the three pointers you were mentioning :-) I'd like to be able to understand the spirit and the details of such codes in the future.
Last edited on
As far as I understand, your finish is exactly what it's called size, while end represents the capacity, right?

Yes!

Thanks @mbozzi for your help :-)

One last thing, that I noticed right after I posted this comment: if you look at my OP, you can read:

==2546== still reachable: 4,096 bytes in 1 blocks

why is this always happening when I debug my codes? Reading on Valgrind documentation, it's written

"Still reachable" blocks are very common and arguably not a problem.

but I can't still understand what is the source of this memory leak. It seems that I could still free that memory, but I've not found any way to do this
Last edited on
@mbozzi, @jlb,

the OP is just using a std::vector<int>, why are you talking about PPP ?

but I can't still understand what is the source of this memory leak

@OP,
you create a pointer to a std::vector but don't delete it. That's your memory leak.
Why don't you just create the vector on the stack. Use pointers only if you have to.
no pointer, no problem.
@thmm

We were talking about PPP because it's the book I'm using, and I believed (erroneously) that the actual implementation was using one pointer only and two integers size and capacity, instead of three pointers. Comparing PPP with the standard library implementation has been really useful to me, as a beginner.

but I can't still understand what is the source of this memory leak


I'm referring to the last post, not the top one. The top one is clear now. I want to understand why I have still the "still reachable block" with 4096 bytes, even if I delete the pointer, like happens in the following test-case:

1
2
3
4
5
6
7
8
#include <vector>
#include <iostream>
int main(){
        std::vector<int>* ptv = new std::vector<int>{};
        delete ptv;
        std::cout << "Hello, World! <<"\n";
        return 0;
} 


If I delete the std::cout I have no leaks, so this must have to do with the put-to operator, but I can't understand why the std::out leaks memory.
Last edited on
The code you posted in the last post will not compile with the cout line, so perhaps you are still running valgrind with your previous code?

When I fix the syntax error on line 6, compile the code, then run valgrind it doesn't show any memory leaks, since you are deleting the memory you allocated.

By the way, a couple of points. First using manual memory management should be avoided whenever possible. Second creating a std::vector * is usually a mistake.

After a quick read of PPPC++2e I can't find any examples of Stroustrop manually allocating a std::vector on the heap. Any heap allocation is done using his custom vector class. He revises the custom vector class across multiple chapters in the book.

If there IS an example, note on what page, please. :)
It could be memory allocated by the implementation and not freed, not a problem in your code.

Unfortunately I dont have access to Valgrind at the moment, but in principle it should be possible to identify the source of the leak with some clever debugging.
Last edited on
Generally, you only need worry about leaks in code that can be tracked back to a call from main().

Leaks from the run-time environment are not something you need to worry about (and not something you could easily fix either).

For example, with the delete ptv; in place, I see the following
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ valgrind --leak-check=full --show-leak-kinds=all ./a.out
==18725== Memcheck, a memory error detector
==18725== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==18725== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==18725== Command: ./a.out
==18725== 
Hello, World!
==18725== 
==18725== HEAP SUMMARY:
==18725==     in use at exit: 72,704 bytes in 1 blocks
==18725==   total heap usage: 3 allocs, 2 frees, 73,752 bytes allocated
==18725== 
==18725== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
==18725==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18725==    by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==18725==    by 0x40106F9: call_init.part.0 (dl-init.c:72)
==18725==    by 0x401080A: call_init (dl-init.c:30)
==18725==    by 0x401080A: _dl_init (dl-init.c:120)
==18725==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==18725== 
==18725== LEAK SUMMARY:
==18725==    definitely lost: 0 bytes in 0 blocks
==18725==    indirectly lost: 0 bytes in 0 blocks
==18725==      possibly lost: 0 bytes in 0 blocks
==18725==    still reachable: 72,704 bytes in 1 blocks
==18725==         suppressed: 0 bytes in 0 blocks
==18725== 
==18725== For counts of detected and suppressed errors, rerun with: -v
==18725== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 

The last remaining block is owned by the dynamic loader.
The malloc probably happens so close to the start of the program, that the free for it would be so close to the end of the process that no-one is bothered by it.

The real leaks you have to worry about are the ones where you see "definitely lost" increasing with the size of the data you process, or the length of time the process is running. Ever increasing "lost" blocks is a sure sign that your program will eventually eat all the memory.

Topic archived. No new replies allowed.