the gamedesigninitiative at cornell university

Lecture 10 : The Details Sizing Up Memory

Primitive Data Types Complex Data Types

— byte: basic value (8 bits) — Pointer: platform dependent — 4 bytes on 32 bit machine — char: 1 byte — 8 bytes on 64 bit machine — short: 2 bytes — Java reference is a pointer — int: 4 bytes — Array: data size * length Not standard — long: 8 bytes May change — Strings same (w/ trailing null) — Struct: sum of fields — float: 4 bytes IEEE standard — Same rule for classes Won’t change — double: 8 bytes — Structs = classes w/o methods

the gamedesigninitiative 2 Memory Details at cornell university Memory Example

class Date { short year; 2 byte byte day; 1 byte byte month; 1 bytes } 4 bytes class Student { int id; 4 bytes Date birthdate; 4 bytes Student* roommate; 4 or 8 bytes (32 or 64 bit) } 12 or 16 bytes

the gamedesigninitiative 3 Memory Details at cornell university Memory and Pointer Casting

— C++ allows ANY cast // Floats for OpenGL — Is not “strongly typed” float[] lineseg = {0.0f, 0.0f, 2.0f, 1.0f}; — Assumes you know best — But must be explicit cast // Points for calculation Vec2* points — Safe = aligns properly // Convert to the other type — Type should be same size points = (Vec2*)lineseg; — Or if array, multiple of size for(int ii = 0; ii < 2; ii++) { — Unsafe = data corruption CCLOG("Point %4.2, %4.2", — It is all your fault points[ii].x, points[ii].y); — Large cause of seg faults }

the gamedesigninitiative 4 Memory Details at cornell university Data Structures and Memory

— Collection types are costly — Even null pointers use memory — Common for pointers to use as much memory as the pointees — Unbalanced trees are very bad — Even true of (pointer) arrays — Array uses additional memory — Not so in array of structs — Objects stored directly in array — But memory alignment!

the gamedesigninitiative 5 Memory Details at cornell university Data Structures and Memory

— Collection types are costly — Even null pointers use memory — Common for pointers to use as much memory as the pointees — Unbalanced trees are very bad — Even true of (pointer) arrays — Array uses additional memory — Not so in array of structs — Objects stored directly in array — But memory alignment!

the gamedesigninitiative 6 Memory Details at cornell university Two Main Concerns with Memory

— Allocating Memory — With OS support: standard allocation —Reserved memory: memory pools

— Getting rid of memory you no longer want — Doing it yourself: deallocation — Runtime support: garbage collection

the gamedesigninitiative 7 Memory Details at cornell university C/C++: Allocation Process

malloc new

— Based on memory size — Based on data type — Give it number of bytes — Give it a data type — Typecast result to assign it — If a class, calls constructor — No initialization at all — Else no default initialization — Example: — Example: char* p = (char*)malloc(4) Point* p = new Point(); Stack Heap Stack Heap s izeof ? n bytes 1

? 0 (Class) … … ? 1

the gamedesigninitiative 8 Memory Details at cornell university C/C++: Allocation Process

malloc new

— Based on memory size — Based on data type — Give it number of bytes — Give it a data type — Typecast result to assign it — If a class, calls constructor — No initialization at all — Else no default initialization — Example: — Example: char* p = (char*)malloc(4) Point* p = new Point(); Stack Heap Stack Heap s izeof ? n bytes 1

? 0 (Class) … … ? 1

the gamedesigninitiative 9 Memory Details at cornell university C/C++: Allocation Process

malloc new

— Based on memory size — Can emulate malloc — Give it number of bytes — Create a char (byte) array — Typecast result to assign it — Arrays not initialized — No initialization at all — Typecast after creation — Example: — Example: char* p = (char*)malloc(4) Point* p = (Point*)(new char[8]) Stack Heap Stack Heap n bytes ? ? n bytes ? ? … … ? ?

the gamedesigninitiative 10 Memory Details at cornell university Custom Allocators

Pre-allocated Array (called Object Pool)

Start Free End

