740 likes | 869 Views
Heap liveness and its usage in automatic memory management. ISMM’02 Unpublished. Ran Shaham Elliot Kolodner Mooly Sagiv. TVLA inside. http://www.cs.tau.ac.il/~ransh/. Motivation. An object could be collected once it is no longer needed
E N D
Heap liveness and its usage in automatic memory management • ISMM’02 • Unpublished Ran Shaham Elliot Kolodner Mooly Sagiv TVLAinside http://www.cs.tau.ac.il/~ransh/
Motivation • An object could be collected once it is no longer needed • Yet, run-time garbage collectors (RTGCs) are typically based on reachability • Profiling tools can detect when objects are needed • The compiler can: • Statically identify a subset of unneeded objects • Issue a free instruction (compile-time Garbage Collection) • Issue a warning when a potentially needed object is reclaimed • Inform run-time garbage collector that a reference to an object is not further used
A Pathological C Program a = malloc(…) ; b = a; free (a); c = malloc (…); if (b == c) printf(“unexpected equality”);
Inefficient Java Class public Class Stack { private Object stack[]; private int top; public Stack(int len) { stack = new Object[len]; top = 0; } public synchronized Object pop() { top= top-1; return stack[top]; } public synchronized void push(Object o) { stack[top]=o; top= top+1; } public synchronized void print() { for (int i=0; i<top; i++) { System.out.println(stack[i]); } } } GC does not reclaim the memory stack[top+1]
Needed Location l is needed a reference to l is used p p’ l is allocated
Needed Reference Expression a reference to l is used e is needed p p’ l is allocated e references l e is not needed free(e) is valid
A Pathological C Program a = malloc(…) ; b = a; free (a); c = malloc (…); if (b == c) printf(“unexpected equality”); a is needed
Location Liveness l is live l is used p p’ l is not assigned
Reference Expression Liveness e is live l is used p p’ l is not assigned e denotes a location l Generalizes liveness of program variables when l is &x
Inefficient Java Class public Class Stack { private Object stack[]; private int top; public Stack(int len) { stack = new Object[len]; top = 0; } public synchronized Object pop() { top= top-1; return stack[top]; } public synchronized void push(Object o) { stack[top]=o; top= top+1; } public synchronized void print() { for (int i=0; i<top; i++) { System.out.println(stack[i]); } } } stack[top+1] is not live
Typical GC Limits class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root
Typical GC Limits class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root
Typical GC Limits class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root
Liveness Analysis Aids GC class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root root.right is live, root.left is dead
Liveness Analysis Aids GC class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root root.right is live, root.left is dead
Liveness Analysis Aids GC class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root root.right is live, root.left is dead
Liveness Analysis Aids GC class Node { Node left, right; int data; } class C { void main(…) { Node root = createTree(); processTree(root.right); } } root root.right is live, root.left is dead
Typical GC Limits Program Variables a b c d e f
Outline • Dynamic liveness measurements • Complete location liveness • Assign-null interface • Static analysis algorithms • (Exact) assign null (improve GC) • (Exact) free (CTGC)
Dynamic Liveness Measurements • Estimating the potential of static analysis • Find upper bounds on expected savings • Can be used in as an assistant tool • Liveness information kinds • Stack reference liveness • Global reference liveness • Heap reference liveness
Main Results • Dynamic measurements for 10 benchmarks • Shallow information Small Potential • local variables 2% • global variables 5% • local + global 9% • Deep information Larger Potential • heap liveness 39% complete location liveness 15% assign-null interface
Dynamic measurements • Implemented via an instrumented JVM • Complete location liveness measurements • Single-run algorithm • Assign-null liveness interface • Determines the liveness of expressions • Assign null to dead reference expressions • Requires two runs of the program
Complete Liveness Measurements • An Observation The last use of the references to an object determines the time an object could be collected assuming liveness information
Heap Liveness Example I Stack … use z.f = t x f y f1 f2 z HeapL= t HeapL f f f Static g
Heap Liveness Example I Stack … use y.f2 = t+2 x f y f1 f2 z HeapL= t+2 HeapL= t f f f Static g
Heap Liveness Example II Stack = t’’ StackR = Directly stack reachable (computed during GC) x f StackR = t’’ HeapL= t’ y f1 Collection time = max(t’, t’’) f2 z f f f Static g Collection time(obj) = max(HeapL(obj), StackR(obj), StaticR(obj), OtherR(obj))
Complete Liveness Summary • Mutator • Tracks the last use of references to an object • Collector • Propagation needed for stack/static liveness • Propagates reachability information • Propagates path liveness • Object Collection/Program Termination • Maximum of liveness/reachability properties of an object • Depends on liveness scheme (heap liveness etc.)
Experimental Results • Instrumented Sun’s classic JVM (1.2) • 10 benchmarks (5 SPECjvm) • Time is measured bytes allocated by the mutator so far in program • Total space savings (integral) • Maximum heap size savings (footprint)
Restricted GC Interface • GC Interface • Should be simple/effective/efficient • Feasible heap liveness representation • Assign null to dead heap references • Simple • Effective? • Partially answered by our experiments • Efficient? • Will be answered by static analysis
Null Assignable Program Points • Normalized statements (Java Bytecode) • Manipulate at most one heap reference • x = y.f is null assignable • Could be followed by y.f = null • Dynamic algorithm • First run • Determine null assignable program points • Assume all program points are null assignable • Detect non-null-assignable program points during the run • Second run • Assign null in null assignable program points
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } n x p d d
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } y n [pt1] x p d d
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } y t n [pt1] [pt2] p d d
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } y t n [pt1] [pt2] p [pt3] d d d1
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } y t n [pt1] [pt2] p [pt3] d d [pt4] d2 d1
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } y t n [pt1] [pt5] [pt2] p [pt3] d d [pt4] d2 d1
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } t y n [pt1] [pt5] [pt2] [pt2] p [pt3] d d [pt4] d2 d1
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } t y n [pt1] [pt5] [pt2] [pt1] p [pt3] d d [pt3] [pt4] d2 d1
n p d d Doubly-Linked List Example – First Run // processing list elements in pairs pt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null; pt3: d1 = t.d; // t.d = null pt4: d2 = y.d; process(d1, d2); pt5: t = y.n; // y.n = null; y = t; } n [pt1] [pt5] [pt2] [pt3] p [pt3] d d [pt3] [pt3] d1 d2
n p d d Doubly-Linked List Example - Second Run // processing list elements in pairs pt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null; pt3: d1 = t.d; t.d = null pt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; } n x p d d
d Doubly-Linked List Example - Second Run // processing list elements in pairs pt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null; pt3: d1 = t.d; t.d = null pt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; } y t n n p p d d d d2 d1
d Doubly-Linked List Example - Second Run // processing list elements in pairs pt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null; pt3: d1 = t.d; t.d = null pt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; } y t n p d d d2 d1
15% average savings for context = 2 • 11% assigning null to instance fields • 10% assigning null to array elements • Results are valid across runs • Detecting null assignable program points on a second input • Running the program with the first input • null assignable program points are those detected for both inputs
Related Work • On the Usefulness of Liveness for Garbage Collection and Leak Detection [HDH01] • Does not handle heap liveness • Algorithm requires two runs • First run: record uses and defs • Analyze log backwards for liveness information • Second run: use liveness results • Garbage Collection and Local Variable Type-Precision and Liveness in Java Virtual Machines [ADM98] • Stack liveness static analysis • Actual trends match our upper bounds • On the Effectiveness of GC in Java [SKS00] • Drag information • Slightly larger potential than heap liveness information • Not clear how to automate space savings • HUP tool (PLDI’01 + M. Pan)
Dynamic liveness measurements -Conclusion • Liveness Information has large potential • Assign null savings “achievable” by static analysis • Stack liveness information • Small potential • Stack+static liveness information • Medium potential • Heap liveness information • Is feasible • Recording history on heap is a powerful mechanism • Larger potential • Depends on static analysis precision • Depends on GC interface
Static Analysis • Combine history with shape analysis • a-La-Horwitz, Pfeiffer, and Reps 1989 • Assign null • Assign null to a dead reference expression • GC exploits information • Free • free an unneeded object
Assign Null Analysis • Insert “x.fld = null” after statements in which the expression x.fld becomes dead • Limitations • Only one reference is assigned null • All the paths to the statement must agree • Detects last-use • Technically • llastu[pt,x.fld](v) • The last use of the location denoted by x.fld occurs at pt • null[pt,x.fld]() • It is safe to insert “x.fld = null” after pt
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; } n n null[pt3,y.n] x