In C++ the memory used by the application is divided into five containers [52]:
text segment – containing the set of instructions for the program to execute;
data segment – containing static and global variables initialised with values, e.g.
float pi = 3.14;
– the size of the data segment is pre-determined at the time
of the compilation of the program and depends on the size of the variables in the
source code;
bss (block started by symbol) segment – containing static and global variables not
explicitly initialised with any value, e.g. int n;
– the size of the data segment is
pre-determined at the time of the compilation of the program and depends on the
size of the variables in the source code;
stack – a LIFO (last in, first out) structure containing the function calls and variables intialised in the functions – the size of the stack is pre-determined at the time of compilation of the program and depends on the operating system and development environment;
heap – containing all dynamically allocated memory (e.g. using instruction new
in
C++).
The access to data on the heap is maintained by pointers which store the memory address of
said data. If the pointer ceases to exist (e.g. because the function which contained it finished
running and hence was taken off the stack) the programmer has no way of referencing the
data on the heap again. The unused, inaccessible data will stay in the memory,
consuming valuable resources if not properly deallocated using delete
instruction.
Issues may arise if the same memory address is held by two different pointers – the
deallocation of memory can render one of the pointers empty and possibly lead to
errors.
In order to facilitate memory management, C++11 standard introduced shared pointers
(shared_ptr
) [1]. Shared pointers maintain a reference counter which is increased when
another shared pointer refers to the same memory address. The memory will only be
deallocated when all shared pointers referencing the memory are destroyed, as shown in
Figure 23.1.
Python manages memory though a private heap containing all Python objects [32]. An internal memory manager is used to ensure the memory is properly allocated and deallocated while the script runs. In contrast to C++, Python’s memory manager tries to optimise the memory usage as much as possible. For example, the same object reference is allocated to a new variable if the object already exists in memory. Another important feature of Python’s memory manager is the use of garbage collector based on reference counting. When the object is no longer referenced by any variable the reference counter is set to zero, which triggers the garbage collector to free the memory (possibly at some later time). A disadvantage of this solution is slower execution time, since the garbage collector routines have to be called periodically. Features of the Python memory manager are schematically shown in Figure 23.2
It is unusual for the Python programmer to manually modify the way Python uses memory
resources – however it is sometimes necessary, as is the case with this project. In particular,
the Python C API exposes several macros to handle reference counting, and developers can
increase or decrease the reference counter of the object using Py_INCREF
and Py_DECREF
respectively [33]. In Boost.Python
, these calls are wrapped in the py::incref
and
py::decref
functions.