Take the following trivial program:
#include <iostream> int main() { return 0; }
If I run this using valgrind, I'm told that there are 72,704 bytes in 1 blocks
that are still reachable
. There have been extensive discussions on SO about whether or not to worry about still reachable warnings--I'm not concerned about that. I'd just like to understand how simply including a standard library header could cause a still reachable warning, when none of the objects from that library were allocated in the program itself.
Here is the full valgrind
output:
$ valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./ValgrindTest ==27671== Memcheck, a memory error detector ==27671== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==27671== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==27671== Command: ./ValgrindTest ==27671== ==27671== ==27671== HEAP SUMMARY: ==27671== in use at exit: 72,704 bytes in 1 blocks ==27671== total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated ==27671== ==27671== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1 ==27671== at 0x4C2AB9D: malloc (vg_replace_malloc.c:296) ==27671== by 0x4EC060F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21) ==27671== by 0x400F305: call_init.part.0 (dl-init.c:85) ==27671== by 0x400F3DE: call_init (dl-init.c:52) ==27671== by 0x400F3DE: _dl_init (dl-init.c:134) ==27671== by 0x40016E9: ??? (in /lib/x86_64-linux-gnu/ld-2.15.so) ==27671== ==27671== LEAK SUMMARY: ==27671== definitely lost: 0 bytes in 0 blocks ==27671== indirectly lost: 0 bytes in 0 blocks ==27671== possibly lost: 0 bytes in 0 blocks ==27671== still reachable: 72,704 bytes in 1 blocks ==27671== suppressed: 0 bytes in 0 blocks ==27671== ==27671== For counts of detected and suppressed errors, rerun with: -v ==27671== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
And an object dump:
$ objdump -d ValgrindTest ValgrindTest: file format elf64-x86-64 Disassembly of section .init: 0000000000400718 <_init>: 400718: 48 83 ec 08 sub $0x8,%rsp 40071c: e8 8b 00 00 00 callq 4007ac <call_gmon_start> 400721: 48 83 c4 08 add $0x8,%rsp 400725: c3 retq Disassembly of section .plt: 0000000000400730 <_ZNSt8ios_base4InitC1Ev@plt-0x10>: 400730: ff 35 ba 08 20 00 pushq 0x2008ba(%rip) # 600ff0 <_GLOBAL_OFFSET_TABLE_+0x8> 400736: ff 25 bc 08 20 00 jmpq *0x2008bc(%rip) # 600ff8 <_GLOBAL_OFFSET_TABLE_+0x10> 40073c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000400740 <_ZNSt8ios_base4InitC1Ev@plt>: 400740: ff 25 ba 08 20 00 jmpq *0x2008ba(%rip) # 601000 <_GLOBAL_OFFSET_TABLE_+0x18> 400746: 68 00 00 00 00 pushq $0x0 40074b: e9 e0 ff ff ff jmpq 400730 <_init+0x18> 0000000000400750 <__libc_start_main@plt>: 400750: ff 25 b2 08 20 00 jmpq *0x2008b2(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x20> 400756: 68 01 00 00 00 pushq $0x1 40075b: e9 d0 ff ff ff jmpq 400730 <_init+0x18> 0000000000400760 <__cxa_atexit@plt>: 400760: ff 25 aa 08 20 00 jmpq *0x2008aa(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x28> 400766: 68 02 00 00 00 pushq $0x2 40076b: e9 c0 ff ff ff jmpq 400730 <_init+0x18> 0000000000400770 <_ZNSt8ios_base4InitD1Ev@plt>: 400770: ff 25 a2 08 20 00 jmpq *0x2008a2(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x30> 400776: 68 03 00 00 00 pushq $0x3 40077b: e9 b0 ff ff ff jmpq 400730 <_init+0x18> Disassembly of section .text: 0000000000400780 <_start>: 400780: 31 ed xor %ebp,%ebp 400782: 49 89 d1 mov %rdx,%r9 400785: 5e pop %rsi 400786: 48 89 e2 mov %rsp,%rdx 400789: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 40078d: 50 push %rax 40078e: 54 push %rsp 40078f: 49 c7 c0 80 09 40 00 mov $0x400980,%r8 400796: 48 c7 c1 f0 08 40 00 mov $0x4008f0,%rcx 40079d: 48 c7 c7 90 08 40 00 mov $0x400890,%rdi 4007a4: e8 a7 ff ff ff callq 400750 <__libc_start_main@plt> 4007a9: f4 hlt 4007aa: 90 nop 4007ab: 90 nop 00000000004007ac <call_gmon_start>: 4007ac: 48 83 ec 08 sub $0x8,%rsp 4007b0: 48 8b 05 29 08 20 00 mov 0x200829(%rip),%rax # 600fe0 <_DYNAMIC+0x1f0> 4007b7: 48 85 c0 test %rax,%rax 4007ba: 74 02 je 4007be <call_gmon_start+0x12> 4007bc: ff d0 callq *%rax 4007be: 48 83 c4 08 add $0x8,%rsp 4007c2: c3 retq 4007c3: 90 nop 4007c4: 90 nop 4007c5: 90 nop 4007c6: 90 nop 4007c7: 90 nop 4007c8: 90 nop 4007c9: 90 nop 4007ca: 90 nop 4007cb: 90 nop 4007cc: 90 nop 4007cd: 90 nop 4007ce: 90 nop 4007cf: 90 nop 00000000004007d0 <deregister_tm_clones>: 4007d0: b8 37 10 60 00 mov $0x601037,%eax 4007d5: 55 push %rbp 4007d6: 48 2d 30 10 60 00 sub $0x601030,%rax 4007dc: 48 83 f8 0e cmp $0xe,%rax 4007e0: 48 89 e5 mov %rsp,%rbp 4007e3: 77 02 ja 4007e7 <deregister_tm_clones+0x17> 4007e5: 5d pop %rbp 4007e6: c3 retq 4007e7: b8 00 00 00 00 mov $0x0,%eax 4007ec: 48 85 c0 test %rax,%rax 4007ef: 74 f4 je 4007e5 <deregister_tm_clones+0x15> 4007f1: 5d pop %rbp 4007f2: bf 30 10 60 00 mov $0x601030,%edi 4007f7: ff e0 jmpq *%rax 4007f9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000400800 <register_tm_clones>: 400800: b8 30 10 60 00 mov $0x601030,%eax 400805: 55 push %rbp 400806: 48 2d 30 10 60 00 sub $0x601030,%rax 40080c: 48 c1 f8 03 sar $0x3,%rax 400810: 48 89 e5 mov %rsp,%rbp 400813: 48 89 c2 mov %rax,%rdx 400816: 48 c1 ea 3f shr $0x3f,%rdx 40081a: 48 01 d0 add %rdx,%rax 40081d: 48 d1 f8 sar %rax 400820: 75 02 jne 400824 <register_tm_clones+0x24> 400822: 5d pop %rbp 400823: c3 retq 400824: ba 00 00 00 00 mov $0x0,%edx 400829: 48 85 d2 test %rdx,%rdx 40082c: 74 f4 je 400822 <register_tm_clones+0x22> 40082e: 5d pop %rbp 40082f: 48 89 c6 mov %rax,%rsi 400832: bf 30 10 60 00 mov $0x601030,%edi 400837: ff e2 jmpq *%rdx 400839: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000400840 <__do_global_dtors_aux>: 400840: 80 3d e9 07 20 00 00 cmpb $0x0,0x2007e9(%rip) # 601030 <__bss_start> 400847: 75 11 jne 40085a <__do_global_dtors_aux+0x1a> 400849: 55 push %rbp 40084a: 48 89 e5 mov %rsp,%rbp 40084d: e8 7e ff ff ff callq 4007d0 <deregister_tm_clones> 400852: 5d pop %rbp 400853: c6 05 d6 07 20 00 01 movb $0x1,0x2007d6(%rip) # 601030 <__bss_start> 40085a: f3 c3 repz retq 40085c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000400860 <frame_dummy>: 400860: 48 83 3d 80 05 20 00 cmpq $0x0,0x200580(%rip) # 600de8 <__JCR_END__> 400867: 00 400868: 74 1e je 400888 <frame_dummy+0x28> 40086a: b8 00 00 00 00 mov $0x0,%eax 40086f: 48 85 c0 test %rax,%rax 400872: 74 14 je 400888 <frame_dummy+0x28> 400874: 55 push %rbp 400875: bf e8 0d 60 00 mov $0x600de8,%edi 40087a: 48 89 e5 mov %rsp,%rbp 40087d: ff d0 callq *%rax 40087f: 5d pop %rbp 400880: e9 7b ff ff ff jmpq 400800 <register_tm_clones> 400885: 0f 1f 00 nopl (%rax) 400888: e9 73 ff ff ff jmpq 400800 <register_tm_clones> 40088d: 90 nop 40088e: 90 nop 40088f: 90 nop 0000000000400890 <main>: 400890: 55 push %rbp 400891: 48 89 e5 mov %rsp,%rbp 400894: b8 00 00 00 00 mov $0x0,%eax 400899: 5d pop %rbp 40089a: c3 retq 000000000040089b <_Z41__static_initialization_and_destruction_0ii>: 40089b: 55 push %rbp 40089c: 48 89 e5 mov %rsp,%rbp 40089f: 48 83 ec 10 sub $0x10,%rsp 4008a3: 89 7d fc mov %edi,-0x4(%rbp) 4008a6: 89 75 f8 mov %esi,-0x8(%rbp) 4008a9: 83 7d fc 01 cmpl $0x1,-0x4(%rbp) 4008ad: 75 27 jne 4008d6 <_Z41__static_initialization_and_destruction_0ii+0x3b> 4008af: 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp) 4008b6: 75 1e jne 4008d6 <_Z41__static_initialization_and_destruction_0ii+0x3b> 4008b8: bf 34 10 60 00 mov $0x601034,%edi 4008bd: e8 7e fe ff ff callq 400740 <_ZNSt8ios_base4InitC1Ev@plt> 4008c2: ba 28 10 60 00 mov $0x601028,%edx 4008c7: be 34 10 60 00 mov $0x601034,%esi 4008cc: bf 70 07 40 00 mov $0x400770,%edi 4008d1: e8 8a fe ff ff callq 400760 <__cxa_atexit@plt> 4008d6: c9 leaveq 4008d7: c3 retq 00000000004008d8 <_GLOBAL__sub_I_main>: 4008d8: 55 push %rbp 4008d9: 48 89 e5 mov %rsp,%rbp 4008dc: be ff ff 00 00 mov $0xffff,%esi 4008e1: bf 01 00 00 00 mov $0x1,%edi 4008e6: e8 b0 ff ff ff callq 40089b <_Z41__static_initialization_and_destruction_0ii> 4008eb: 5d pop %rbp 4008ec: c3 retq 4008ed: 90 nop 4008ee: 90 nop 4008ef: 90 nop 00000000004008f0 <__libc_csu_init>: 4008f0: 48 89 6c 24 d8 mov %rbp,-0x28(%rsp) 4008f5: 4c 89 64 24 e0 mov %r12,-0x20(%rsp) 4008fa: 48 8d 2d df 04 20 00 lea 0x2004df(%rip),%rbp # 600de0 <__init_array_end> 400901: 4c 8d 25 c8 04 20 00 lea 0x2004c8(%rip),%r12 # 600dd0 <__frame_dummy_init_array_entry> 400908: 4c 89 6c 24 e8 mov %r13,-0x18(%rsp) 40090d: 4c 89 74 24 f0 mov %r14,-0x10(%rsp) 400912: 4c 89 7c 24 f8 mov %r15,-0x8(%rsp) 400917: 48 89 5c 24 d0 mov %rbx,-0x30(%rsp) 40091c: 48 83 ec 38 sub $0x38,%rsp 400920: 4c 29 e5 sub %r12,%rbp 400923: 41 89 fd mov %edi,%r13d 400926: 49 89 f6 mov %rsi,%r14 400929: 48 c1 fd 03 sar $0x3,%rbp 40092d: 49 89 d7 mov %rdx,%r15 400930: e8 e3 fd ff ff callq 400718 <_init> 400935: 48 85 ed test %rbp,%rbp 400938: 74 1c je 400956 <__libc_csu_init+0x66> 40093a: 31 db xor %ebx,%ebx 40093c: 0f 1f 40 00 nopl 0x0(%rax) 400940: 4c 89 fa mov %r15,%rdx 400943: 4c 89 f6 mov %r14,%rsi 400946: 44 89 ef mov %r13d,%edi 400949: 41 ff 14 dc callq *(%r12,%rbx,8) 40094d: 48 83 c3 01 add $0x1,%rbx 400951: 48 39 eb cmp %rbp,%rbx 400954: 75 ea jne 400940 <__libc_csu_init+0x50> 400956: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx 40095b: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp 400960: 4c 8b 64 24 18 mov 0x18(%rsp),%r12 400965: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13 40096a: 4c 8b 74 24 28 mov 0x28(%rsp),%r14 40096f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15 400974: 48 83 c4 38 add $0x38,%rsp 400978: c3 retq 400979: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000400980 <__libc_csu_fini>: 400980: f3 c3 repz retq 400982: 90 nop 400983: 90 nop Disassembly of section .fini: 0000000000400984 <_fini>: 400984: 48 83 ec 08 sub $0x8,%rsp 400988: 48 83 c4 08 add $0x8,%rsp 40098c: c3 retq
For completeness, I'm using:
Ubuntu: 12.04
Valgrind: 3.10.1 3.7.0
g++: 4.8.1
NB: As a side note, this does not happen when I include other headers such as <fstream>
or <cmath>
.
valgrind only checks for memory leaks when your program exits. At that point it traces all memory reachable from the stack and global variables, and anything unreachable is considered to be a leak.
Valgrind reports two types of issues: memory errors and memory leaks. When a program dynamically allocates memory and forgets to later free it, it creates a leak. A memory leak generally won't cause a program to misbehave, crash, or give wrong answers, and is not an urgent situation.
Start up two terminal windows so that you can interact with Valgrind and GDB simultaneously. In one terminal, run Valgrind with the --vgdb-error=0 option. When running with --vgdb-error= n, Valgrind waits for n errors to occur before pausing and waiting for a connection from GDB.
It's Valgrind's fault. First, -fsanitize=leak
does not show anything. Second, Valgrind itself states that:
First of all: relax, it's probably not a bug, but a feature. Many implementations of the C++ standard libraries use their own memory pool allocators. Memory for quite a number of destructed objects is not immediately freed and given back to the OS, but kept in the pool(s) for later re-use. The fact that the pools are not freed at the exit of the program cause Valgrind to report this memory as still reachable. The behaviour not to free pools at the exit could be called a bug of the library though.
Using GCC, you can force the STL to use malloc and to free memory as soon as possible by globally disabling memory caching. Beware! Doing so will probably slow down your program, sometimes drastically.
With GCC 2.91, 2.95, 3.0 and 3.1, compile all source using the STL with -D__USE_MALLOC. Beware! This was removed from GCC starting with version 3.3.
With GCC 3.2.2 and later, you should export the environment variable GLIBCPP_FORCE_NEW before running your program.
With GCC 3.4 and later, that variable has changed name to GLIBCXX_FORCE_NEW.
[...]
I guess those alleged memory pools are freed after program's termination, in the so-called start-up code that calls main
, among the other settings. Internal functions defined outside user's code should be treated as if they didn't exist, that's why Valgrind can't (and shouldn't) see further frees.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With