Sie sind auf Seite 1von 19

CS 342: C++ Memory Management

Overview of C++ Memory Management


4 major memory segments
Global: variables outside stack, heap Code (a.k.a. text): the compiled program Heap: dynamically allocated variables Stack: parameters, automatic and temporary variables

global code heap

Key differences from Java


Destructors of automatic variables called when stack frame where declared pops No garbage collection: program must explicitly free dynamic memory

Heap and stack use vary dynamically Code and global use is fixed Code segment is read-only

stack

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Illustrating C++ Memory


int default_value = 1; int main (int argc, char **argv) { Calculator *d = new Calculator; d -> set_value (default_value); return 0; }
int default_value

global code heap

int value_ Calculator

void Calculator::set_value (int i) { Calculator * this value_ = i; Calculator::set_value int i }

stack

main crt0

Calculator * d

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Illustrating C++ Memory, cont.


int default_value = 1; int main (int argc, char **argv) { Calculator *d = new Calculator; d -> set_value (default_value); return 0; }
int default_value

global code heap

int value_ Calculator

void Calculator::set_value (int i) { Calculator * this value_ = i; Calculator::set_value int i }

stack

main crt0

Calculator * d

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

A Basic Issue: Aliasing


int main (int argc, char **argv) { Calculator c; Calculator * d = & c; Calculator & e = c; delete d; return 0; }

Multiple aliases for same object


c is a simple alias, the object itself d is a variable holding a pointer e is a variable holding a reference

What happens when we call delete on d?


Destroy a stack variable (may get a bus error there if were lucky) If not, we may crash in destructor of c at function exit Or worse, a local stack corruption that may lead to problems later

Calculator &e Calculator *d

Problem: object destroyed but Calculator c another alias to it was then used

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Memory Initialization Errors


int main (int argc, char **argv) { Calculator *d; cout << d->get_value (); for (int i; i < argc; ++i) cout << argv [i]; return 0; }

Heap and stack memory is not initialized for us in C++


Get whatever was out in memory Unless we initialize it ourselves So we should always do so

What happens when we dereference pointer d?


If were lucky, a program crash If not, we get bad output Or worse, a local over-write leading to problems later

Is there anything else wrong with this code?


Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management


Calculator * bad () { Calculator c; return & c; } Calculator & also_bad () { Calculator c; return c; } Calculator mediocre () { Calculator c; return c; } Calculator * good () { return new Calculator; }

Memory Lifetime Errors


Automatic variables
Are destroyed on function return But in bad, we return a pointer to a variable that no longer exists Reference from also_bad similar Like an un-initialized pointer

What if we returned a copy?


Ok, we avoid the bad pointer, and end up with an actual object But we do twice the work (why?) And, its a temporary variable (more on this next)

We really want dynamic allocation here

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Memory Lifetime Errors, cont.


int main (int argc, char **argv) { Calculator *c = & mediocre (); cout << good () -> get_value (); cout << c -> get_value (); } Calculator mediocre () { Calculator c; return c; } Calculator * good () { return new Calculator; }

Dynamically allocated variables


Are not garbage collected But are lost if no one refers to them: called a memory leak

Temporary variables
Are destroyed at end of statement Similar to problems w/ automatics

Can you spot 2 problems?


One with a temporary variable One with dynamic allocation

Important intuition
Incorrect use is the real problem here Need to understand the details

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Memory Lifetime Errors, cont.


int main (int argc, char **argv) { Calculator *c = good (); cout << mediocre ().get_value (); cout << c -> get_value (); delete c; } Calculator mediocre () { Calculator c; return c; } Calculator * good () { return new Calculator; }

Dynamically allocated variables


Should be referenced somehow And the reference used to destroy them when finished with them

Temporary variables
Can be used throughout a statement But should not be referenced outside the statement where created Use the temporary variable to do output, etc. within a statement Use dynamic variables across scopes
Especially for use outside the creating stack frame

Example resolved

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

A More General View: Scopes


Temporary variables
Are scoped to an expression, e.g., a = b + 3 * c;

Automatic variables
Are scoped to the duration of the function in which they are declared

Dynamically allocated variables


Are scoped from explicit creation (new) to explicit destruction (delete)

Global variables
Are scoped to the entire lifetime of the program Includes static class and namespace members May still have initialization ordering issues (more later: Singleton Pattern)

Member variables
Are scoped to the lifetime of the object within which they reside Depend on whether object itself is global, dynamic, automatic, temporary
Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Scopes of Variables vs. References


Variables and the references to them may have different scopes
May result in obvious kinds of lifetime errors we saw earlier But may also introduce more subtle issues of aliasing Still can risk destroying an object too soon or too late

May want to tie different kinds of scopes together


