Memory management ================= 1) What is the problem in the program below? (p3.c) #include #include int* alloc() { int a[2] = {0, 0}; return a; } int main() { int* a1; int* a2; a1 = alloc(); a2 = alloc(); a1[0] = 13; a2[0] = 17; printf("a1 = {%d, %d}\n", a1[0], a1[1]); printf("a2 = {%d, %d}\n", a2[0], a2[1]); } 2) This error is due to the use of a stack to store functions. What is stacked once we call a function? 3) What is the behavior of the Java program below? public class Dangling { private static int[] get() { int a[] = {1, 2, 3}; return a; } public static void main(String args[]) { int[] a1 = get(); a1[0] = 17; int[] a2 = get(); a2[0] = 42; System.out.println(a1[0] + ", " + a2[0]); } } 3.1) What is the difference between the Java and the C programs? 4) There are languages which have "managed" memory, and there are languages which don't. Can you list examples? 4.1) Are these languages important at all? 5) What are possible memory errors that we can get in unmanaged languages? 5.1) What is the problem here? (p1.c) #include #include int main() { int* i = (int*) malloc (sizeof(int)); *i = 3; printf("%d\n", *i); } 5.2) And what about here? (p2.c) #include #include void dangling() { int* i = (int*) malloc (sizeof(int)); int* j; *i = 3; free(i); j = (int*) malloc (sizeof(int)); *j = 8; printf("%d\n", *i); } int main() { dangling(); } 6) These errors are related to the use of a memory space called heap. What is the heap? 7) How would a heap management system be implemented? * A free block has the structure: size; Next Free Block; empty area (size - 2) * A full block has the structure: size; occupied area (size - 1) @ allocate(int size) { @ aux = FL; @ lag = NULL; while (aux != NULL && *aux < size + 1) { lag = aux; aux = *aux + 1; } if (aux == NULL) { throw new OutOfMemoryError(); } @ nextFree = aux + size + 1; *nextFree = *aux - size - 1; *(nextFree + 1) = *(aux + 1); if (lag == NULL) { FL = nextFree; } else { *(lag + 1) = nextFree; } *aux = size + 1; return aux + 1; // the size of the block cannot be overwritten } deallocate(@p) { @aux = p - 1; *p = FL; FL = aux; } 8) How would be the sequence below? p1 = m.allocate(4); p2 = m.allocate(2); m.deallocate(p1); p3 = m.allocate(1); 9) Which problems do we see in this kind of allocation? p1 = m.allocate(4); p2 = m.allocate(4); m.deallocate(p1); m.deallocate(p2); p3 = m.allocate(7); 10) What is the problem in this sequence? p1 = m.allocate(4); p2 = m.allocate(1); m.deallocate(p1); p3 = m.allocate(5); 11) What are the main concerns of a heap manager? - Placement: where to allocate a block - Splitting: when and how to split large blocks - Coalescing: when and how to recombine 12) Where to allocate a block? - first fit, best fit, next fit - Lists, Trees, Hashtables, etc 13) When and how to split a large block? - We are splitting to deliver the requested size. - Sometimes it is better to split well known sizes. - Ex.: region management in web servers. 14) When and how to recombine adjacent free blocks? - No coalescing - Eager coalescing - Delayed coalescing 15) How to manage the heap? - Casual approach - Manual reclamation - Library management - Garbage collection 16) When does the casual approach makes sense? - Small programs - Real time programs 17) When it does not make sense? 18) Any stories of dramatic failures? - The London Ambulance service, October of 1992 19) What about manual reclamation? Which languages offer it? 20) What are the advantages of manual reclamation? 21) What resources must be provided by a language that offers manual reclamation? - free/delete/reclaim/etc - destructors 22) What are the possible errors that one can cause when using manual reclamation? - Memory leak - Dangling pointer 23) How to debug memory problems? 23.1) Has anyone heard of Valgrind? - Example: $> gcc -g p1.c -o p1 ; valgrind ./p1 $> gcc -g p2.c -o p2 ; valgrind ./p2 $> gcc -g p3.c -o p3 ; valgrind ./p3 24) Even though languages such as C or C++ do not (natively) provide memory management at the run-time level, still large pieces of software are built in such languages. How to manage memory when the program size increases? - Management at the component level. 25) What would be a good implementation of a linked list in C? 26) Has anyone heard of smart-pointers? 27) Smart pointers are like proxies, i.e, a design pattern. We have seen other example of a proxy used at the language level in the course. Which example? 27.1) Anyway, what is a proxy? And what would be a smart-pointer proxy? 28) Consider the smart pointer below. What does it do? #include template class auto_ptr { T* ptr; public: explicit auto_ptr(T* p = 0) : ptr(p) { } ~auto_ptr() { delete ptr; } T& operator*() { return *ptr; } T* operator->() { return ptr; } }; 28.1) Can you give an example of use? class MyClass { public: MyClass(int id) : _id(id) { std::cout << "Creating MyClass " << _id << std::endl; } ~MyClass() { std::cout << "Object is being destroyed\n"; } void print() const { std::cout << "MyClass(" << _id << ")\n"; } int _id; }; void useSP(int i) { auto_ptr p(new MyClass(i)); p->print(); } void dontUseSP(int i) { MyClass* p(new MyClass(i)); p->print(); } int main(int argc, char** argv) { if (argc % 2) { useSP(argc); } else { dontUseSP(argc); } } $> g++ -g autoEx.cpp ; valgrind ./a.out ; valgrind ./a.out 123 28.2) What happens when our pointer goes out of scope? 28.3) And what if an exception happens, i.e, during the print method below: void dontUseSP(int i) { MyClass* p(new MyClass(i)); p->print(); delete p; } Will the object ever be deleted? 29) Besides the scope_pointer seen above, which other uses would we have for smart-pointers, i.e, what would be the meaning of 'p = q'? - Create a new copy. - Transfer the ownership from p to q. - Do reference counting. - Throw an exception. - etc. 30) Many programming languages use garbage collectors to manage memory. What is this? 31) How to see the Java's garbage collector in action? public class ObjCreator { public static void main(String args[]) { if (args.length != 1) { System.err.println("Syntax: java ObjCreator num_objs"); System.exit(1); } Integer objInt = null; int numObj = Integer.parseInt(args[0]); for (int i = 0; i < numObj; i++) { if (i % 2 == 0) { objInt = new Integer(i+1); } else { objInt = new Integer(i-1); } } System.out.println(objInt); } } $> java -verbose:gc ObjCreator 100000 $> java -verbose:gc ObjCreator 1000000 $> java -verbose:gc ObjCreator 10000000 32) Which objects can be garbage collected? 33) How does it happen that an object becomes unreachable? If r is a reference pointing to object R, then R can become detached in the following ways: - r is assigned null - r is assigned another object - r is a formal parameter, R is an actual parameter, and the function exits. - r is a local variable, and its local scope exits. 34) The fact that an object R is detached from a reference r indicates that R can be reclaimed? 35) Which objects cannot be reclaimed? 1) Object directly attached to entities in the software text. 2) Object that are attached to objects in (1) 36) What types of garbage collectors exist? * Mark and sweep: find the live heap links and mark those blocks that are reachable. Then make a pass over the heap and return unmarked free blocks to the free pool. * Copying collection: memory is divided in two; only one half is used at a time. When used half is full, copy used blocks to the other location, and erase the old one. 37) Advantages? 37.1) What is the main complication in this scheme? Pointers must be changed. 38) Why is it so difficult to implement in C? - What if I change a variable that is not a current heap link? #include #include int main(int argc, char** argv) { int* p1 = (int*) malloc (sizeof(int)); int* p2 = atoi(argv[1]); int* p3; int i = p2; p1[0] = 3; printf("Address = %d\n", p1); printf("Address = %d\n", *p2); printf("Address = %d\n", i); p3 = i; *p3 = 4; printf("Address = %d\n", *p2); } * Reference counting: each block has a counter of heap links to it. This counter is incremented when a heap link is copied, decremented when the link is discarded. When the counter goes to zero, the block is freed. 39) Advantages? - naturally incremental, with no big pause. 39.1) What is the main problem with this scheme? 40) Which improvements can we think for garbage collection? - Generational collectors: divide block into generations, according to age. - Incremental: does GC a little at a time. 41) Does the program TestSum.java have any problem? 42) What would happen in these calls? $> java -Xmx32m TestSum 1000000 $> java -Xmx128m TestSum 1000000