Lecture 8
Lecture 8
Lecture 8: Memory
Management
Ignacio Vizzo and Cyrill Stachniss
Working memory or RAM
http://www.clipartkid.com
3
Address operator for pointers
Operator & returns the address of the
variable in memory
Return value type is “pointer to value type”
sizeof(pointer) is 8 bytes in 64bit systems
Example:
1 int a = 42;
2 int* a_ptr = &a;
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... n
&a
http://www.cplusplus.com/doc/tutorial/pointers/
4
Pointer to pointer
Example:
1 int a = 42;
2 int* a_ptr = &a;
3 int ** a_ptr_ptr = &a_ptr;
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... n
&a &a_ptr
6
Pointer dereferencing
7
Pointer dereferencing
1 # include <iostream >
2 using std :: cout; using std :: endl;
3 int main () {
4 int a = 42;
5 int* a_ptr = &a;
6 int b = *a_ptr;
7 cout << "a = " << a << " b = " << b << endl;
8 * a_ptr = 13;
9 cout << "a = " << a << " b = " << b << endl;
10 return 0;
11 }
Output:
1 a = 42, b = 42
2 a = 13, b = 42
8
Uninitialized pointer
1 # include <iostream >
2 using std :: cout;
3 using std :: endl;
4 int main () {
5 int* i_ptr; // BAD! Never leave unitialized !
6 cout << "ptr address : " << i_ptr << endl;
7 cout << "value under ptr: " << *i_ptr << endl;
8 i_ptr = nullptr ;
9 cout << "new ptr address : " << i_ptr << endl;
10 cout << "ptr size: " << sizeof (i_ptr) << " bytes ";
11 cout << " (" << sizeof (i_ptr) * 8 << "bit) " << endl;
12 return 0;
13 }
10
Arrays in memory and pointers
Value ar[0] ar[1] ar[2]
11
Careful! Overflow!
1 # include <iostream >
2 int main () {
3 int ar[] = {1, 2, 3};
4 // WARNING ! Iterating too far!
5 for (int i = 0; i < 6; i++){
6 std :: cout << i << ": value : " << ar[i]
7 << "\t addr:" << &ar[i] << std :: endl;
8 }
9 return 0;
10 }
obj->Func() ↔ (*obj).Func()
13
Pointers are polymorphic
Pointers are just like references, but have
additional useful properties:
Can be reassigned
Can point to “nothing” (nullptr)
Can be stored in a vector or an array
Use pointers for polymorphism
1 Derived derived ;
2 Base* ptr = & derived ;
16
Using const with pointers
Pointers can point to a const variable:
1 // Cannot change value , can reassign pointer .
2 const MyType * const_var_ptr = &var;
3 const_var_ptr = & var_other ;
stack heap
http://www.freestockphotos.biz https://pixabay.com
18
Stack memory
Static memory
Available for short term storage (scope)
Small / limited (8 MB Linux typically)
Memory allocation is fast
LIFO (Last in First out) structure
Items added to top of the stack with push
Items removed from the top with pop
19
Stack memory
stack frame
10
8
7
6
4
3
2
command: 2 x pop()
20
Heap memory
Dynamic memory
Available for long time (program runtime)
Raw modifications possible with new and
delete (usually encapsulated within a class)
Allocation is slower than stack allocations
21
Operators new and new[]
User controls memory allocation (unsafe)
Use new to allocate data:
1 // pointer variable stored on stack
2 int* int_ptr = nullptr ;
3 // 'new ' returns a pointer to memory in heap
4 int_ptr = new int;
5
6 // also works for arrays
7 float * float_ptr = nullptr ;
8 // 'new ' returns a pointer to an array on heap
9 float_ptr = new float [ number ];
23
Example: heap memory
1 # include <iostream >
2 using std :: cout; using std :: endl;
3 int main () {
4 int size = 2; int* ptr = nullptr ;
5 {
6 ptr = new int[size ];
7 ptr [0] = 42; ptr [1] = 13;
8 } // End of scope does not free heap memory !
9 // Correct access , variables still in memory .
10 for (int i = 0; i < size; ++i) {
11 cout << ptr[i] << endl;
12 }
13 delete [] ptr; // Free memory .
14 for (int i = 0; i < size; ++i) {
15 // Accessing freed memory . UNDEFINED !
16 cout << ptr[i] << endl;
17 }
18 return 0;
19 } 24
Memory leak
25
Memory leak
25
Memory leak
Heap
ptr_1
LEAKED!
ptr_2
25
Memory leak (delete)
1 int main () {
2 int * ptr_1 = nullptr ;
3 int * ptr_2 = nullptr ;
4
5 // Allocate memory for two bytes on the heap.
6 ptr_1 = new int;
7 ptr_2 = new int;
8 cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
9
10 // Overwrite ptr_2 and make it point where ptr_1
11 ptr_2 = ptr_1;
12
13 // ptr_2 overwritten , no chance to access the memory .
14 cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
15 delete ptr_1;
16 delete ptr_2;
17 return 0;
18 }
26
Error: double free or corruption
27
Tools to the rescue
Standard tools like: valgrind ./my_program
Compiler flags -fsanitize=address
Stackoverflow!
28
code-fsanitize=address
1 ===================================================
2 ==19747== ERROR: AddressSanitizer : attempting double -
free on 0 x602000000010 in thread T0:
3 # ... more stuff
4 0 x602000000010 is located 0 bytes inside of 4-byte
5 # ... even more stuff
6 SUMMARY : AddressSanitizer : double -free in operator
delete (void*, unsigned long)
7 # ... even more more stuff
8 ==19747== ABORTING
29
valgrind output
1 HEAP SUMMARY :
2 in use at exit: 4 bytes in 1 blocks
3 total heap usage: 4 allocs , 4 frees , 76 ,808 bytes
allocated
4
5 LEAK SUMMARY :
6 definitely lost: 4 bytes in 1 blocks
7 indirectly lost: 0 bytes in 0 blocks
8 possibly lost: 0 bytes in 0 blocks
9 still reachable : 0 bytes in 0 blocks
10 suppressed : 0 bytes in 0 blocks
11 Rerun with --leak -check=full to see details of leaked
memory
12
13 For counts of detected and suppressed errors , rerun
with: -v
14 ERROR SUMMARY : 1 errors from 1 contexts ( suppressed : 0
from 0)
30
Memory leak example
1 int main () {
2 double *data = nullptr ;
3 size_t size = pow (1024 , 3) / 8; // Produce 1GB
4
5 for (int i = 0; i < 4; ++i) {
6 // Allocate memory for the data.
7 data = new double [size ];
8 std :: fill(data , data + size , 1.23);
9 // Do some important work with the data here.
10 cout << " Iteration : " << i << " done. " << (i + 1)
11 << " GiB has been allocated !" << endl;
12 }
13
14 // This will only free the last allocation !
15 delete [] data;
16 int unused ;
17 std :: cin >> unused ; // Wait for user.
18 return 0;
19 } 31
Memory leak example
If we run out of memory an std::bad_alloc
error is thrown
Be careful running this example, everything
might become slow
1 # ...
2 Iteration : 19 done. 20 GiB has been allocated !
3 Iteration : 20 done. 21 GiB has been allocated !
4 Iteration : 21 done. 22 GiB has been allocated !
5 Iteration : 22 done. 23 GiB has been allocated !
6 terminate called after throwing an instance of 'std ::
bad_alloc '
7 what (): std :: bad_alloc
8 [1] 30561 abort (core dumped ) ./ memory_leak_2
32
Dangling pointer
33
Dangling pointer
33
Dangling pointer
1 int* ptr_1 = some_heap_address ;
2 int* ptr_2 = some_heap_address ;
3 delete ptr_1;
4 ptr_1 = nullptr ;
5 // Cannot use ptr_2 anymore ! Behavior undefined !
ptr_2
33
Dangling pointer
34
Dangling pointer example
1 # include <iostream >
2 using std :: cout; using std :: endl;
3 int main () {
4 int size = 5;
5 int * ptr_1 = new int[size ];
6 int * ptr_2 = ptr_1; // Point to same data!
7 ptr_1 [0] = 100; // Set some data.
8 cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
9 cout << "ptr_2 [0]: " << ptr_2 [0] << endl;
10 delete [] ptr_1; // Free memory .
11 ptr_1 = nullptr ;
12 cout << "1: " << ptr_1 << " 2: " << ptr_2 << endl;
13 // Data under ptr_2 does not exist anymore !
14 cout << "ptr_2 [0]: " << ptr_2 [0] << endl;
15 return 0;
16 }
35
Even worse when used in
functions
1 # include <stdio.h>
2 // data processing
3 int* GenerateData (int size);
4 void UseDataForGood ( const int* const data , int size);
5 void UseDataForBad ( const int* const data , int size);
6 int main () {
7 int size = 10;
8 int* data = GenerateData (size);
9 UseDataForGood (data , size);
10 UseDataForBad (data , size);
11 // Is data pointer valid here? Should we free it?
12 // Should we use 'delete []' or 'delete '?
13 delete [] data; // ?????????????
14 return 0;
15 }
36
Memory leak or
dangling pointer
1 void UseDataForGood ( const int* const data , int size) {
2 // Process data , do not free. Leave it to caller .
3 }
4 void UseDataForBad ( const int* const data , int size) {
5 delete [] data; // Free memory !
6 data = nullptr ; // Another problem - this does
nothing !
7 }
37
RAII
Resource Allocation Is Initialization.
New object → allocate memory
Remove object → free memory
Objects own their data!
1 class MyClass {
2 public :
3 MyClass () { data_ = new SomeOtherClass ; }
4 ~ MyClass () {
5 delete data_;
6 data_ = nullptr ;
7 }
8 private :
9 SomeOtherClass * data_;
10 };
39
Shallow vs deep copy
40
Smart pointers
41
Raw pointers are hard to love
1. Its declaration doesn’t indicate whether it
points to a single object or to an array.
2. Its declaration reveals nothing about
whether you should destroy what it points
to when you’re done using it, i.e., if the
pointer owns the thing it points to.
3. If you determine that you should destroy
what the pointer points to, there’s no way
to tell how. Should you use delete, or is
there a different destruction mechanism
(e.g., a dedicated destruction function the
pointer should be passed to)?
42
Raw pointers are hard to love
4. If you manage to find out that delete is the
way to go, Reason 1 means it may not be
possible to know whether to use the
single-object form (“delete”) or the array
form (“delete []”). If you use the wrong
form, results are undefined.
5. There’s typically no way to tell if the pointer
dangles, i.e., points to memory that no
longer holds the object the pointer is
supposed to point to. Dangling pointers
arise when objects are destroyed while
pointers still point to them.
43
Smart pointers
44
C++11 smart pointers types
47
std::unique_ptr example
48
std::unique_ptr example
unique_ptr are unique: This means that
we can move stuff but not copy:
1 vehicle_2 = std :: move( vehicle_1 );
1 vehicle_1 = 0 x56330247ce70
2 vehicle_2 = 0 x56330247cec0
49
std::unique_ptr example
50
std::unique_ptr example
51
Unique pointer (std::unique_ptr)
Constructor of a unique pointer takes
ownership of a provided raw pointer
No runtime overhead over a raw pointer
Syntax for a unique pointer to type Type:
1 # include <memory >
2 // Using default constructor Type ();
3 auto p = std :: unique_ptr <Type >( new Type);
4 // Using constructor Type(<params >);
5 auto p = std :: unique_ptr <Type >( new Type(<params >));
0
http://en.cppreference.com/w/cpp/memory/unique_ptr
52
What makes it “unique”
0
http://en.cppreference.com/w/cpp/memory/shared_ptr 55
Shared pointer
1 class MyClass {
2 public :
3 MyClass () { cout << "I'm alive !\n"; }
4 ~ MyClass () { cout << "I'm dead ... :(\n"; }
5 };
6
7 int main () {
8 auto a_ptr = std :: make_shared <MyClass >();
9 cout << a_ptr. use_count () << endl;
10 {
11 auto b_ptr = a_ptr;
12 cout << a_ptr. use_count () << endl;
13 }
14 cout << "Back to main scope \n";
15 cout << a_ptr. use_count () << endl;
16 return 0;
17 }
56
When to use what?
57
Typical beginner error
1 int main () {
2 // Allocate a variable in the stack
3 int a = 42;
4
5 // Create a pointer to that part of the memory
6 int* ptr_to_a = &a;
7
8 // Know stuff about pointers eh?
9 auto a_unique_ptr = std :: unique_ptr <int >( ptr_to_a );
10
11 // Same happens with std :: shared_ptr .
12 auto a_shared_ptr = std :: shared_ptr <int >( ptr_to_a );
13
14 std :: cout << " Program terminated correctly !!!\n";
15 return 0;
16 }
58
Typical beginner error
1 int* ptr_to_a = &a;
2
3 // Know stuff about pointers eh?
4 auto a_unique_ptr = std :: unique_ptr <int >( ptr_to_a );
5
6 // Same happens with std :: shared_ptr .
7 auto a_shared_ptr = std :: shared_ptr <int >( ptr_to_a );
https://youtu.be/UOB7-B2MfwA
62
Suggested Video
Smart Pointers (in deep)
https://youtu.be/xGDLkt-jBJ4
63
References
Shared Pointers
https://en.cppreference.com/w/cpp/memory/shared_ptr
Unique Pointers
https://en.cppreference.com/w/cpp/memory/unique_ptr
64