For example, freeing dynamic memory when an exception is thrown Or when an object owning another object is destroyed

Solution in C++ is a set of idioms


Constructor allocates, destructor de-allocates Guard: ties dynamic resource scopes to automatic scopes Shallow copy, deep copy semantics Reference counting: ties dynamic lifetime to a group of references Copy-on-write: allows more efficient management of multiple aliasing

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Ctor Allocates, Dtor Deallocates


#if ! defined (CALCULATOR_H) #define CALCULATOR_H #define DEFAULT_SIZE 20 class Calculator { public: Calculator (size_t s = DEFAULT_SIZE); ~Calculator (); private: size_t stack_size_; int * values_; }; #endif /* CALCULATOR_H */

Resources allocated by an object need to be freed Constructor, destructor offer good start/end points Fixed stack calculator
Can push and pop ints Can add, etc. to top of stack Stack size may differ for each object Once set, stack size is fixed Notice the default parameter

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Ctor Allocates, Dtor Deallocates, cont.


#include "Calculator.h" Calculator:: Calculator (size_t s) : stack_size_ (s), values_ (0) { if (stack_size_ > 0) { values_ = new int [stack_size_]; if (values_ == 0) stack_size_ = 0; } } // ...

Calculator initialization sets size, but zeroes out the pointer why? Test Only allocate if length is greater than zero Could also use conditional operator at initialization:
values_ (s > 0 ? new int [s] : 0)

Which is way is better? What does new do?


Allocates memory Calls constructor(s)

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Ctor Allocates, Dtor Deallocates, cont.


// ... Calculator::~Calculator () { delete [] values_; } int main (int argc, char** argv) { Calculator c; // When c is created, it // allocates memory return 0; // Now when c is destroyed, so // is the memory it allocated }

Destructor releases the dynamic array So, scopes are tied


Calculator and its stack

What does delete do?


Calls destructor(s) Frees memory

What are new [] and delete []


vs. plain old new and delete

What if values_ was 0 when delete was called?

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Applying Guard: Function Scope


Calculator * create_and_init () { Calculator *c = new Calculator; auto_ptr<Calculator> p (c); init (c); // may throw exception p.release (); return c; } int run () { try { Calculator *d = create_and_init (); } catch (...) {} // ... }

Guard idiom revisited C++ has an auto_ptr class template auto_ptr<X> assumes ownership of a pointer-to-X auto_ptr destructor calls delete on the owned pointer Call release to break ownership by auto_ptr when its safe to do so

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Copy Constructor: Shallow


// ... Calculator::Calculator (const Calculator & c) : stack_size_ (c.stack_size_), values_ (c. values_) { }

Two ways to copy


Shallow: aliases existing resources (i.e., initially) Deep: makes a complete and separate copy

Version to left shows shallow copy


Efficient But may be risky: why?

Whats the invariant?


values_, stack_size_
Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Copy Constructor: Deep


Calculator::Calculator (const Calculator & c) : stack_size_ (c.stack_size_), values_ (0) { if (stack_size_ > 0) { values_ = new int [stack_size_]; if (values_ == 0) stack_size_ = 0; for (size_t s = 0; s < stack_size_; ++s) values_[s] = c.values_[s]; } }

Version to left shows deep copy


Safe: no aliasing But may not be very efficient I.e., what if its reasonable to share the resource n times?

Note trade-offs with arrays


Allocate memory once More efficient than multiple calls to new (heap search) Constructor and assignment called on each array element Less efficient than block copy But sometimes more correct

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Intro to Reference Counting


Basic Problem
Resource sharing is efficient But hard to tell when done Must avoid early deletion Must avoid leaks

reference

Solution reference resource = hi counter = = 3 reference


Share resource and a counter Each new reference increments the counter When a reference is done, it decrements the counter When count drops to zero, delete resource and counter

Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Intro to Copy on Write


Basic Problem resource = lo reference counter = = 1
Reference counting enables safe and efficient sharing But what about modifications? May want logically separate copies of resource

Solution reference resource = hi counter = = 2 reference


Start with reference counting Writer checks for count > 1
Copies reference & counter Updates both counters Performs the write

Readers all share a copy, each writer can get its own
Copyright 2002 Dept. of Computer Science and Engineering, Washington University

CS 342: C++ Memory Management

Summary: Memory Management Tips


Know what goes where in memory Understand mechanics of stack, dynamic allocation Watch for simple lifetime errors Consider more subtle scope/lifetime issues Pay attention to aliasing (draw a picture) Think about shallow versus deep copying trade-offs Master useful idioms for C++ memory management
Learn how they work Understand when to apply them Look for ways to apply them in the labs and beyond
Copyright 2002 Dept. of Computer Science and Engineering, Washington University

Das könnte Ihnen auch gefallen