— Idea: Instead of new, get object from array — Just reassign all of the fields — Use Factory pattern for constructor Easy if only — See alloc() method in CUGL objects one object — Problem: Running out of objects type to — We want to reuse the older objects allocate — Easy if deletion is FIFO, but often isn’t

the gamedesigninitiative 11 Memory Details at cornell university Allocation Patterns in CUGL class PolygonNode : public Node { public: /** Creates, but does not initialize node */ Allocation Sprite(); only

/** Initializes a node with an image filename. */ virtual bool initWithFile(const string& filename); Initialization /** Initializes a node with a texture. */ only virtual bool initWithTexture(const shared_ptr& texture);

/** Creates a node with an image filename. */ static shared_ptr allocWithFile(const string& filename); Allocation & /** Creates a node with a Texture object. */ initialization static shared_ptr allocWithTexture(const shared_ptr& texture);

};

the gamedesigninitiative 12 Memory Details at cornell university Separating Allocation and Initialization

Pre-allocated Array These are not pointers!

Start Free End

— Array contains objects, not pointers — Allocating the array will allocate objects — No initialization until user grabs an object — Makes it easy to implement factory pattern — Hides choice of underlying allocation — Uses standard initialization independent of allocation

the gamedesigninitiative 13 Memory Details at cornell university Allocation Patterns in CUGL class PolygonNode : public Node { public: Allocation /** Creates, butStandard does not initialize allocation node */ Sprite(); only

/** Initializes a node with an image filename. */ virtual bool initWithFile(const string& filename); Initialization /** Initializes a node with a texture. */ only virtual bool initWithTexture(const shared_ptr& texture);

/** Creates a node with an image filename. */ static shared_ptr allocWithFile(const string& filename); Allocation & Customizable allocation /** Creates a node with a Texture object. */ initialization static shared_ptr allocWithTexture(const shared_ptr& texture);

};

the gamedesigninitiative 14 Memory Details at cornell university Free Lists

— Create an object queue // Free the new particle — Separate from preallocation freelist.push_back(p); — Stores objects when “freed” … — To allocate an object… // Allocate a new particle — Look at front of free list Particle* q; — If object there take it if (!freelist.isEmpty()) { — Otherwise make new object q = freelist.pop(); — Preallocation unnecessary } else { — Queue wins in long term q = new Particle(); — Main performance hit is } deletion/fragmentation q.(…)

the gamedesigninitiative 15 Memory Management at cornell university Particle Pool Example

the gamedesigninitiative 16 Memory Details at cornell university Particle Pool Example

class ParticlePool { public: /** Creates a ParticlePool with the given capacity. */ ParticlePool(int capacity); /** Returns a new OR reused object from this pool. */ Particle* obtain(); /** Marks object as eligible for reuse. */ void free (Particle* object) ; private: /** Allocates a new object from the pool. */ Particle* alloc(); }; the gamedesigninitiative 17 Memory Details at cornell university Particle Pool Example

class ParticlePool { public: /** Creates a ParticlePool with the given capacity. */ ParticlePool(int capacity);Use instead /** Returns a new OR reusedof new object from this pool. */ Particle* obtain(); /** Marks object as eligible for reuse.Use */ instead void free (Particle* object) ; of delete private: /** Allocates a new object from the pool. */ Particle* alloc(); }; the gamedesigninitiative 18 Memory Details at cornell university Particle Pool Example

class ParticlePool { public: /** Creates a ParticlePool with the given capacity. */ ParticlePool(int capacity);Use instead /** Returns a new OR reusedof new object from this pool. */ Particle* obtain(); /** Marks object as eligible for reuse.Use */ instead void free (Particle* object) ; of delete private: /** Allocates a new objectWhat from tothe do pool. */ Particle* alloc(); if nothing free }; the gamedesigninitiative 19 Memory Details at cornell university Two Main Concerns with Memory

— Allocating Memory — With OS support: standard allocation —Reserved memory: memory pools

— Getting rid of memory you no longer want — Doing it yourself: deallocation — Runtime support: garbage collection

the gamedesigninitiative 20 Memory Details at cornell university Manual Deletion in C/C++

