Development/Tutorials/Debugging/Shared Memory Usage in KDE: Difference between revisions

    From KDE TechBase
    No edit summary
    No edit summary
    Line 130: Line 130:
      VmLib:      2912 kB
      VmLib:      2912 kB
    You see - not using libraries save place in memory, but as libraries are shared, it does not make sense to deny using libraries that are in memory anyway.
    You see - not using libraries save place in memory, but as libraries are shared, it does not make sense to deny using libraries that are in memory anyway.
    == disassemble it ==
    Now let's disassemble the small program using the command
    # objdump -d tutorial1
    [...]
    00000000004005b4 <main>:
      4005b4:      55                      push  %rbp
      4005b5:      48 89 e5                mov    %rsp,%rbp
      4005b8:      48 83 ec 20            sub    $0x20,%rsp
      4005bc:      89 7d ec                mov    %edi,-0x14(%rbp)
      4005bf:      48 89 75 e0            mov    %rsi,-0x20(%rbp)
      4005c3:      c7 45 fc 00 00 00 00    movl  $0x0,-0x4(%rbp)
      4005ca:      eb 0e                  jmp    4005da <main+0x26>
      4005cc:      bf 04 00 00 00          mov    $0x4,%edi
      4005d1:      e8 ea fe ff ff          callq  4004c0 <_Znwm@plt>
      4005d6:      83 45 fc 01            addl  $0x1,-0x4(%rbp)
      4005da:      81 7d fc 9f 86 01 00    cmpl  $0x1869f,-0x4(%rbp)
      4005e1:      0f 9e c0                setle  %al
      4005e4:      84 c0                  test  %al,%al
      4005e6:      75 e4                  jne    4005cc <main+0x18>
      4005e8:      eb fe                  jmp    4005e8 <main+0x34>
      4005ea:      90                      nop
      4005eb:      90                      nop
      4005ec:      90                      nop
      4005ed:      90                      nop
      4005ee:      90                      nop
      4005ef:      90                      nop
    [...]
    You see this is the main function in real [http://www.staerk.de/thorsten/Tutorials/Assembler_Tutorial assembler code].


    = See also =
    = See also =
    * http://www-archive.mozilla.org/projects/footprint/footprint-guide.html
    * http://www-archive.mozilla.org/projects/footprint/footprint-guide.html

    Revision as of 09:56, 16 October 2011

    This article helps understanding UNIX memory management. In UNIX a process uses basically three kinds of memory segments: shared memory segments, code segments and data segments.

    Shared memory is used by shared libraries. This memory is shared by all processes which use a certain library. Unfortunately there is no easy way to determine how much shared memory is used by how many processes. So a process can use 10Mb of shared memory, but you don't know whether this memory is shared with 1, 2 or 10 processes. So if you have 10 processes who each use 10Mb of shared memory this actually requires 10Mb in the best case and 100Mb in the worst case.

    Code segments contain the actual executable code of your program. This memory is shared by all processes of this same program. If you start your program 5 times, it needs to load the code segment of your program only once.

    Data segments contain the data of your program. This kind of memory is very important because the data segments of a process are not shared with other processes. Starting the same program 5 times makes that the data segments are 5 times in memory.

    The size reported by ps auxf is typically just the numbers for shared, code and data added. This is not a very accurate representation of the memory usage of an application.

    KDE applications tend to be reported as quite large because the numbers reported include the size of the shared memory segments. This size is added to the size of each KDE application while in practice the shared memory segments appear in memory only once. This is rather illusive, imagine how the output of ps would look like if it included the size of the UNIX kernel for each process!

    Instead of looking at the output of ps you get a better idea of the actual memory usage of an application by looking at the output of

    cat /proc/<pid-of-process>/status.
    

    Example program

    To demonstrate this, let's write a memory leaking program:

    main.cpp

    #include <KAboutData>
    #include <KApplication>
    #include <KCmdLineArgs>
    #include <KMessageBox>
    
    int main (int argc, char *argv[])
    {
        KAboutData aboutData( "tutorial1", 0, ki18n("Tutorial 1"), "1.0",
                              ki18n("Displays a KMessageBox popup") );
     
        KCmdLineArgs::init( argc, argv, &aboutData );
        KApplication app;
    
        for ( int i=0; i<100000; i++ ) new QString();
    
        KMessageBox::questionYesNo( 0, i18n( "Hello World" ) );
        int* i;
        return 0;
    }
    

    CMakeLists.txt

    project (tutorial1)
    find_package(KDE4 REQUIRED)
    include (KDE4Defaults)
    include_directories(${KDE4_INCLUDES})
    set(tutorial1_SRCS main.cpp)
    kde4_add_executable(tutorial1 ${tutorial1_SRCS})
    target_link_libraries(tutorial1 ${KDE4_KDEUI_LIBS})
    install(TARGETS tutorial1  ${INSTALL_TARGETS_DEFAULT_ARGS})
    

    Compile and link this program:

    cmake . && make -j4
    

    Run it:

    ./tutorial1 &
    [3] 22733
    

    In this case the program gets the process ID 22733. We look at its memory consumption:

    cat /proc/22733/status
    VmRSS:     19772 kB
    VmData:     5776 kB
    VmStk:        84 kB
    VmExe:         8 kB
    VmLib:     26804 kB
    

    If we change the "100000" in the program code to "1", we get a different picture:

    VmRSS:     16624 kB
    VmData:     2652 kB
    VmStk:        84 kB
    VmExe:         8 kB
    VmLib:     26804 kB
    

    So we see the heap is counted to VmData and contained in VmRSS.

    Why is it so big

    Probably VmLib is so big because it contains all library code in memory needed for KDE. Let's see what the loader thinks are our program's dependencies:

    # ldd tutorial1
           linux-vdso.so.1 =>  (0x00007fff739f3000)
           libkdeui.so.5 => /usr/local/lib64/libkdeui.so.5 (0x00007fdb6448c000)
           libkdecore.so.5 => /usr/local/lib64/libkdecore.so.5 (0x00007fdb63f31000)
           libQtDBus.so.4 => /usr/local/lib64/libQtDBus.so.4 (0x00007fdb63cb5000)
           libQtCore.so.4 => /usr/local/lib64/libQtCore.so.4 (0x00007fdb6380b000)
    [...]
    

    We gonna remove the KDE and Qt stuff, so let's write a new main.cpp:

    int main (int argc, char *argv[])
    {
       for ( int i=0; i<100000; i++ ) new int;
       while (true);
       return 0;
    }
    

    And compile and link it with the C++ libraries:

    # g++ -o tutorial1 main.cpp
    

    Why do we need the C++ libraries (g++ is basically gcc -lstdc++)? Because we have a call to the new keyword in the program. What are the dependencies now?

    # ldd tutorial1
           linux-vdso.so.1 =>  (0x00007fffd3bff000)
           libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fe2437a5000)
           libm.so.6 => /lib64/libm.so.6 (0x00007fe24354e000)
           libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe243338000)
           libc.so.6 => /lib64/libc.so.6 (0x00007fe242fcb000)
           /lib64/ld-linux-x86-64.so.2 (0x00007fe243aae000)
    

    that's all.

    Note that this program runs until you terminate it with CTRL_C as we are not using KDE's messagebox any more.

    # cat /proc/21028/status | grep VmLib
    VmLib:      2912 kB
    

    You see - not using libraries save place in memory, but as libraries are shared, it does not make sense to deny using libraries that are in memory anyway.

    disassemble it

    Now let's disassemble the small program using the command

    # objdump -d tutorial1
    [...]
    00000000004005b4 <main>:
     4005b4:       55                      push   %rbp
     4005b5:       48 89 e5                mov    %rsp,%rbp
     4005b8:       48 83 ec 20             sub    $0x20,%rsp
     4005bc:       89 7d ec                mov    %edi,-0x14(%rbp)
     4005bf:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
     4005c3:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
     4005ca:       eb 0e                   jmp    4005da <main+0x26>
     4005cc:       bf 04 00 00 00          mov    $0x4,%edi
     4005d1:       e8 ea fe ff ff          callq  4004c0 <_Znwm@plt>
     4005d6:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
     4005da:       81 7d fc 9f 86 01 00    cmpl   $0x1869f,-0x4(%rbp)
     4005e1:       0f 9e c0                setle  %al
     4005e4:       84 c0                   test   %al,%al
     4005e6:       75 e4                   jne    4005cc <main+0x18>
     4005e8:       eb fe                   jmp    4005e8 <main+0x34>
     4005ea:       90                      nop
     4005eb:       90                      nop
     4005ec:       90                      nop
     4005ed:       90                      nop
     4005ee:       90                      nop
     4005ef:       90                      nop
    [...]
    

    You see this is the main function in real assembler code.

    See also