Garbage Collection. CS 351: Systems Programming Michael Saelee

Similar documents
Design Issues. Subroutines and Control Abstraction. Subroutines and Control Abstraction. CSC 4101: Programming Languages 1. Textbook, Chapter 8

Automatic Memory Management

Garbage Collection. Hwansoo Han

Runtime. The optimized program is ready to run What sorts of facilities are available at runtime

CS 241 Honors Memory

Run-Time Environments/Garbage Collection

CS577 Modern Language Processors. Spring 2018 Lecture Garbage Collection

One-Slide Summary. Lecture Outine. Automatic Memory Management #1. Why Automatic Memory Management? Garbage Collection.

Garbage Collection. Akim D le, Etienne Renault, Roland Levillain. May 15, CCMP2 Garbage Collection May 15, / 35

Programming Language Implementation

Garbage Collection. Steven R. Bagley

Deallocation Mechanisms. User-controlled Deallocation. Automatic Garbage Collection

Run-time Environments -Part 3

Hard Real-Time Garbage Collection in Java Virtual Machines

Garbage Collection Algorithms. Ganesh Bikshandi

Memory Management: The Details

CS61C : Machine Structures

ACM Trivia Bowl. Thursday April 3 rd (two days from now) 7pm OLS 001 Snacks and drinks provided All are welcome! I will be there.

Heap Management. Heap Allocation

Acknowledgements These slides are based on Kathryn McKinley s slides on garbage collection as well as E Christopher Lewis s slides

CS 345. Garbage Collection. Vitaly Shmatikov. slide 1

Sustainable Memory Use Allocation & (Implicit) Deallocation (mostly in Java)

Lecture 15 Garbage Collection

Lecture 13: Garbage Collection

Compiler Construction

Reference Counting. Reference counting: a way to know whether a record has other users

Manual Allocation. CS 1622: Garbage Collection. Example 1. Memory Leaks. Example 3. Example 2 11/26/2012. Jonathan Misurda

Reference Counting. Reference counting: a way to know whether a record has other users

CS Computer Systems. Lecture 8: Free Memory Management

Run-time Environments - 3

Compiler Construction

Automatic Garbage Collection

Advances in Programming Languages: Regions

CS61C : Machine Structures

Lecture Notes on Garbage Collection

CS61C Midterm Review on C & Memory Management

CMSC 330: Organization of Programming Languages. Memory Management and Garbage Collection

CS193E Lecture #3 Categories and Protocols Cocoa Memory Management

CS61, Fall 2012 Section 2 Notes

Lecture 7 More Memory Management Slab Allocator. Slab Allocator

CPSC 213. Introduction to Computer Systems. Instance Variables and Dynamic Allocation. Unit 1c

CPSC 213. Introduction to Computer Systems. Winter Session 2017, Term 2. Unit 1c Jan 24, 26, 29, 31, and Feb 2

CA341 - Comparative Programming Languages

Robust Memory Management Schemes

Lecture 15 Advanced Garbage Collection

CMSC 330: Organization of Programming Languages

Memory Management. Memory Management... Memory Management... Interface to Dynamic allocation

Memory Allocation. Static Allocation. Dynamic Allocation. Dynamic Storage Allocation. CS 414: Operating Systems Spring 2008

CMSC 330: Organization of Programming Languages

CS842: Automatic Memory Management and Garbage Collection. Mark and sweep

CS61C : Machine Structures

A.Arpaci-Dusseau. Mapping from logical address space to physical address space. CS 537:Operating Systems lecture12.fm.2

Performance of Non-Moving Garbage Collectors. Hans-J. Boehm HP Labs

Agenda. CSE P 501 Compilers. Java Implementation Overview. JVM Architecture. JVM Runtime Data Areas (1) JVM Data Types. CSE P 501 Su04 T-1

Lecture Notes on Advanced Garbage Collection

Mark-Sweep and Mark-Compact GC

Run Time Environment

Compiler Construction D7011E

G Programming Languages - Fall 2012

Hazard Pointers. Number of threads unbounded time to check hazard pointers also unbounded! difficult dynamic bookkeeping! thread B - hp1 - hp2

Dynamic Memory Allocation

