First, you aren't testing anything with that unused vector
. Compilers are smart, and both gcc
and clang
at -O2
compile the code above to an empty main()
(other than a single xor eax, eax
to set the return value. See the assembly here. Also, the default constructor for most vector
implementations (including gcc
and clang
) won't even allocate anything - it will wait until the first element is added before taking the expensive step of allocation.
To get a more concrete result, allocate a BIG vector (so you can distinguish it from the noise) and pass it to a method in another translation unit (or defined in a separate .cpp file), like this:
#include <vector>
void sink(std::vector<int>& v);
int main() {
std::vector<int> v(12345678);
sink(v);
}
Now when you check the assembly, you see it is actually doing something.
So the ~72,000 bytes you are seeing reported by Valgrind has nothing to do with your std::vector<int> v
and you'd probably see the same figure with a completely empty main.
Still the idea of the question and the quoted documentation stands apart from that issue and I'll answer it below.
All memory is generally freed back to the OS when the program exits, and it is the OS that enforces this, not the standard library. The OS simply cleans up all resources used by the process, including an unshared memory allocation. When Valgrind refers to "in use at exit" it is talking about before this OS cleanup occurs, since that's what you want to know to see if you are forgetting to free anything.
You don't need any separate process to handle this. It is implemented by having Valgrind track malloc
and free
calls, and perhaps some other standard allocation routines.
The comment you quoted from the FAQ about many standard library using "use their own memory pool allocators" is referring to the idea that a standard library may use another caching allocation layer on top of those which calls one of the known allocations calls like malloc
or operator new
initially when memory is needed, but when the memory is de-allocated it saves it internally in some list rather than calling the corresponding de-allocation routine (such as free
or delete
).
On subsequent allocations it will use the stuff in its internal lists in preference to going back to the standard methods (if the list is exhausted, it has to call the standard routines). This would make it invisible to Valgrind, which would consider the memory still "in use" by the application.
Because of the somewhat useless definitions of the std::allocator
stuff in old versions of C++ this wasn't heavily used, and I don't agree that "many" standard libraries use this type of pool allocator by default - at least today: I am not in fact aware of any that does this anymore between the major standard library implementations, although some did in the past. However, the allocator argument is a template parameter of each container class, so end users may also perform this customization, especially since the allocator
interface has been improved in newer standards.
Big wins in practice for such pooled allocators are (a) using thread-local, fixed size allocations for a container as all contained objects are the same size and (b) allowing the allocator to free everything in one operation when the container is destroyed rather than freeing element by element.
The documentation you quoted is a bit confusing because it talks about (not) retuning memory to the OS - but it should really say "retuning to the standard allocation routines". Valgrind does not need memory to be returned to the OS to see it as freed - it hooks all the standard routines and knows when you have freed at that level. The standard routines themselves heavily "cache" allocated memory as described above (this is common, unlike allocator routine caching which is uncommon) so if Valgrind required memory to be returned to the OS it would be quite useless at reporting "allocated memory at exit".