Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Exploring Hidden Java Costs (360|AnDev, July 2016)

Exploring Hidden Java Costs (360|AnDev, July 2016)

As Java 8 features comes to Android, it’s important to remember that every standard library API and language feature comes with an associated cost. Even as devices get faster and have more memory, concerns of code size and performance overhead are still very relevant. This talk will be an exploration into hidden costs associated with some of Java’s features. We’ll focus on optimizations relevant for both library and application developers and on the tools that can be used to measure their impact.

Video: https://www.youtube.com/watch?v=WALV33rWye4

Jake Wharton

July 29, 2016
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

  1. How many methods? $ echo "class Example {
 }" >

    Example.java $ javac Example.java 2? 0! 1?
  2. How many methods? $ echo "class Example {
 }" >

    Example.java $ javac Example.java $ javap Example.class 2? 0! 1?
  3. How many methods? $ echo "class Example {
 }" >

    Example.java $ javac Example.java $ javap Example.class class Example { Example(); } 2? 0! 1?
  4. $ echo "class Example {
 }" > Example.java $ javac

    Example.java $ javap Example.class class Example { Example(); } How many methods? $ echo "class Example {
 }" > Example.java $ javac Example.java $ javap Example.class class Example { Example(); } 2? 0! 1? How many methods? 0! 1? 2?
  5. $ echo "class Example {
 }" > Example.java $ javac

    Example.java $ javap Example.class class Example { Example(); } How many methods? 2? 0! 1!
  6. $ dx --dex --output=example.dex Example.class $ dexdump -f example.dex Opened

    'example.dex', DEX version '035' DEX file header: magic : 'dex\n035\0' checksum : b4e92dde signature : f51f...a6a1 file_size : 436 header_size : 112 link_size : 0 link_off : 0 (0x000000) string_ids_size : 5 string_ids_off : 112 (0x000070) type_ids_size : 3 type_ids_off : 132 (0x000084) proto_ids_size : 1 proto_ids_off : 144 (0x000090) field_ids_size : 0 field_ids_off : 0 (0x000000) method_ids_size : 2 method_ids_off : 156 (0x00009c) class_defs_size : 1 class_defs_off : 172 (0x0000ac) data_size : 232 data_off : 204 (0x0000cc) How many methods? 2? 0! 1!
  7. $ dx --dex --output=example.dex Example.class $ dexdump -f example.dex Opened

    'example.dex', DEX version '035' DEX file header: magic : 'dex\n035\0' checksum : b4e92dde signature : f51f...a6a1 file_size : 436 header_size : 112 link_size : 0 link_off : 0 (0x000000) string_ids_size : 5 string_ids_off : 112 (0x000070) type_ids_size : 3 type_ids_off : 132 (0x000084) proto_ids_size : 1 proto_ids_off : 144 (0x000090) field_ids_size : 0 field_ids_off : 0 (0x000000) method_ids_size : 2 method_ids_off : 156 (0x00009c) class_defs_size : 1 class_defs_off : 172 (0x0000ac) data_size : 232 data_off : 204 (0x0000cc) How many methods? $ dx --dex --output=example.dex Example.class $ dexdump -f example.dex Opened 'example.dex', DEX version '035' DEX file header: magic : 'dex\n035\0' checksum : b4e92dde signature : f51f...a6a1 file_size : 436 header_size : 112 link_size : 0 link_off : 0 (0x000000) string_ids_size : 5 string_ids_off : 112 (0x000070) type_ids_size : 3 type_ids_off : 132 (0x000084) proto_ids_size : 1 proto_ids_off : 144 (0x000090) field_ids_size : 0 field_ids_off : 0 (0x000000) method_ids_size : 2 method_ids_off : 156 (0x00009c) class_defs_size : 1 class_defs_off : 172 (0x0000ac) data_size : 232 data_off : 204 (0x0000cc) 2? 0! 1!
  8. $ dex-method-list example.dex Example <init>() java.lang.Object <init>() $ javap -c

    Example.class class Example { Example(); Code: 0: aload_0 1: invokespecial #1 //java/lang/Object."<init>":()V 4: return } How many methods? 2? 0! 1!
  9. $ dex-method-list example.dex Example <init>() java.lang.Object <init>() $ javap -c

    Example.class class Example { Example(); Code: 0: aload_0 1: invokespecial #1 //java/lang/Object."<init>":()V 4: return } How many methods? 2? 0! 1! $ dex-method-list example.dex Example <init>() java.lang.Object <init>() $ javap -c Example.class class Example { Example(); Code: 0: aload_0 1: invokespecial #1 //java/lang/Object."<init>":()V 4: return } 2? 0! 1! How many methods?
  10. $ dex-method-list example.dex Example <init>() java.lang.Object <init>() $ javap -c

    Example.class class Example { Example(); Code: 0: aload_0 1: invokespecial #1 //java/lang/Object."<init>":()V 4: return } How many methods? 2! 0! 1!
  11. How many methods? 2 0 1 class Example {
 }"

    0 declared methods 0 in the source file
  12. How many methods? 2 1 class Example {
 }" 0

    declared methods 0 in the source file 0 and only humans care
  13. How many methods? 2 class Example {
 }" 0 declared

    methods 1 1 compiled method 0 in the class file
  14. How many methods? 2 class Example {
 }" 0 declared

    methods 1 compiled method 2 referenced methods 0 in the dex file
  15. How many methods? class Example {
 }3 0 declared methods

    1 compiled method 2 referenced methods 0 in the dex file 0 for the true "method count"
  16. // ItemsView.java public class ItemsView { private class ItemsAdapter {

    }
 }" $ javac ItemsView.java $ ls ItemsView.class ItemsView.java ItemsView$ItemsAdapter.class
  17. // ItemsView.java public class ItemsView { private class ItemsAdapter {

    }
 }" 
 $ javap ItemsView public class ItemsView { public ItemsView(); }
  18. // ItemsView.java public class ItemsView { private class ItemsAdapter {

    }G
 }" 
 $ javap 'ItemsView$ItemsAdapter' class ItemsView$ItemsAdapter { ItemsView$ItemsAdapter(); } 
 // ItemsAdapter.java
  19. // ItemsView.java public class ItemsView { private class ItemsAdapter {

    }G
 }" 
 // ItemsView$ItemsAdapter.java ItemsView$
  20. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 
 private class ItemsAdapter {
 }G
 }"
  21. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }" 
 
 
 
 
 // ItemsView$ItemsAdapter.java ItemsView$
  22. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  23. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 }" 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G 
 
 
 
 
 private
  24. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 }" 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G 
 
 
 
 
 private // ItemsView.java public class ItemsView {
 private static String displayText(String item) {
 return ""; // TODO
 }
 }" 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }
 }
  25. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  26. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ ItemsView.java $ javap -c 'ItemsView$ItemsAdapter'

    class ItemsView$ItemAdapter { void bindItem(android.widget.TextView, java.lang.String); Code: 0: aload_1 1: aload_2 2: invokestatic #3 // Method ItemsView.access$000:… 5: invokevirtual #4 // Method TextView.setText:… 8: return }
  27. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ ItemsView.java $ javap -c 'ItemsView$ItemsAdapter'

    class ItemsView$ItemAdapter { void bindItem(android.widget.TextView, java.lang.String); Code: 0: aload_1 1: aload_2 2: invokestatic #3 // Method ItemsView.access$000:… 5: invokevirtual #4 // Method TextView.setText:… 8: return }
  28. $ javap -p ItemsView123 class ItemsView { ItemsView(); private static

    java.lang.String displayText(…); static java.lang.String access$000(…); } -c
  29. $ javap -p -c ItemsView123 class ItemsView { ItemsView(); Code:

    <removed> private static java.lang.String displayText(…); Code: <removed> static java.lang.String access$000(…); Code: 0: aload_0 1: invokestatic #1 // Method displayText:… 4: areturn }
  30. $ javap -p -c ItemsView class ItemsView { ItemsView(); Code:

    <removed> private static java.lang.String displayText(…); Code: <removed> static java.lang.String access$000(…); Code: 0: aload_0 1: invokestatic #1 // Method displayText:… 4: areturn }
  31. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }B
 }A 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }b
 }a
  32. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }B static String access$000(String item) {
 return displayText(item);
 }C
 }A 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }b
 }a // ItemsView.java public class ItemsView {
 private static String displayText(String item) {
 return ""; // TODO
 }B static String access$000(String item) {
 return displayText(item);
 }C
 }A 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }b
 }a
  33. // ItemsView.java public class ItemsView {
 private static String displayText(String

    item) {
 return ""; // TODO
 }B static String access$000(String item) {
 return displayText(item);
 }C
 }A 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.access$000(item));
 }b
 }a // ItemsView.java public class ItemsView {
 private static String displayText(String item) {
 return ""; // TODO
 }B static String access$000(String item) {
 return displayText(item);
 }C
 }A 
 // ItemsView$ItemsAdapter.java class ItemsView$ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.access$000(item));
 }b
 }a 
 
 
 
 
 
 
 
 displayText
  34. $ dx --dex --output=example.dex *.class $ dex-method-list example.dex ItemsView <init>()

    ItemsView access$000(String) → String ItemsView displayText(String) → String ItemsView$ItemsAdapter <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  35. $ dx --dex --output=example.dex *.class $ dex-method-list example.dex ItemsView <init>()

    ItemsView access$000(String) → String ItemsView displayText(String) → String ItemsView$ItemsAdapter <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  36. $ java -jar android-sdk/build-tools/24.0.1/jack.jar \ -cp android-sdk/platforms/android-24/android.jar \ --output-dex .

    \ ItemsView.java $ dex-method-list classes.dex ItemsView -wrap0(String) → String ItemsView <init>() ItemsView displayText(String) → String ItemsView$ItemsAdapter <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  37. $ java -jar android-sdk/build-tools/24.0.1/jack.jar \ -cp android-sdk/platforms/android-24/android.jar \ --output-dex .

    \ ItemsView.java $ dex-method-list classes.dex ItemsView -wrap0(String) → String ItemsView <init>() ItemsView displayText(String) → String ItemsView$ItemsAdapter <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  38. $ echo "-dontobfuscate -keep class ItemsView$ItemsAdapter { void bindItem(...); }

    " > rules.txt $ java -jar proguard-base-5.2.1.jar \ -include rules.txt \ -injars . \ -outjars example-proguard.jar \ -libraryjars android-sdk/platforms/android-24/android.jar $ dex-method-list example-proguard.jar $ dex-method-list example-proguard.jar
  39. $ echo "-dontobfuscate -keep class ItemsView$ItemsAdapter { void bindItem(...); }

    " > rules.txt $ java -jar proguard-base-5.2.1.jar \ -include rules.txt \ -injars . \ -outjars example-proguard.jar \ -libraryjars android-sdk/platforms/android-24/android.jar $ dex-method-list example-proguard.jar ItemsView access$000(String) → String ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence)
  40. $ dex-method-list example-proguard.jar ItemsView <init>() ItemsView access$000(String) → String ItemsView$ItemsAdapter

    <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  41. $ dex-method-list example-proguard.jar ItemsView <init>() ItemsView access$000(String) → String ItemsView$ItemsAdapter

    <init>(ItemsView) ItemsView$ItemsAdapter bindItem(TextView, String) android.widget.TextView setText(CharSequence) java.lang.Object <init>()
  42. $ unzip example-proguard.jar $ javap -c ItemsView public final class

    ItemsView { static java.lang.String access$000(java.lang.String); Code: 0: ldc #1 // String "" 2: areturn }
  43. class MyActivity extends Activity {
 @Override protected void onCreate(Bundle state)

    {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 // Hello!
 }
 });
 }
 }
  44. class MyActivity extends Activity {
 @Override protected void onCreate(Bundle state)

    {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 // Hello!
 }C
 });
 }B
 }A
  45. class MyActivity extends Activity {
 @Override protected void onCreate(Bundle state)

    {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 doSomething(); }C
 });
 }B private void doSomething() {
 // ...
 }D
 }A 
 
 
 
 
 // Hello!
  46. class MyActivity extends Activity {
 private int count; 
 @Override

    protected void onCreate(Bundle state) {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 count = 0;
 ++count;
 --count;
 count++;
 count--;
 Log.d("Count", "= " + count); }C
 });
 }B
 }A 
 
 
 
 
 
 doSomething(); 
 
 private void doSomething() {
 // ...
 }D

  47. class MyActivity extends Activity {
 private int count; 
 @Override

    protected void onCreate(Bundle state) {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 count = 0;
 ++count;
 --count;
 count++;
 count--;
 Log.d("Count", "= " + count); }C
 });
 }B
 }A
  48. class MyActivity extends Activity {
 private int count; 
 @Override

    protected void onCreate(Bundle state) {
 super.onCreate(state); 
 setContentView(R.layout.whatever);
 findViewById(R.id.button).setOnClickListener( new OnClickListener() {
 @Override public void onClick(View view) {
 count = 0;
 ++count;
 --count;
 count++;
 count--;
 Log.d("Count", "= " + count); }C
 });
 }B
 }A
  49. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ MyActivity.java $ javap MyActivity class

    MyActivity extends android.app.Activity { MyActivity(); protected void onCreate(android.os.Bundle); static int access$002(MyActivity, int); static int access$004(MyActivity); static int access$006(MyActivity); static int access$008(MyActivity); static int access$010(MyActivity); static int access$000(MyActivity); }
  50. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ MyActivity.java $ javap MyActivity class

    MyActivity extends android.app.Activity { MyActivity(); protected void onCreate(android.os.Bundle); static int access$002(MyActivity, int); static int access$004(MyActivity); static int access$006(MyActivity); static int access$008(MyActivity); static int access$010(MyActivity); static int access$000(MyActivity); } // count = 0 write // ++count preinc // --count predec // count++ postinc // count-- postdec // count read
  51. $ adb shell mkdir /mnt/sdcard/apks $ adb shell cmd package

    list packages -3 -f \ | cut -c 9- \ | sed 's|=| /mnt/sdcard/apks/|' \ | xargs -t -L1 adb shell cp $ adb pull /mnt/sdkcard/apks
  52. accessors.sh #!/bin/bash
 set -e
 
 METHODS=$(dex-method-list $1 | \grep 'access\$')


    ACCESSORS=$(echo "$METHODS" | wc -l | xargs)
 METHOD_AND_READ=$(echo "$METHODS" | egrep 'access\$\d+00\(' | wc -l | xargs)
 WRITE=$(echo "$METHODS" | egrep 'access\$\d+02\(' | wc -l | xargs)
 PREINC=$(echo "$METHODS" | egrep 'access\$\d+04\(' | wc -l | xargs)
 PREDEC=$(echo "$METHODS" | egrep 'access\$\d+06\(' | wc -l | xargs)
 POSTINC=$(echo "$METHODS" | egrep 'access\$\d+08\(' | wc -l | xargs)
 POSTDEC=$(echo "$METHODS" | egrep 'access\$\d+10\(' | wc -l | xargs)
 OTHER=$(($ACCESSORS - $METHOD_AND_READ - $WRITE - $PREINC - $PREDEC - $POSTINC - $POSTDEC))
 
 NAME=$(basename $1)
 
 echo -e "$NAME\t$ACCESSORS\t$READ\t$WRITE\t$PREINC\t$PREDEC\t$POSTINC\t$POSTDEC\t$OTHER"
  53. $ column -t -s $'\t' \ <(echo -e "NAME\tTOTAL\tMETHOD/READ\tWRITE\tPREINC\tPREDEC\tPOSTINC\tPOSTDEC\tOTHER" \

    && find apks -type f | \ xargs -L1 ./accessors.sh | \ sort -k2,2nr) Warning: This may take a while to run!
  54. $ column -t -s $'\t' \ <(echo -e "NAME\tTOTAL\tMETHOD/READ\tWRITE\tPREINC\tPREDEC\tPOSTINC\tPOSTDEC\tOTHER" \

    && find apks -type f | \ xargs -L1 ./accessors.sh | \ sort -k2,2nr) NAME TOTAL METHOD/READ WRITE PREINC PREDEC POSTINC POSTDEC OTHER com.amazon.avod.thirdpartyclient 5545 4963 506 0 0 9 5 62 com.amazon.kindle 5245 4399 789 0 0 11 4 42 com.amazon.mShop.android 4774 4091 593 2 0 42 6 40 com.audible.application 4357 3782 526 0 0 7 1 41 com.waze 4269 3544 675 0 0 18 4 28 com.amazon.mShop.android.shopping 3587 2968 536 3 0 38 5 37 me.lyft.android 3330 3111 200 0 1 2 1 15 com.nhl.gc1112.free 2796 2446 316 1 2 9 5 17 com.etrade.mobilepro.activity 2611 2327 273 0 0 2 1 8 air.com.nbcuni.com.nbcsports.liveextra 2448 2120 305 0 0 14 2 7 com.untappdllc.app 2314 2000 247 1 0 6 0 60 com.paypal.android.p2pmobile 2066 1771 286 1 0 2 0 6 com.google.android.apps.giant 1967 1747 199 0 0 6 3 12 com.netflix.mediaclient 1963 1642 294 1 0 9 2 15 com.stackexchange.marvin 1842 1386 260 0 0 6 2 188 net.flixster.android 1836 1583 239 0 0 6 2 6 com.Slack 1801 1571 215 0 0 5 2 8 com.showtime.showtimeanytime 1727 1450 250 0 0 4 4 19 com.google.android.apps.cloudconsole 1643 1464 168 0 0 1 2 8 com.eventbrite.attendee 1529 1368 147 0 0 2 0 12 org.videolan.vlc 1358 1163 151 0 0 2 1 41 com.imdb.mobile 1353 897 92 1 0 3 1 359 com.robinhood.android 1239 1036 149 0 0 4 0 50 com.twitter.android 1088 957 117 0 0 5 0 9
  55. public class ItemsView {
 private static String displayText(String item) {


    return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  56. public class ItemsView {
 private static String displayText(String item) {


    return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  57. public class ItemsView {
 private static String displayText(String item) {


    return ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  58. public class ItemsView {
 static String displayText(String item) {
 return

    ""; // TODO
 }L
 
 private class ItemsAdapter {
 void bindItem(TextView tv, String item) {
 tv.setText(ItemsView.displayText(item));
 }H
 }G
 }"
  59. interface Callback<T> {
 void call(T value);
 } 
 
 


    class StringCallback implements Callback<String> {
 @Override public void call(String value) {
 System.out.println(value);
 }
 } // Callbacks.java
  60. $ javac Callbacks.java $ javap StringCallback class StringCallback implements Callback<java.lang.String>

    { StringCallback(); public void call(java.lang.String); public void call(java.lang.Object); } -c
  61. $ javap -c StringCallback class StringCallback implements Callback<java.lang.String> { StringCallback();

    Code: <removed> public void call(java.lang.String); Code: <removed> public void call(java.lang.Object); Code: 0: aload_0 1: aload_1 2: checkcast #4 // class java/lang/String 5: invokevirtual #5 // Method call:(Ljava/lang/String;)V 8: return }
  62. $ javap -c StringCallback class StringCallback implements Callback<java.lang.String> { StringCallback();

    Code: <removed> public void call(java.lang.String); Code: <removed> public void call(java.lang.Object); Code: 0: aload_0 1: aload_1 2: checkcast #4 // class java/lang/String 5: invokevirtual #5 // Method call:(Ljava/lang/String;)V 8: return }
  63. // Providers.java 
 
 
 
 
 
 class ViewProvider

    implements Provider<View> {
 @Override public View get(Context context) {
 return new View(context);
 }
 } 
 
 interface Provider<T> {
 T get(Context context);
 }
  64. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ Example.java $ javap -c ViewProvider

    class ViewProvider implements Provider<android.view.View> { ViewProvider(); Code: <removed> public android.view.View get(android.content.Context); Code: <removed> public java.lang.Object get(android.content.Context); Code: 0: aload_0 1: aload_1 2: invokevirtual #4 // Method get:(…)Landroid/view/View; 5: areturn }
  65. $ javac -bootclasspath android-sdk/platforms/android-24/android.jar \ Example.java $ javap -c ViewProvider

    class ViewProvider implements Provider<android.view.View> { ViewProvider(); Code: <removed> public android.view.View get(android.content.Context); Code: <removed> public java.lang.Object get(android.content.Context); Code: 0: aload_0 1: aload_1 2: invokevirtual #4 // Method get:(…)Landroid/view/View; 5: areturn }
  66. // Providers.java 
 
 
 
 
 
 class ViewProvider

    implements Provider<View> {
 @Override public View get(Context context) {
 return new View(context);
 }2
 }3 
 
 interface Provider<T> {
 T get(Context context);
 }
  67. class ViewProvider implements Provider<View> {
 @Override public View get(Context context)

    {
 return new View(context);
 }2
 }3 class TextViewProvider extends ViewProvider {
 @Override public TextView get(Context context) {
 return new TextView(context);
 }
 }
  68. class ViewProvider implements Provider<View> {
 @Override public View get(Context context)

    {
 return new View(context);
 }
 } class TextViewProvider extends ViewProvider {
 @Override public TextView get(Context context) {
 return new TextView(context);
 }
 }
  69. $ javap TextViewProvider class TextViewProvider extends ViewProvider { TextViewProvider(); public

    android.widget.TextView get(android.content.Context); public android.view.View get(android.content.Context); public java.lang.Object get(android.content.Context); }
  70. #!/usr/bin/python
 
 import os
 import subprocess
 import sys
 
 list

    = subprocess.check_output(["dex-method-list", sys.argv[1]])
 
 class_info_by_name = {}
 
 for item in list.split('\n'):
 first_space = item.find(' ')
 open_paren = item.find('(')
 close_paren = item.find(')')
 last_space = item.rfind(' ')
 
 class_name = item[0:first_space]
 method_name = item[first_space + 1:open_paren]
 params = [param for param in item[open_paren + 1:close_paren].split(', ') if len(param) > 0]
 return_type = item[last_space + 1:]
 if last_space < close_paren:
 return_type = 'void'
 
 # print class_name, method_name, params, return_type
 
 if class_name not in class_info_by_name:
 class_info_by_name[class_name] = {}
 class_info = class_info_by_name[class_name]
 
 if method_name not in class_info:
 class_info[method_name] = []
 method_info_by_name = class_info[method_name]
 
 method_info_by_name.append({
 'params': params,
 'return': return_type
 })
 
 count = 0
 for class_name, class_info in class_info_by_name.items():
 for method_name, method_info_by_name in class_info.items():
 for method_info in method_info_by_name:
 for other_method_info in method_info_by_name:
 if method_info == other_method_info:
 continue # Do not compare against self.
 params = method_info['params']
 other_params = other_method_info['params']
 if len(params) != len(other_params):
 continue # Do not compare different numbered parameter lists.
 
 match = True
 erased = False
 for idx, param in enumerate(params):
 other_param = other_params[idx]
 if param != 'Object' and not param[0].islower() and other_param == 'Object':
 erased = True
 elif param != other_param:
 match = False
 
 return_type = method_info['return']
 other_return_type = other_method_info['return']
 if return_type != 'Object' and other_return_type == 'Object':
 erased = True
 elif return_type != other_return_type:
 match = False
 
 if match and erased:
 count += 1
 # print "FOUND! %s %s %s %s" % (class_name, method_name, params, return_type)
 # print " %s %s %s %s" % (class_name, method_name, other_params, other_return_type)
 
 print os.path.basename(sys.argv[1]) + '\t' + str(count) erased.py
  71. $ column -t -s $'\t' \ <(echo -e "NAME\tERASED" \

    && find apks -type f | \ xargs -L1 ./erased.py | \ sort -k2,2nr) Warning: This may take a while to run!
  72. $ column -t -s $'\t' \ <(echo -e "NAME\tERASED" \

    && find apks -type f | \ xargs -L1 ./erased.py | \ sort -k2,2nr) NAME ERASED com.amazon.mShop.android 4387 me.lyft.android 3269 com.amazon.avod.thirdpartyclient 2564 com.amazon.kindle 2439 com.imdb.mobile 2375 com.amazon.mShop.android.shopping 2259 com.nhl.gc1112.free 2178 com.Slack 2041 com.google.android.apps.giant 1971 com.ubercab 1877 com.squareup.cash 1819 com.twitter.android 1777 air.com.nbcuni.com.nbcsports.liveextra 1415 com.google.android.apps.docs.editors.slides 1378 com.audible.application 1206 com.robinhood.android 1196 com.foursquare.robin 1165 com.joelapenna.foursquared 1095 com.untappdllc.app 1091 com.google.android.apps.cloudconsole 1040 com.eventbrite.attendee 897
  73. class Greeter {
 void sayHi() {
 System.out.println("Hi!");
 }1
 }2 


    
 
 
 class Example {
 public static void main(String... args) { Executor executor = Executors.newSingleThreadExecutor();
 Greeter greeter = new Greeter();
 }5
 }6
  74. class Greeter {
 void sayHi() {
 System.out.println("Hi!");
 }1
 }2 class

    Example {
 public static void main(String... args) { Executor executor = Executors.newSingleThreadExecutor();
 final Greeter greeter = new Greeter();
 executor.execute(new Runnable() {
 @Override public void run() {
 greeter.sayHi();
 }4
 });
 }5
 }6
  75. class Greeter {
 void sayHi() {
 System.out.println("Hi!");
 }1
 }2 class

    Example {
 public static void main(String... args) { Executor executor = Executors.newSingleThreadExecutor();
 Greeter greeter = new Greeter();
 executor.execute(() -> greeter.sayHi());
 }5
 }6
  76. class Greeter {
 void sayHi() {
 System.out.println("Hi!");
 }1
 }2 class

    Example {
 public static void main(String... args) { Executor executor = Executors.newSingleThreadExecutor();
 Greeter greeter = new Greeter();
 executor.execute(greeter::sayHi);
 }5
 }6
  77. $ javac *.java Retrolambda toolchain $ java -Dretrolambda.inputDir=. -Dretrolambda.classpath=. \


    -jar retrolambda.jar 
 $ dx --dex --output=example.dex *.class 
 $ dex-method-list example.dex
  78. $ javac *.java Retrolambda toolchain $ java -jar android-sdk/build-tools/24.0.1/jack.jar \


    -cp android-sdk/platforms/android-24/android.jar \
 --output-dex . *.java Jack toolchain $ java -Dretrolambda.inputDir=. -Dretrolambda.classpath=. \
 -jar retrolambda.jar 
 $ dx --dex --output=example.dex *.class 
 $ dex-method-list example.dex 
 
 $ dex-method-list classes.dex
  79. Code Retrolambda 2.1.0 Retrolambda 2.3.0 Jack 24.0.1 new Runnable() {


    @Override public void run() {
 greeter.sayHi();
 }
 } 2 2 2
  80. Example <init>() Example main(String[]) Example run(Runnable) Example$1 <init>(Greeter) Example$1 run()

    Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run()
  81. Example <init>() Example main(String[]) Example run(Runnable) Example$1 <init>(Greeter) Example$1 run()

    Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run()
  82. Example$1 <init>(Greeter) Example$1 run() $ javap -c 'Example$1' final class

    Example$1 implements java.lang.Runnable { Example$1(Greeter); Code: <removed> public void run(); Code: 0: aload_0 1: getfield #1 // Field val$greeter:LGreeter; 4: invokevirtual #3 // Method Greeter.sayHi:()V 7: return }
  83. Example$1 <init>(Greeter) Example$1 run() $ javap -c 'Example$1' final class

    Example$1 implements java.lang.Runnable { Example$1(Greeter); Code: <removed> public void run(); Code: 0: aload_0 1: getfield #1 // Field val$greeter:LGreeter; 4: invokevirtual #3 // Method Greeter.sayHi:()V 7: return }
  84. Code Retrolambda 2.1.0 Retrolambda 2.3.0 Jack 24.0.1 new Runnable() {


    @Override public void run() {
 greeter.sayHi();
 }
 } 2 2 2
  85. Code Retrolambda 2.1.0 Retrolambda 2.3.0 Jack 24.0.1 new Runnable() {


    @Override public void run() {
 greeter.sayHi();
 }
 } 2 2 2 () -> greeter.sayHi() 6 or 7 4 3
  86. Example <init>() Example lambda$main$0(Greeter) Example main(String[]) Example run(Runnable) Example$$Lambda$1 <init>(Greeter)

    Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1 run() Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run() Retrolambda
  87. Example <init>() Example lambda$main$0(Greeter) Example main(String[]) Example run(Runnable) Example$$Lambda$1 <init>(Greeter)

    Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1 run() Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run() Retrolambda
  88. Example lambda$main$0(Greeter) Example$$Lambda$1 <init>(Greeter) Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1 run()

    Retrolambda $ javap -c Example class Example { static void lambda$main$0(Greeter); Code: 0: aload_0 1: invokespecial #45 // Method sayHi:()V 4: return }
  89. Retrolambda Example lambda$main$0(Greeter) Example$$Lambda$1 <init>(Greeter) Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1

    run() $ javap -c Example class Example { static void lambda$main$0(Greeter); Code: 0: aload_0 1: invokespecial #45 // Method sayHi:()V 4: return }
  90. Example lambda$main$0(Greeter) Example$$Lambda$1 <init>(Greeter) Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1 run()

    Retrolambda $ javap -c 'Example$$Lambda$1' final class Example$$Lambda$1 implements java.lang.Runnable { public void run(); Code: 0: aload_0 1: getfield #15 // Field arg$1:LGreeter; 4: invokestatic #21 // Method Example.lambda$main$0: 7: return }
  91. Example lambda$main$0(Greeter) Example$$Lambda$1 <init>(Greeter) Example$$Lambda$1 lambdaFactory$(Greeter) → Runnable Example$$Lambda$1 run()

    Retrolambda $ javap -c 'Example$$Lambda$1' final class Example$$Lambda$1 implements java.lang.Runnable { public void run(); Code: 0: aload_0 1: getfield #15 // Field arg$1:LGreeter; 4: invokestatic #21 // Method Example.lambda$main$0: 7: return }
  92. Example -Example_lambda$1(Greeter) Example <init>() Example main(String[]) Example run(Runnable) Example$-void_main_java_lang_String__args_LambdaImpl0 <init>(Greeter)

    Example$-void_main_java_lang_String__args_LambdaImpl0 run() Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run() Jack
  93. Example -Example_lambda$1(Greeter) Example <init>() Example main(String[]) Example run(Runnable) Example$-void_main_java_lang_String__args_LambdaImpl0 <init>(Greeter)

    Example$-void_main_java_lang_String__args_LambdaImpl0 run() Greeter <init>() Greeter sayHi() java.io.PrintStream println(String) java.lang.Object <init>() java.lang.Runnable run() Jack
  94. Code Retrolambda 2.1.0 Retrolambda 2.3.0 Jack 24.0.1 new Runnable() {


    @Override public void run() {
 greeter.sayHi();
 }
 } 2 2 2 () -> greeter.sayHi() 6 or 7 4 3
  95. Code Retrolambda 2.1.0 Retrolambda 2.3.0 Jack 24.0.1 new Runnable() {


    @Override public void run() {
 greeter.sayHi();
 }
 } 2 2 2 () -> greeter.sayHi() 6 or 7 4 3 greeter::sayHi 4 3 or 4 3
  96. lambdas.sh #!/bin/bash
 set -e
 
 ALL=$(dex-method-list $1)
 
 RL=$(echo "$ALL"

    | \grep ' lambda\$' | wc -l | xargs)
 JACK=$(echo "$ALL" | \grep '_lambda\$' | wc -l | xargs)
 
 NAME=$(basename $1)
 
 echo -e "$NAME\t$RL\t$JACK"
  97. $ column -t -s $'\t' \ <(echo -e "NAME\tRETROLAMBDA\tJACK" \

    && find apks -type f | \ xargs -L1 ./lambdas.sh | \ sort -k2,2nr) Warning: This may take a while to run!
  98. $ column -t -s $'\t' \ <(echo -e "NAME\tRETROLAMBDA\tJACK" \

    && find apks -type f | \ xargs -L1 ./lambdas.sh | \ sort -k2,2nr) NAME RETROLAMBDA JACK com.squareup.cash 826 0 com.robinhood.android 680 0 com.imdb.mobile 306 0 com.stackexchange.marvin 174 0 com.eventbrite.attendee 53 0 com.untappdllc.app 53 0
  99. HashMap<K, V> HashSet<K, V> HashMap<Integer, V> HashMap<Integer, Boolean> HashMap<Integer, Integer>

    HashMap<Integer, Integer> HashMap<Integer, Long> HashMap<Long, V>
  100. HashMap<K, V> HashSet<K, V> HashMap<Integer, V> HashMap<Integer, Boolean> HashMap<Integer, Integer>

    HashMap<Integer, Integer> HashMap<Integer, Long> HashMap<Long, V> ArrayMap<K, V> ArraySet<V> SparseArray<V> SparseBooleanArray SparseIntArray SparseLongArray LongSparseArray<V>
  101. HashMap<K,V> Node<K,V>[] table; int size; ... Node<K,V> int hash;
 K

    key;
 V value;
 Node<K,V> next; Node<K,V> int hash;
 K key;
 V value;
 Node<K,V> next;
  102. $ java -jar jol-cli-0.5-full.jar internals java.util.HashMap # Running 64-bit HotSpot

    VM. # Objects are 8 bytes aligned. java.util.HashMap object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 4 4 (object header) 00 00 00 00 8 4 (object header) 9f 37 00 f8 12 4 Set AbstractMap.keySet null 16 4 Collection AbstractMap.values null 20 4 int HashMap.size 0 24 4 int HashMap.modCount 0 28 4 int HashMap.threshold 0 32 4 float HashMap.loadFactor 0.75 36 4 Node[] HashMap.table null 40 4 Set HashMap.entrySet null 44 4 (loss due to the next object alignment) Instance size: 48 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  103. $ java -jar jol-cli-0.5-full.jar internals java.util.HashMap # Running 64-bit HotSpot

    VM. # Objects are 8 bytes aligned. java.util.HashMap object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 4 4 (object header) 00 00 00 00 8 4 (object header) 9f 37 00 f8 12 4 Set AbstractMap.keySet null 16 4 Collection AbstractMap.values null 20 4 int HashMap.size 0 24 4 int HashMap.modCount 0 28 4 int HashMap.threshold 0 32 4 float HashMap.loadFactor 0.75 36 4 Node[] HashMap.table null 40 4 Set HashMap.entrySet null 44 4 (loss due to the next object alignment) Instance size: 48 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  104. $ java -jar jol-cli-0.5-full.jar internals 'java.util.HashMap$Node' # Running 64-bit HotSpot

    VM. # Objects are 8 bytes aligned. java.util.HashMap$Node object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Node.hash N/A 16 4 Object Node.key N/A 20 4 Object Node.value N/A 24 4 Node Node.next N/A 28 4 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  105. $ java -jar jol-cli-0.5-full.jar internals 'java.util.HashMap$Node' # Running 64-bit HotSpot

    VM. # Objects are 8 bytes aligned. java.util.HashMap$Node object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Node.hash N/A 16 4 Object Node.key N/A 20 4 Object Node.value N/A 24 4 Node Node.next N/A 28 4 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
  106. HashMap<K,V> Node<K,V>[] table; int size; Node<K,V> int hash;
 K key;


    V value;
 Node<K,V> next; 48 + 32 * entries HashMap object Node objects
  107. HashMap<K,V> Node<K,V>[] table; int size; Node<K,V> int hash;
 K key;


    V value;
 Node<K,V> next; 48 + 32 * entries + 4 * (entries / loadFactor) + 8 HashMap object array of Node in HashMap Node objects
  108. $ javac SparseArray.java $ java -cp .:jol-cli-0.5-full.jar org.openjdk.jol.Main \
 internals

    android.util.SparseArray # Running 64-bit HotSpot VM. # Objects are 8 bytes aligned. android.util.SparseArray object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 4 4 (object header) 00 00 00 00 8 4 (object header) 1a 69 01 f8 12 4 int SparseArray.mSize 0 16 1 boolean SparseArray.mGarbage false 17 3 (alignment/padding gap) N/A 20 4 int[] SparseArray.mKeys [0, 0, 0, 0, 0, 0, …] 24 4 Object[] SparseArray.mValues [null, null, null, …] 28 4 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
  109. $ javac SparseArray.java $ java -cp .:jol-cli-0.5-full.jar org.openjdk.jol.Main \
 internals

    android.util.SparseArray # Running 64-bit HotSpot VM. # Objects are 8 bytes aligned. android.util.SparseArray object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 4 4 (object header) 00 00 00 00 8 4 (object header) 1a 69 01 f8 12 4 int SparseArray.mSize 0 16 1 boolean SparseArray.mGarbage false 17 3 (alignment/padding gap) N/A 20 4 int[] SparseArray.mKeys [0, 0, 0, 0, 0, 0, …] 24 4 Object[] SparseArray.mValues [null, null, null, …] 28 4 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
  110. SparseArray<V> int[] keys; V[] values; int size; 32 + 4

    * entries SparseArray object array of int keys
  111. SparseArray<V> int[] keys; V[] values; int size; 32 + 4

    * entries + 4 * entries SparseArray object array of int keys array of V values
  112. SparseArray<V> int[] keys; V[] values; int size; 32 + (4

    * entries + 4 * entries) / 0.75 SparseArray object array of int keys array of V values binary tree fudge factor
  113. SparseArray<V> 32 + (4 * entries + 4 * entries)

    / 0.75 HashMap<Integer, V> 48 + 32 * entries + 4 * (entries / loadFactor) + 8
  114. SparseArray<V> 32 + (4 * entries + 4 * entries)

    / 0.75 HashMap<Integer, V> 48 + 32 * entries + 4 * (entries / 0.75) + 8
  115. SparseArray<V> 32 + (4 * 50 + 4 * 50)

    / 0.75 HashMap<Integer, V> 48 + 32 * 50 + 4 * (50 / 0.75) + 8
  116. SparseArray<V> 32 + (4 * 50 + 4 * 50)

    / 0.75 = 656 HashMap<Integer, V> 48 + 32 * 50 + 4 * (50 / 0.75) + 8 = 1922
  117. TODO (for you!) • Turn on "private member" inspection in

    the IDE and listen to it. • Report synthetic accessor methods in libraries as bugs.
  118. TODO (for you!) • Turn on "private member" inspection in

    the IDE and listen to it. • Report synthetic accessor methods in libraries as bugs. • Update Retrolambda to 2.3.0 and try not to use it in open source libraries.
  119. TODO (for you!) • Turn on "private member" inspection in

    the IDE and listen to it. • Report synthetic accessor methods in libraries as bugs. • Update Retrolambda to 2.3.0 and try not to use it in open source libraries. • Try using the Jack compiler in your app. Report failures to b.android.com.
  120. TODO (for you!) • Turn on "private member" inspection in

    the IDE and listen to it. • Report synthetic accessor methods in libraries as bugs. • Update Retrolambda to 2.3.0 and try not to use it in open source libraries. • Try using the Jack compiler in your app. Report failures to b.android.com. • Use ProGuard without overly-matchy rules (-keep class com.foo.**).
  121. Links • Jack generates extra method for method references
 http://b.android.com/218301

    • Retrolambda generates extra method for lambdas / method references
 https://github.com/orfjackal/retrolambda/issues/81 • dex-method-list tool for showing methods in class/jar/aar/dex/apk
 https://github.com/JakeWharton/dex-method-list • Java Object Layout ("jol") tool for showing memory cost of types
 http://openjdk.java.net/projects/code-tools/jol/ • accessors.sh, erased.py, lambdas.sh helper scripts
 http://jakes.link/exploring-scripts