c++clinuxgccheap-memory

An empty program that does nothing in C++ needs a heap of 204KB but not in C


Consider this empty program:

int main()
{ return 0; }

If I compile it with C++ with g++ main.cpp && strace ./a.out, and analyze the output with strace, I observed that the last lines of the output are (you can add -O3 that the effect is the same):

mprotect(0x7f71af154000, 45056, PROT_READ) = 0
mprotect(0x7f71af38b000, 4096, PROT_READ) = 0
brk(NULL)                               = 0xed2000
brk(0xf05000)                           = 0xf05000
exit_group(0)                           = ?
+++ exited with 0 +++

However, if I do instead: gcc main.cpp && strace ./a.out

mprotect(0x7f4114318000, 16384, PROT_READ) = 0
mprotect(0x7f4114547000, 4096, PROT_READ) = 0
exit_group(0)                           = ?
+++ exited with 0 +++

As you see, in C++ there's an extra brk that extends the heap by exactly 204KB (0xf05000 - 0xed2000 = 204KB after translating both to decimal). That can be easily verified by replacing that program by (coliru link):

#include <iostream>
#include <unistd.h>

int main()
{
    char buf[1024];
    sprintf(buf, "pmap -XX %u", getpid());
    std::system(buf);
    
    return 0;
}

and you can easily observe that compiling with g++ the [heap] has a size of 204KB, and with gcc the [heap] line even disappeared from the pmap output.

NOTE: By the way, and to my surprise, gcc had no problem at all with include <iostream> and with the presence of std in std::system.

What are these 204KB used for? Not worried or anything about these little 204KB but it caught my attention and I'm curious now.


Solution

  • Remove the libraries added by g++ and it will not do the extra allocation:

    g++ -nodefaultlibs -lc main.cpp
    

    With the default libraries, the C++ program gets linked to:

            /lib64/ld-linux-x86-64.so.2
            libc.so.6 => /lib64/libc.so.6
            libgcc_s.so.1 => /lib64/libgcc_s.so.1
            libm.so.6 => /lib64/libm.so.6
            libstdc++.so.6 => /lib64/libstdc++.so.6
            linux-vdso.so.1
    

    Without the default libraries + libc:

            /lib64/ld-linux-x86-64.so.2
            libc.so.6 => /lib64/libc.so.6
            linux-vdso.so.1
    

    The extra libraries being:

            libgcc_s.so.1 => /lib64/libgcc_s.so.1
            libm.so.6 => /lib64/libm.so.6
            libstdc++.so.6 => /lib64/libstdc++.so.6
    

    I wonder anyway which specific C++ lib increased the heap

    By adding the extra libraries to the linking one at a time, we can see that it's libstdc++ that causes the extra allocation, so if you can get away without the compiled part of the C++ standard library, this is what you could use:

    g++ -nodefaultlibs -lgcc_s -lm -lc
    

    Single-stepping through the program compiled with the default libraries shows the construction and destruction of misc. standard C++ objects, such as this std::atomic<std::shared_ptr<std::chrono::tzdb_list::_Node>>. The timezone database alone ought to take up a bit of space, but I'm not sure if that alone covers the full 204 KiB you observed. The conclusion is however that the C++ standard library has more objects (in addition to those in the C standard library) that are implicitly constructed (the standard iostreams for example):

    $ gdb -tui ./a.out
    (gdb) layout asm
    (gdb) start
    (fiddling about for a long time)
    (gdb) tui d
    (gdb) bt
    #0  std::atomic<std::shared_ptr<std::chrono::tzdb_list::_Node> >::~atomic (this=0x7ffff7e62810 <std::chrono::tzdb_list::_Node::_S_head_owner>, __in_chrg=<optimized out>)
        at /usr/src/debug/gcc-14.2.1-3.fc40.x86_64/obj-x86_64-redhat-linux/x86_64-redhat-linux/libstdc++-v3/include/bits/shared_ptr_atomic.h:630
    #1  0x00007ffff7a51e60 in __cxa_finalize (d=0x7ffff7e52200) at cxa_finalize.c:82
    #2  0x00007ffff7cb4947 in __do_global_dtors_aux () from /lib64/libstdc++.so.6
    #3  0x00007fffffffd750 in ?? ()
    #4  0x00007ffff7fc90f2 in _dl_call_fini (closure_map=0x7ffff7f8a000) at dl-call_fini.c:43