— Depends on allocation int main() { — malloc: free cout << "Program started" << endl; int* a = new int[LENGTH]; — new: delete

— What does deletion do? delete a; — Marks memory as available for(int ii = 0; ii < LENGTH; ii++) { — Does not erase contents cout << "a[" << ii << "]=" — Does not reset pointer << a[ii] << endl; — Only crashes if pointer bad } — Pointer is currently NULL cout << "Program done" << endl; — Pointer is illegal address }

the gamedesigninitiative 21 Memory Details at cornell university Memory Leaks

— Leak: Cannot release memory — Object allocated on heap — Only reference is moved — Consumes memory fast!

— Can even happen in Java memoryArea = newArea; — JNI supports native libraries — Method may allocate memory — Need another method to free — Example: dispose() in JOGL

the gamedesigninitiative 22 Memory Details at cornell university A Question of Ownership void foo() { void foo(int key) { MyObject* o = MyObject* o = new MyObject(); table.get(key); o.doSomething(); o.doSomething(); o = null; o = null; Memory Not a return; Leak return; Leak } }

the gamedesigninitiative 23 Memory Details at cornell university A Question of Ownership void foo() { void foo(int key) { MyObject* o = MyObject* o = table.get(key); table.get(key); table.remove(key); table.remove(key); ntable.put(key,o); o = null; Memory o = null; Leak? return; Not a return; Leak } }

the gamedesigninitiative 24 Memory Details at cornell university A Question of Ownership

Thread 1 Thread 2

“Owners” of obj

void run() { void run() { o.doSomething1(); o.doSomething2(); } }

Who deletes obj?

the gamedesigninitiative 25 Memory Details at cornell university Understanding Ownership

Function-Based Object-Based

— Object owned by a function — Owned by another object — Function allocated object — Referenced by a field — Can delete when function done — Stored in a data structure — Ownership never transferred — Allows multiple ownership — No guaranteed relationship — May pass to other functions between owning objects — But always returns to owner — Call each owner a reference — Really a stack-based object — When can we deallocate? — Active as long as allocator is — No more references — But allocated on heap (why?) — References “unimportant”

the gamedesigninitiative 26 Memory Details at cornell university Understanding Ownership

Function-Based Object-Based

— Object owned by a function — Owned by another object — Function allocated object — Referenced by a field — Can delete when function done — Stored in a data structure — Ownership never transferred — Allows multiple ownership — No guaranteed relationship — May pass to other functions between owning objects — But always returns to owner — Call each owner a reference — Really a stack-based object — When can we deallocate? — Active as long as allocator is — No more references — But allocated on heap (why?) — References “unimportant”

the gamedesigninitiative 27 Memory Details at cornell university Reference Strength

Strong Reference

— Reference asserts ownership — Reference != ownership — Cannot delete referred object — Object can be deleted anytime — Assign to NULL to release — Often for performance caching — Else assign to another object — Only use indirect references — Can use reference directly — Copy to local variable first — Compute on local variable — No need to copy reference — Treat like a normal object — Be prepared for NULL — Reconstruct the object? — Standard type of reference — Abort the computation?

the gamedesigninitiative 28 Memory Details at cornell university

— Every object has a counter — Tracks number of “owners” — No owners = Ref 1 Ref 2 — Increment when assign reference — Historically an explicit method call

— Method often called retain() Object Count = 12 — Decrement when remove reference — Method call is release() — If makes count 0, delete it

the gamedesigninitiative 29 Memory Details at cornell university References vs. Garbage Collectors

Reference Counting Mark-and-Sweep

— Advantages — Advantages — Deallocation is immediate — No assignment overhead — Works on non-memory objects — Can handle reference cycles — Ideal for real-time systems — No specialized training to use — Disadvantages — Disadvantages — Overhead on every assignment — Collection can be expensive — Cannot easily handle cycles — Hurts performance when runs (e.g. object points to itself) — Usually triggered whenever — May require training to use the memory is close to full

the gamedesigninitiative 30 Memory Details at cornell university Smart Pointers

— std::shared_ptr — Templatized type in C++11 T Object — Provides reference counting std::shared_ptr — Need to include Pointer to T — Counting is automatic Ptr to Control Block — Uses overloaded operators Control Block — Increments at creation/copy Strong Count — Decrements when destroyed Weak Count — is on stack! Other Data — Passing as a parameter copies — So often pass by reference

