Heap liveness and its usage in automatic memory management Ran Shaham Elliot Kolodner Mooly Sagiv...
-
Upload
belinda-harrington -
Category
Documents
-
view
217 -
download
0
Transcript of Heap liveness and its usage in automatic memory management Ran Shaham Elliot Kolodner Mooly Sagiv...
Heap liveness and its usage in
automatic memory management
Ran Shaham
Elliot Kolodner
Mooly Sagiv
•ISMM’02
•Unpublished
http://www.cs.tau.ac.il/~ransh/
TV
LA inside
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 Classpublic 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
pp’
l is neededa reference to l is used
l is allocated
Needed Reference Expression
pp’
e is needed a reference to l is used
e references l
e is not needed free(e) is valid
l is allocated
A Pathological C Program
a = malloc(…) ;
b = a;
free (a);
c = malloc (…);
if (b == c) printf(“unexpected equality”);
a is needed
Location Liveness
pp’
l is live
l is not assigned
l is used
Reference Expression Liveness
pp’
e is live
e denotes a location l
l is not assigned
l is used
Generalizes liveness of program variables when l is &x
Inefficient Java Classpublic 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 IStack
y
z
gStatic
x
f
f2
f 1
f
fHeapL= t
…
use z.f
f
HeapL
= t
Heap Liveness Example IStack
y
z
gStatic
x
f
f2
f 1
f
fHeapL= t+2
…
use y.f2
f
HeapL= t
= t+2
Heap Liveness Example IIStack
y
z
gStatic
x
f
f2
f 1
f
f
StackR = t’’
f
StackR = Directly stack reachable
(computed during GC)
Collection time = max(t’, t’’)
Collection time(obj) = max(HeapL(obj), StackR(obj), StaticR(obj), OtherR(obj))
= t’’
HeapL= t’
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)
Analyzer - Complete Liv eness
0
0.5
1
1.5
2
2.5
3
3.5
0 50 100 150 200 250 300
allocation time (MB)
siz
e (
MB
) heap liveness
stack+staticliveness
w /out liveness
Potential Savings – Total Space
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
jess
raytr
ace
db
javac
jack
analy
zer
juru
eule
r
mc
tvla
average
heap+stack+static liveness heap liveness
stack+static liveness static liveness
stack liveness
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
Doubly-Linked List Example – First Run
x
n
pd d
n
pd d
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
x
n
pd d
n
pd d
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
y
Doubly-Linked List Example – First Run
n
pd d
n
pd d
yt
[pt2]
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
n
pd d
n
pd d
yt
[pt2]
[pt3]
d1
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
n
pd d
n
pd d
yt
[pt2]
[pt3]
d1
[pt4]
d2
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
n
pd d
n
pd d
y t
[pt2]
[pt3]
d1
[pt4]
d2
[pt5][pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
n
pd d
n
pd d
t y
[pt2]
[pt3]
d1
[pt4]
d2
[pt5]
[pt2]
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
[pt4]
Doubly-Linked List Example – First Run
n
pd d
n
pd d
t y
[pt2]
[pt3]
d1
[pt3]
d2
[pt5]
[pt1]
[pt1]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; // y.d = null process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example – First Run
n
pd d
n
pd d
[pt2]
[pt3]
d2
[pt3]
d1
[pt5]
[pt3]
[pt1]
[pt3]
// processing list elements in pairspt1: y = x.n; // x.n = null; x = null; while (y != null) { pt2: t = y.p; // y.p = null;pt3: d1 = t.d; // t.d = nullpt4: d2 = y.d; process(d1, d2); pt5: t = y.n; // y.n = null; y = t; }
Doubly-Linked List Example - Second Run
x
n
pd d
n
pd d
// processing list elements in pairspt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null;pt3: d1 = t.d; t.d = nullpt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; }
n
pd d
n
pd d
ty
// processing list elements in pairspt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null;pt3: d1 = t.d; t.d = nullpt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; }
Doubly-Linked List Example - Second Run
d1 d2
// processing list elements in pairspt1: y = x.n; x.n = null; x = null; while (y != null) { pt2: t = y.p; y.p = null;pt3: d1 = t.d; t.d = nullpt4: d2 = y.d; process(d1, d2); pt5: t = y.n; y.n = null; y = t; }
Doubly-Linked List Example - Second Run
d
n
pd d
ty
d1 d2
Assign Null - Potential Savings
0%
5%
10%
15%
20%
25%
30%
35%
40%
jes
s
ray
tra
ce
db
jav
ac
jac
k
an
aly
ze
r
juru
eu
ler
mc
tvla
av
era
ge
context=0
context=1
context=2
context=3
• 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; }
x
nn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x,y
nn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x,y
nn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x,y
n
llastu[pt3,y.n]
nn
t
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
y,t
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
y,t
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
y
llastu[pt3,y.n]
nt null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
y,t null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
y,t null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
llastu[pt3,y.n]
y
n
t
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
nn
llastu[pt3,y.n]
n
llastu[pt3,y.n]
n
y,tn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
nn
llastu[pt3,y.n]
n
llastu[pt3,y.n]
n
y,tn
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
llastu[pt3,y.n]
y
n
tn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
y,tn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
nn
llastu[pt3,y.n]
n
y,tn
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n]
n
llastu[pt3,y.n]
yn
llastu[pt3,y.n]
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n] llastu[pt3,y.n]
n
null[pt3,y.n]
Assign Null Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
llastu[pt3,y.n] llastu[pt3,y.n]
n
null[pt3,y.n]
• Static Reachability Information +Static Liveness Information CT garbage detection
• Issue “free(e)” for unneeded e
Compile-Time Garbage Collection
Exact Free Analysis
– Insert free(x) at program points when the x becomes unneeded
• Only one location is freed• All the paths to the statement must agree on the
garbage
– History predicates• lastu[pt,x](v)
– the last use of the location pointed-to by x occurs
• unneeded [pt,x]() – “free(x)” is safe after pt
Exact Free Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
nn
unneeded[pt1,x]
unneeded[pt2,y]
unneeded[pt3,y]
unneeded[pt4,t]
Exact unneeded Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x,y
nn
unneeded[pt1,x]
unneeded[pt2,y]
unneeded[pt3,y]
unneeded[pt4,t]
lastu[pt1,x]
Exact Free Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x,y
nn
unneeded[pt2,y]
unneeded[pt3,y]
unneeded[pt4,t]
lastu[pt2,y]
Exact Free Example // traversing list elements pt1: y = x; pt2: while (y != null) { pt3: t = y.n; pt4: y = t; }
x
n
unneeded[pt3,y]
lastu[pt3,y] lastu[pt3,y]
n
Preliminary Conclusions
• Heap Liveness is useful
• Can be dynamically computed for large programs
• More data is needed to confirm scalability of static analysis
• Recording local history is a powerful tool– Refines the abstraction