Application Extraction for Java: A retrospective
report of the Jax project
Frank Tip & Chris Laffra
Outline
• project background & goals
• techniques
• results
• industrial applications
• perspective
2
Jax Project Background & Goals
• successful project at IBM Research from 1998-2003
• goal:
- originally: shrink Java applications so that they can be downloaded faster,
especially over slow connections
- later: optimize Java applications running on embedded devices
• approach:
- static analysis (but rely on user to specify reflective behaviors and dynamic
class loading)
- program transformations (e.g. merging classes)
• applications:
- open-source release via alphaworks
- used by various IBM teams and IBM customers (e.g. apps running on
Wimbledon website)
- integrated in Smartlinker component of IBM WebSphere Device Developer
• contributors: Aldo Eisma, Chris Laffra, Jens Palsberg, David Streeter, Peter
Sweeney, Frank Tip
3
OOPSLA’99
What did it do?
• call graph construction
• removal of dead methods & dead fields
- also: removal of method bodies, write-only fields
• inlining of small methods
• class hierarchy transformations
• name compression
• constant pool compression
4
Call Graph Construction
• Jax relied on type-based algorithms
- Class Hierarchy Analysis (CHA) [Dean et al. 1995]
- Rapid Type Analysis (RTA) [Bacon 1997]
- XTA [Tip & Palsberg 2000]
• considerations:
- efficiency: scaled to thousands of classes
- simplicity: no need for construction of CFGs, SSA Form, pointer
analysis, …
- need to handle incomplete applications (Jax did not analyze the
code in Java’s standard libraries but made conservative
assumptions, in the spirit of Averroes [Ali 2013)
5
Example
public static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
Precise Call Graph
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
4 edges
7
XTA Algorithm for Call Graph Construction
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
8
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
9
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
10
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
{ B }
11
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
{ B, C }
12
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
{ B, C }
6 edges
13
XTA
static void main(){
B b1 = new B();
A a1 = new A();
f(b1);
g(b1);
}
static void f(A a2){
a2.foo();
}
static void g(B b2){
B b3 = b2;
b3 = new C();
b3.foo();
}
class A {
void foo(){ }
}
class B extends A {
void foo(){ }
}
class C extends B {
void foo(){ }
}
class D extends B {
void foo(){ }
}
{ A, B }
{ A, B }
{ B, C }
6 edges
RA: 10 edges
CHA: 9 edges
RTA: 8 edges
0-CFA: 6 edges (different) 14
XTA
15
OOPSLA’00
Removal of Dead Methods & Fields
• after call graph construction, remove unreachable
methods
- in some cases, must remove the body to preserve
dispatch behavior
• remove dead fields
• remove write-only fields
- e.g., fields initialized in constructor but never read
- also remove the write instructions
16
Method Call Inlining
• inline method calls if:
- XTA determines that a method call has a unique target
- inlining the call does not increase application size
17
Class Hierarchy Transformations
• remove uninstantiated class that does not have any subclasses
and that that does not contain any live methods or fields
• merge base class X and a derived class Y if (i) there is no live non-
abstract method f that occurs in both X and Y , and: (1) X is
uninstantiated, or (2) Y does not contain any live non-static fields.
- by requiring that (1) or (2) holds, we ensure that no object
created by the application becomes larger (i.e., contains more
fields) as a result of the merge
• similar transformations for merging classes with interfaces
18
Name Compression
• select short names (e.g., “a”, “b”, “c”) to classes, methods, fields
• care must be taken to preserve:
- overriding behavior
- overload resolution
19
Impact of the Transformations
20
ACM TOPLAS’02
Modular Extraction Language (MEL)
• specify that methods are dynamically loaded or
reflectively accessed
• support various extraction scenarios:
- extract library without assumptions about clients
- extract library in the context of a specific
application
- extract client without assumptions about libraries
21
FSE’00
Results
22
CACM’03
SmartLinker - Commercial Application of Jax
• IBM J9 Virtual Machine for embedded platforms offers Japt:
- an optimization utility to reduce Java classes from a JAR file
- based directly on Jax algorithms, RTA and XTA
- focus on embedded, inlining JSR calls, AOT, JXE linking
23
BTA
AOT
Link
RTX, XTA
Inlining
flattening
Smartlinker
jax
AOT
Link
RTX, XTA, ITA
inlining
flattening
jar2jxe
japt
Smartlinker Use Case: Strings in Eclipse
• 50% of the heap is Strings of which 50% is unique
• String compaction can save 35% of the heap
• Combining Japt compression with memory-mapped JXEs
allows WebSphere Application Server to scale 2X
K
95K
190K
285K
380K
475K
Original Japt
Eclipse runtime.jar
Holistic Optimisation - Faster and Less Memory
Lessons Learned
• Debloating can offer dramatic improvements in memory consumption and startup times.
• OO design is a useful technique, providing meaningful abstractions and scalable designs.
Developers should keep using redundant class definitions, deep inheritance, verbose interfaces.
- rely on static analysis tools like Jax/SmartLinker to debloat verbose libraries & frameworks
• the debloating techniques that we applied are broadly applicable:
- applets, applications, Eclipse, WAS, embedded platforms, Android
- any OO language, e.g. Swift, C#, Objective-C, Kotlin, C++.
• Promising future work would be scenario-based modularization of applications. For mobile, startup
times are key. So, can we analyze what is needed just for startup? Another scenario would be
once-only tasks, such as registration. Each component could have its own optimization techniques
applied, for instance startup code would benefit from AOT (which costs more space on disk, but
loads a lot faster). Once-only code would not be AOT'd.
• Current work at Northeastern: debloating of applications written in dynamic languages
- use a combination of (i) unsound static/dynamic analysis and (ii) a recovery mechanism for
dynamic loading of missing functionality (subject to checks). Current focus on JavaScript.
26
Publications and References
• Frank Tip, Chris Laffra, Peter F. Sweeney, David Streeter: Practical Experience with an
Application Extractor for Java. OOPSLA 1999: 292-305
• Peter F. Sweeney, Frank Tip: Extracting library-based object-oriented applications. SIGSOFT
FSE 2000: 98-107
• Frank Tip, Jens Palsberg: Scalable propagation-based call graph construction algorithms.
OOPSLA 2000: 281-293
• Frank Tip, Peter F. Sweeney, Chris Laffra, Aldo Eisma, David Streeter: Practical extraction
techniques for Java. ACM Trans. Program. Lang. Syst. 24(6): 625-666 (2002)
• Frank Tip, Peter F. Sweeney, Chris Laffra: Extracting library-based Java applications. Commun.
ACM 46(8): 35-40 (2003)
• WebSphere Everyplace Custom Environment product definition at www-
01.ibm.com/software/wireless/wece/features.html, referencing Japt.
• Sean Foley: From Global to Local Escape Analysis in Java. Performance Engineering Best
Practices Conference (2008)
27

Jax retrospective

  • 1.
    Application Extraction forJava: A retrospective report of the Jax project Frank Tip & Chris Laffra
  • 2.
    Outline • project background& goals • techniques • results • industrial applications • perspective 2
  • 3.
    Jax Project Background& Goals • successful project at IBM Research from 1998-2003 • goal: - originally: shrink Java applications so that they can be downloaded faster, especially over slow connections - later: optimize Java applications running on embedded devices • approach: - static analysis (but rely on user to specify reflective behaviors and dynamic class loading) - program transformations (e.g. merging classes) • applications: - open-source release via alphaworks - used by various IBM teams and IBM customers (e.g. apps running on Wimbledon website) - integrated in Smartlinker component of IBM WebSphere Device Developer • contributors: Aldo Eisma, Chris Laffra, Jens Palsberg, David Streeter, Peter Sweeney, Frank Tip 3 OOPSLA’99
  • 4.
    What did itdo? • call graph construction • removal of dead methods & dead fields - also: removal of method bodies, write-only fields • inlining of small methods • class hierarchy transformations • name compression • constant pool compression 4
  • 5.
    Call Graph Construction •Jax relied on type-based algorithms - Class Hierarchy Analysis (CHA) [Dean et al. 1995] - Rapid Type Analysis (RTA) [Bacon 1997] - XTA [Tip & Palsberg 2000] • considerations: - efficiency: scaled to thousands of classes - simplicity: no need for construction of CFGs, SSA Form, pointer analysis, … - need to handle incomplete applications (Jax did not analyze the code in Java’s standard libraries but made conservative assumptions, in the spirit of Averroes [Ali 2013) 5
  • 6.
    Example public static voidmain(){ B b1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } }
  • 7.
    Precise Call Graph staticvoid main(){ B b1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } 4 edges 7
  • 8.
    XTA Algorithm forCall Graph Construction static void main(){ B b1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } 8
  • 9.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } 9
  • 10.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } 10
  • 11.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } { B } 11
  • 12.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } { B, C } 12
  • 13.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } { B, C } 6 edges 13
  • 14.
    XTA static void main(){ Bb1 = new B(); A a1 = new A(); f(b1); g(b1); } static void f(A a2){ a2.foo(); } static void g(B b2){ B b3 = b2; b3 = new C(); b3.foo(); } class A { void foo(){ } } class B extends A { void foo(){ } } class C extends B { void foo(){ } } class D extends B { void foo(){ } } { A, B } { A, B } { B, C } 6 edges RA: 10 edges CHA: 9 edges RTA: 8 edges 0-CFA: 6 edges (different) 14
  • 15.
  • 16.
    Removal of DeadMethods & Fields • after call graph construction, remove unreachable methods - in some cases, must remove the body to preserve dispatch behavior • remove dead fields • remove write-only fields - e.g., fields initialized in constructor but never read - also remove the write instructions 16
  • 17.
    Method Call Inlining •inline method calls if: - XTA determines that a method call has a unique target - inlining the call does not increase application size 17
  • 18.
    Class Hierarchy Transformations •remove uninstantiated class that does not have any subclasses and that that does not contain any live methods or fields • merge base class X and a derived class Y if (i) there is no live non- abstract method f that occurs in both X and Y , and: (1) X is uninstantiated, or (2) Y does not contain any live non-static fields. - by requiring that (1) or (2) holds, we ensure that no object created by the application becomes larger (i.e., contains more fields) as a result of the merge • similar transformations for merging classes with interfaces 18
  • 19.
    Name Compression • selectshort names (e.g., “a”, “b”, “c”) to classes, methods, fields • care must be taken to preserve: - overriding behavior - overload resolution 19
  • 20.
    Impact of theTransformations 20 ACM TOPLAS’02
  • 21.
    Modular Extraction Language(MEL) • specify that methods are dynamically loaded or reflectively accessed • support various extraction scenarios: - extract library without assumptions about clients - extract library in the context of a specific application - extract client without assumptions about libraries 21 FSE’00
  • 22.
  • 23.
    SmartLinker - CommercialApplication of Jax • IBM J9 Virtual Machine for embedded platforms offers Japt: - an optimization utility to reduce Java classes from a JAR file - based directly on Jax algorithms, RTA and XTA - focus on embedded, inlining JSR calls, AOT, JXE linking 23 BTA AOT Link RTX, XTA Inlining flattening Smartlinker jax AOT Link RTX, XTA, ITA inlining flattening jar2jxe japt
  • 24.
    Smartlinker Use Case:Strings in Eclipse • 50% of the heap is Strings of which 50% is unique • String compaction can save 35% of the heap • Combining Japt compression with memory-mapped JXEs allows WebSphere Application Server to scale 2X K 95K 190K 285K 380K 475K Original Japt Eclipse runtime.jar
  • 25.
    Holistic Optimisation -Faster and Less Memory
  • 26.
    Lessons Learned • Debloatingcan offer dramatic improvements in memory consumption and startup times. • OO design is a useful technique, providing meaningful abstractions and scalable designs. Developers should keep using redundant class definitions, deep inheritance, verbose interfaces. - rely on static analysis tools like Jax/SmartLinker to debloat verbose libraries & frameworks • the debloating techniques that we applied are broadly applicable: - applets, applications, Eclipse, WAS, embedded platforms, Android - any OO language, e.g. Swift, C#, Objective-C, Kotlin, C++. • Promising future work would be scenario-based modularization of applications. For mobile, startup times are key. So, can we analyze what is needed just for startup? Another scenario would be once-only tasks, such as registration. Each component could have its own optimization techniques applied, for instance startup code would benefit from AOT (which costs more space on disk, but loads a lot faster). Once-only code would not be AOT'd. • Current work at Northeastern: debloating of applications written in dynamic languages - use a combination of (i) unsound static/dynamic analysis and (ii) a recovery mechanism for dynamic loading of missing functionality (subject to checks). Current focus on JavaScript. 26
  • 27.
    Publications and References •Frank Tip, Chris Laffra, Peter F. Sweeney, David Streeter: Practical Experience with an Application Extractor for Java. OOPSLA 1999: 292-305 • Peter F. Sweeney, Frank Tip: Extracting library-based object-oriented applications. SIGSOFT FSE 2000: 98-107 • Frank Tip, Jens Palsberg: Scalable propagation-based call graph construction algorithms. OOPSLA 2000: 281-293 • Frank Tip, Peter F. Sweeney, Chris Laffra, Aldo Eisma, David Streeter: Practical extraction techniques for Java. ACM Trans. Program. Lang. Syst. 24(6): 625-666 (2002) • Frank Tip, Peter F. Sweeney, Chris Laffra: Extracting library-based Java applications. Commun. ACM 46(8): 35-40 (2003) • WebSphere Everyplace Custom Environment product definition at www- 01.ibm.com/software/wireless/wece/features.html, referencing Japt. • Sean Foley: From Global to Local Escape Analysis in Java. Performance Engineering Best Practices Conference (2008) 27