the gamedesigninitiative 31 Memory Details at cornell university Smart Pointers

— std::shared_ptr #include #include "A.h" — Templatized type in C++11 using namespace std; — Provides reference counting /** Okay, but inefficient */ — Need to include void foo(shared_ptr var) — Counting is automatic { … — Uses overloaded operators } — Increments at creation/copy /** Preferred approach */ — Decrements when destroyed void foo(const shared_ptr& var) { — Smart pointer is on stack! … — Passing as a parameter copies } — So often pass by reference

the gamedesigninitiative 32 Memory Details at cornell university Smart Pointers

— std::shared_ptr #include #include "A.h" — Templatized type in C++11 using namespace std; — Provides reference counting /** Okay, but inefficient */ — Need to include void foo(shared_ptr var) — Counting is automatic { foo claims … ownership — Uses overloaded operators } — Increments at creation/copy /** Preferred approach */ — Decrements when destroyed void foo(const shared_ptr& var) { — Smart pointer is on stack! foo wants … usage only — Passing as a parameter copies } — So often pass by reference

the gamedesigninitiative 33 Memory Details at cornell university Smart Pointers

— std::shared_ptr #include #include "A.h" — Templatized type in C++11 using namespace std; — Provides reference counting /** Okay, but inefficient */ — Need to include void foo(shared_ptr var) — Counting is automatic { foo claims … ownership — Uses overloaded operators } — Increments at creation/copy /** Preferred approach */ — Decrements when destroyed void foo(const shared_ptr& var) { — Smart pointer is on stack! foo can change A, foo wants … usage only — Passing as a parameter copiesnot smart} pointer — So often pass by reference

the gamedesigninitiative 34 Memory Details at cornell university Smart Pointers

— std::shared_ptr #include #include "A.h" — Templatized type in C++11 using namespace std; — Provides reference counting /** Good */ — Need to include shared_ptr foo(void) { shared_ptr result — Counting is automatic ... return result; — Uses overloaded operators } — Increments at creation/copy /** BAD!!! */ — Decrements when destroyed shared_ptr& foo(void) { shared_ptr result — Smart pointer is on stack! ... — Passing as a parameter copies return result; } — So often pass by reference

the gamedesigninitiative 35 Memory Details at cornell university Smart Pointers

— std::shared_ptr #include #include "A.h" — Templatized type in C++11 using namespace std; — Provides reference counting /** Good */ — Need to include shared_ptr foo(void) { shared_ptr result — Counting is automatic ... Copied return result; to caller — Uses overloaded operators } — Increments at creation/copy /** BAD!!! */ — Decrements when destroyed shared_ptr& foo(void) { shared_ptr result — Smart pointer is on stack! ... Deleted — Passing as a parameter copies return result; with stack } — So often pass by reference

the gamedesigninitiative 36 Memory Details at cornell university Recall: Smart Pointers and Allocation

Heap Allocation Smart Pointer

void func() { void func() { Point* p = new Point(1,2,3); shared_ptr p; … p = make_shared(1,2,3); delete p; … Same arguments } } as constructor — Must remember to delete — Deletion is not necessary — Otherwise will memory leak — Sort-of garbage collection

the gamedesigninitiative 37 C++ Overview at cornell university Allocation Patterns in CUGL class PolygonNode : public Node { public: /** Creates, but does not initialize node */ Sprite();

/** Initializes a node with an image filename. */ virtual bool initWithFile(const string& filename);

/** Initializes a node with a texture. */ virtual bool initWithTexture(const shared_ptr& texture);

/** Creates a node with an image filename. */ static shared_ptr allocWithFile(const string& filename); Smart pointer /** Creates a node with a Texture object. */ & initialization static shared_ptr allocWithTexture(const shared_ptr& texture);

};

the gamedesigninitiative 38 Memory Details at cornell university Recall: Reference Strength

Strong Reference Weak Reference

— shared_ptr — weak_ptr — Always safe to use — Not always safe to use — Held until all deleted — Returns null if deleted