the gamedesigninitiative at cornell university Lecture 9 Memory Management

CSCI-1200 Data Structures Fall 2011 Lecture 24 Garbage Collection & Smart Pointers

Garbage Collection. Vyacheslav Egorov

CS61C : Machine Structures

Process s Address Space. Dynamic Memory. Backing the Heap. Dynamic memory allocation 3/29/2013. When a process starts the heap is empty

Garbage Collection Techniques

ITP 342 Advanced Mobile App Dev. Memory

Opera&ng Systems CMPSCI 377 Garbage Collec&on. Emery Berger and Mark Corner University of Massachuse9s Amherst

CS 536 Introduction to Programming Languages and Compilers Charles N. Fischer Lecture 11

Motivation for Dynamic Memory. Dynamic Memory Allocation. Stack Organization. Stack Discussion. Questions answered in this lecture:

CS61C : Machine Structures

About this exam review

6.172 Performance Engineering of Software Systems Spring Lecture 9. P after. Figure 1: A diagram of the stack (Image by MIT OpenCourseWare.

CSCI-1200 Data Structures Spring 2017 Lecture 27 Garbage Collection & Smart Pointers

Dynamic Memory Allocation. CS 351: Systems Programming Michael Saelee

Dynamic Memory Allocation

Memory Management. Didactic Module 14 Programming Languages - EEL670 1

Memory Management. Chapter Fourteen Modern Programming Languages, 2nd ed. 1

CS 33. Storage Allocation. CS33 Intro to Computer Systems XXVII 1 Copyright 2017 Thomas W. Doeppner. All rights reserved.

Faculty of Electrical Engineering, Mathematics, and Computer Science Delft University of Technology

CS558 Programming Languages

Precise Garbage Collection for C. Jon Rafkind * Adam Wick + John Regehr * Matthew Flatt *

Algorithms for Dynamic Memory Management (236780) Lecture 4. Lecturer: Erez Petrank

Habanero Extreme Scale Software Research Project

Review. Partitioning: Divide heap, use different strategies per heap Generational GC: Partition by age Most objects die young

Compilers. 8. Run-time Support. Laszlo Böszörmenyi Compilers Run-time - 1

Compiler construction 2009

CS 4120 Lecture 37 Memory Management 28 November 2011 Lecturer: Andrew Myers

Mobile Application Development

Princeton University. Computer Science 217: Introduction to Programming Systems. Dynamic Memory Management

Dynamic Memory Management! Goals of this Lecture!

Simple Garbage Collection and Fast Allocation Andrew W. Appel

CS558 Programming Languages

Memory Allocation III

Q1: /8 Q2: /30 Q3: /30 Q4: /32. Total: /100

Managed runtimes & garbage collection. CSE 6341 Some slides by Kathryn McKinley

(notes modified from Noah Snavely, Spring 2009) Memory allocation

Memory Allocation. Copyright : University of Illinois CS 241 Staff 1

Garbage Collection (1)

Transcription:

Garbage Collection CS 351: Systems Programming Michael Saelee <lee@iit.edu>

= automatic deallocation i.e., malloc, but no free!

system must track status of allocated blocks free (and potentially reuse) when unneeded

Two typical approaches: 1. reference counting 2. tracing

1. reference counting = maintain count of references to a given object; when refcount 0, dealloc (analogous to FD OFD tracking; i.e., when 0 FDs refer to OFD, free it)

Two flavors of reference counting: - manual reference counting - automatic reference counting

manual/automatic pertain to who is in charge of tracking # refs (user vs. runtime)

case study: ObjC refcount adjusting methods: - new: create obj with refcount = 1 - retain: increment refcount - release: decrement refcount [note: ok to call methods on nil (no effect)]

e.g., manual reference counting (ObjC) @implementation Foo { // definition of class Foo Widget *_mywidget; // instance var declaration (inited to nil) - (void)setwidget:(widget *)w { // setter method [_mywidget release]; // no longer need old obj; refcount-- _mywidget = [w retain]; // take ownership of new obj; refcount++ - (void)dealloc { // called when Foo s refcount = 0 [_mywidget release]; // release hold of obj in ivar; refcount-- @end Widget *w = [Widget new]; // allocate Widget obj; refcount = 1 Foo *f = [Foo new]; // allocate Foo obj; refcount = 1 [f setwidget:w]; // f now (also) owns w; refcount on w = 2 [w release]; // don't need w anymore; refcount on w = 1... [f release]; // done with f; refcount on f = 0 (dealloc'd) // refcount on w also -> 0 (dealloc d)

how is this preferable to malloc/free? much improved granularity of mem mgmt. - a single free will not universally release an object (what if someone still needs it?) - each object is responsible for its own data (instead of thinking for everyone)

and perhaps surprisingly, can also simplify mem mgmt. of nested/linked structures: @implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head (refcount=1) (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head (refcount=1) (refcount=1) (refcount=1) (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head (refcount=0) (refcount=1) (refcount=1) (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head dealloc d (refcount=0) (refcount=1) (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head dealloc d dealloc d (refcount=0) (refcount=1)

@implementation LinkedList { LLNode *_head; - (void)add:(id)obj { LLNode *n = [LLNode new]; [n setobj:obj]; [n setnext:_head]; [_head release]; _head = n; - (void)clear { [_head release]; _head = nil; - (void)dealloc { [_head release]; @end @implementation LLNode { id _obj; LLNode *_next; - (void)setobj:(id)obj { [_obj release]; _obj = [obj retain]; - (void)setnext:(llnode *)next { [_next release]; _next = [next retain]; - (void)dealloc { [_obj release]; [_next release]; @end head dealloc d dealloc d dealloc d dealloc d

but, a catch: beware circular references! head (refcount=2) (refcount=2) (refcount=2) (refcount=1)

but, a catch: beware circular references! head (refcount=1) (refcount=2) (refcount=2) (refcount=1) prevents dealloc!

typically establish conventions for nonretaining references; aka. weak references e.g., list:prior & tree:child links

rules for ref-counting are quite simple: - for strong pointers, on assignment must release old obj & retain new one - also release if pointer goes out of scope - for weak pointers, never retain; obj refcount is unaffected

automatic reference counting requires that we designate strong & weak pointers - retains & releases are automatic - dealloc on refcount = 0 is automatic

@implementation LLNode { strong id _obj; strong LLNode *_next; @end @implementation LinkedList { strong LLNode *_head; - (void)add:(id)obj { LLNode *node = [LLNode new]; node.obj = obj; // obj is retained by node node.next = _head; // _head is retained by node _head = node; // node is retained; old _head value is released - (void)clear { _head = nil; // releases (previous) _head, causing chain of deallocs @end

@implementation LLNode { strong id _obj; strong LLNode *_next; weak LLNode *_prior; // note weak pointer; will not auto-retain @end @implementation LinkedList { strong LLNode *_head; - (void)add:(id)obj { // creates a circular doubly-linked list LLNode *node = [LLNode new]; node.obj = obj; if (_head == nil) { node.next = node.prior = node; // only next retains (not prior) _head = node; else { node.next = _head; node.prior = _head.prior; _head.prior.next = _head.prior = node; _head = node; - (void)clear { _head.prior.next = nil; // take care that head is not in retain cycle _head = nil; // so that this deallocs list (prior refs ok) @end

important: ref-counting provides predictable deallocation i.e., when refcount 0 for an obj, it will be deallocated (contrast this with the next approach)

2. tracing = periodically determine what memory has become unreachable, and deallocate it

general approach: - treat memory as a graph - identify root nodes/pointers - (recursively) find all reachable memory

root nodes? - pointers in static memory - pointers in active stack frames

Static & Local Root space Heap space

simple algorithm: mark & sweep 1. mark all reachable blocks in heap 2. find all allocated, non-reachable blocks in heap & free (sweep) them; also unmark all blocks traversed

#define IS_ALLOCATED(p) (*(size_t *)(p) & 1) // bit 0 = allocated? #define IS_MARKED(p) (*(size_t *)(p) & 2) // bit 1 = marked? #define BLK_SIZE(p) (*(size_t *)(p) & ~3L) void sweep () { char *bp = heap_start(); while (bp < heap_end()) { if (IS_MARKED(bp)) *bp &= ~2L; // clear out mark bit else if (IS_ALLOCATED(bp)) free(bp + SIZE_T_SIZE); // free unmarked, allocated block bp += BLK_SIZE(bp); void mark(void *p) { char *bp = find_block_header(p); // expensive, but doable (how?) if (bp == NULL!IS_ALLOCATED(bp) IS_MARKED(bp)) { return; *(size_t *)bp = 2; // mark block // next, must recurse and mark all blocks reachable from this one

#define IS_ALLOCATED(p) (*(size_t *)(p) & 1) // bit 0 = allocated? #define IS_MARKED(p) (*(size_t *)(p) & 2) // bit 1 = marked? #define BLK_SIZE(p) (*(size_t *)(p) & ~3L) void sweep () { char *bp = heap_start(); while (bp < heap_end()) { if (IS_MARKED(bp)) *bp &= ~2L; // clear out mark bit else if (IS_ALLOCATED(bp)) free(bp + SIZE_T_SIZE); // free unmarked, allocated block bp += BLK_SIZE(bp); void mark(void *p) { char *bp = find_block_header(p); // expensive, but doable (how?) if (bp == NULL!IS_ALLOCATED(bp) IS_MARKED(bp)) { return; *(size_t *)bp = 2; // mark block for (int i=size_t_size; i<=blk_size(bp)-sizeof(void *); i++) { mark(*(void **)(bp + i)); // all words in payload can be pointers!

for (int i=size_t_size; i<=blk_size(bp)-sizeof(void *); i++) { mark(*(void **)(bp + i)); // all words in payload can be pointers! need to do this because the user can store a pointer just about anywhere using C - but may encounter a lot of false positives - i.e., not a real pointer, but results in an allocated block being marked this is a conservative GC implementation

for (int i=size_t_size; i<=blk_size(bp)-sizeof(void *); i++) { mark(*(void **)(bp + i)); // all words in payload can be pointers! note: this may still not be good enough! if the user chooses to derive pointer values (e.g., via arithmetic) GC typically needs to be able to make some assumptions for efficiency

in a system with run time type information, can reliably detect if a value is a pointer enables precise garbage collection

(moving onto other issues) typically want GC to run periodically in the background so that memory is freed without interrupting the user program

but may lead to concurrent access of heap data structures void sweep () { char *bp = heap_start(); while (bp < heap_end()) { if (IS_MARKED(bp)) *bp &= ~2L; // clear out mark bit else if (IS_ALLOCATED(bp)) free(bp + SIZE_T_SIZE); // free unmarked, allocated block bp += BLK_SIZE(bp); and, if unlucky, race conditions! (e.g., what if free leads to coalescing of block currently being allocated?)

also, in mark & sweep, manipulating the heap during a GC cycle invalidates marks

mark & sweep requires that the heap be frozen while being carried out aka stop-the-world GC horrible performance implications!

other algorithms permit on-the-fly marking e.g., tri-color marking

partition allocated blocks into 3 sets: - white: unscanned - black: reachable nodes that don t refer to white nodes - grey: reachable nodes (but may refer to black/white nodes)

at outset: - directly reachable blocks are grey - all other nodes are white

periodically: - scan all children of a grey object (making them black) - move white blocks found to grey set

i.e., always white grey black

when grey set is empty; i.e., when we ve scanned all reachable blocks, free any remaining white blocks

many other heuristics exist to optimize GC - generational GCs classify blocks by age and prioritize searching recent ones - copying GCs allow for memory compaction (require opaque pointers)

but question remains: when & how frequently to perform G.C.? - invariably incurs overhead - worse, results in unpredictable performance!

Demo: Java GC behavior

// Test bench for GC public class TestGCThread extends Thread { public void run(){ while(true){ try { int delay = (int)math.round(100 * Math.random()); Thread.sleep(delay); // random sleep catch(interruptedexception e){ // create random number of objects int count = (int)math.round(10000 * Math.random()); for (int i=0; i < count; i++){ new TestObject(); // immediately unreachable! public static void main(string[] args){ new TestGCThread().start(); class TestObject { static long alloced = 0; static long freed = 0; public TestObject () { alloced++; public void finalize () { freed++;