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

Jump to: navigation, search
(Example program: Add some syntax highlighting)
(Replaced content with "{{Moved To Community | Guidelines_and_HOWTOs/Debugging }}")
 
Line 1: Line 1:
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'''.
+
{{Moved To Community | Guidelines_and_HOWTOs/Debugging }}
 
+
= Terms =
+
'''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 [http://man-wiki.net/index.php/Ps 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'''
+
<syntaxhighlight lang="cpp-qt">
+
#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;
+
}
+
</syntaxhighlight>
+
'''CMakeLists.txt'''
+
<syntaxhighlight lang="cmake">
+
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})
+
</syntaxhighlight>
+
 
+
Compile and link this program:
+
 
+
<syntaxhighlight lang="bash">
+
cmake . && make -j4
+
</syntaxhighlight>
+
 
+
Run it:
+
<syntaxhighlight lang="bash">
+
./tutorial1 &
+
[3] 22733
+
</syntaxhighlight>
+
 
+
In this case the program gets the process ID 22733. We look at its memory consumption:
+
<pre>
+
cat /proc/22733/status
+
VmRSS:    19772 kB
+
VmData:    5776 kB
+
VmStk:        84 kB
+
VmExe:        8 kB
+
VmLib:    26804 kB
+
</pre>
+
If we change the "100000" in the program code to "1", we get a different picture:
+
<pre>
+
VmRSS:    16624 kB
+
VmData:    2652 kB
+
VmStk:        84 kB
+
VmExe:        8 kB
+
VmLib:    26804 kB
+
</pre>
+
So we see the heap is counted to <tt>VmData</tt> and contained in <tt>VmRSS</tt>.
+
 
+
== 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 [http://www.staerk.de/thorsten/Tutorials/Assembler_Tutorial assembler code].
+
 
+
== find out its symbols ==
+
Let's find out what symbols it contains:
+
<pre>
+
# nm tutorial1
+
0000000000600e10 d _DYNAMIC
+
0000000000600fe8 d _GLOBAL_OFFSET_TABLE_
+
00000000004006e0 R _IO_stdin_used
+
                w _Jv_RegisterClasses
+
                U _Znwm@@GLIBCXX_3.4
+
0000000000600df0 d __CTOR_END__
+
0000000000600de8 d __CTOR_LIST__
+
0000000000600e00 D __DTOR_END__
+
0000000000600df8 d __DTOR_LIST__
+
00000000004007a8 r __FRAME_END__
+
0000000000600e08 d __JCR_END__
+
0000000000600e08 d __JCR_LIST__
+
0000000000601020 A __bss_start
+
0000000000601010 D __data_start
+
0000000000400690 t __do_global_ctors_aux
+
0000000000400520 t __do_global_dtors_aux
+
0000000000601018 D __dso_handle
+
                w __gmon_start__
+
0000000000600de4 d __init_array_end
+
0000000000600de4 d __init_array_start
+
0000000000400680 T __libc_csu_fini
+
00000000004005f0 T __libc_csu_init
+
                U __libc_start_main@@GLIBC_2.2.5
+
0000000000601020 A _edata
+
0000000000601030 A _end
+
00000000004006c8 T _fini
+
0000000000400488 T _init
+
00000000004006d8 t _real_fini
+
00000000004004d0 T _start
+
00000000004004fc t call_gmon_start
+
0000000000601020 b completed.5939
+
0000000000601010 W data_start
+
0000000000601028 b dtor_idx.5941
+
0000000000400590 t frame_dummy
+
00000000004005b4 T main
+
</pre>
+
Now according to [http://man-wiki.net/index.php/Nm nm's man page] T stands for text which stands for code segment, and D stands for the data segment.
+
 
+
= See also =
+
* http://www-archive.mozilla.org/projects/footprint/footprint-guide.html
+

Latest revision as of 09:00, 5 August 2016

This page is now on the community wiki.


This page was last modified on 5 August 2016, at 09:00. Content is available under Creative Commons License SA 4.0 unless otherwise noted.