std::shared_ptr T Object std::shared_ptr Pointer to T Pointer to T Ptr to Control Block Ptr to Control Block T Object

Control Block BAD! Strong Count Weak Count

the gamedesigninitiative 39 MemoryOther Data Details at cornell university Recall: Reference Strength

Strong Reference Weak Reference

— shared_ptr — weak_ptr — Always safe to use — Not always safe to use — Held until all deleted — Returns null if deleted

std::shared_ptr T Object std::weak_ptr Pointer to T Pointer to T Ptr to Control Block Ptr to Control Block T Object

Control Block Good Strong Count Weak Count

the gamedesigninitiative 40 MemoryOther Data Details at cornell university Weak Pointers vs. Raw Pointers

— Weak pointers are still managed! — weak_ptr and A* not the same thing — Weak pointer has reference to the control block — Will null the base pointer when no longer valid — But sometimes you want a raw pointer A* — May be required by 3rd party APIs — Can get this from strong, weak pointers: var.get() — Caching this value past a function call is unsafe

the gamedesigninitiative 41 Memory Details at cornell university Recall: Typecasting and Smart Pointers

Normal Pointers Smart Pointers

B* b; // The super class shared_ptr b; // Contains B* A* a; // The subclass shared_ptr a; // Contains A* Acceptable: Bad: b = new B(); b = make_shared(); a = (A*)b; a = (shared_ptr)b; Better: Good: b = new B(); b = make_shared(); a = dynamic_cast(b); a = dynamic_pointer_cast(b);

the gamedesigninitiative 42 C++ Overview at cornell university Recall: Typecasting and Smart Pointers

Normal Pointers Smart Pointers

B* b; // The super class shared_ptr b; // Contains B* A* a; // The subclass shared_ptr a; // Contains A* Acceptable: Bad: b = new B(); b = make_shared(); a = (A*)b; a = (shared_ptr)b; Better: Good: Must acquire control block! b = new B(); b = make_shared(); a = dynamic_cast(b); a = dynamic_pointer_cast(b);

the gamedesigninitiative 43 C++ Overview at cornell university Platform Specific Issues

— Android: JNI interface issues — May need to call Java method from C++ — Doing so requires pointers/references to Java — This requires a special allocator/deallocator — Apple: Reference counting issues — Objective C has its own reference counting — Works with normal raw pointers so easier to use — But this requires specialized compiler support — Obj-C++ does not enable this support!

the gamedesigninitiative 44 Memory Details at cornell university Android: Calling Java from inside C++

// Retrieve the JNI environment. See SDL API JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); for more info // Retrieve the Java instance of the SDLActivity and get its class jobject activity = (jobject)SDL_AndroidGetActivity(); jclass clazz(env->GetObjectClass(activity));

// Find the identifier of the method to call jmethodID method_id; method_id = env->GetMethodID(clazz, "myMethod", "()V");

// Effectively call the Java method env->CallVoidMethod(activity, method_id);

// Clean up the local references. Memory leak env->DeleteLocalRef(activity); if forget this env->DeleteLocalRef(clazz);

the gamedesigninitiative 45 Memory Details at cornell university Apple: Reference Issues in Obj-C++

// Note Objective separates allocation, initialization // Reference var has reference count of 1 A* var = [[A alloc] init]; // Increments reference count to 2 [var retain]; // Decrements reference count to 1 [var release]; // Decrements reference count to 0 AND deletes [var release];

the gamedesigninitiative 46 Memory Details at cornell university Apple: Reference Issues in Obj-C++

// Note Objective separates allocation, initialization // Reference var has reference count of 1 A* var = [[A alloc] init]; // Increments reference count to 2 [var retain]; // Decrements reference count to 1 [var release];

// Decrements referenceDo this count instead to 0 AND deletes [var release]; of free or delete

the gamedesigninitiative 47 Memory Details at cornell university Summary

— Must control allocation of heap objects — Preallocate objects when it makes sense — Use free-lists to recycle objects when possible — Use the CUGL factory pattern to support these — Must track ownership of allocated objects — Know who is responsible for deleting — True even when using smart pointers — Pay attention to platform specific issues

the Memory Details gamedesigninitiative 48 at cornell university