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

Decoding Android Runtime

Jitin
September 19, 2019

Decoding Android Runtime

While writing code, we mostly care about that it should be efficient and should not break. How that code executes at runtime is a completely different story in case of Android. The .dex format has been here for years and everyone is accustomed to it. But there are a lot of complexities going on once the .dex reaches your device. This talk will focussed on analyzing on what happens after your code in Kotlin/Java is compiled, packaged and run on an actual device. We will take a deep dive into .dex format as well as see how runtime has evolved since Android 1.0 and taken a great leap with Android 5.0 and above. In the process, we will also learn how to write runtime efficient code.

Jitin

September 19, 2019
Tweet

More Decks by Jitin

Other Decks in Technology

Transcript

  1. JITIN S HARMA D E C O D I N

    G A N D R O I D R U N T I M E @_jitinsharma jitinsharma.in
  2. @_jitinsharma / jitinsharma.in Zygote - Zygote Process acts a base

    process from which all processes are forked for subsequent applications. - This allows Android to run in a Multi-Process environment where all processes run independent of each other. - All resources preloaded by Zygote are accessed by subsequent applications through shared memory.
  3. @_jitinsharma / jitinsharma.in Zygote System Server Load core libraries and

    resources Starts Activity Manager Notification Manager, Bluetooth Service, Package Manager, Sensor Service…
  4. @_jitinsharma / jitinsharma.in Manifest Parsing - All data from AndroidManifest.xml

    is read and is written to data/system/packages.xml - This file acts as a global source for all packages and is used by PackageManager API - This happens only once either when device is booted or an application is being installed.
  5. @_jitinsharma / jitinsharma.in Manifest Parsing - Identifies various elements within

    your application - At the same time manifest performs checks which can cause installation errors if elements are not defined properly.
  6. @_jitinsharma / jitinsharma.in .dex Dalvik VM A register based VM

    optimised for memory constrained device .dex -> Dalvik Executable
  7. @_jitinsharma / jitinsharma.in Stack Machines Register Machines value1 value2 value3

    value4 value1 value2 operation pop pop value5 value3 value4 push addr1 value1 operation addr2 value2 addr3 value3
  8. @_jitinsharma / jitinsharma.in Stack Machines Register Machines value1 value2 value3

    value4 value1 value2 operation pop pop value5 value3 value4 push addr1 value1 operation addr2 value2 addr3 value3 ARM Optimized
  9. @_jitinsharma / jitinsharma.in dex - Uses 16 bit instruction format.

    - Bytecode tends to be larger due to register based architecture for storing addresses
  10. @_jitinsharma / jitinsharma.in dex - Uses 16 bit instruction format.

    - Bytecode tends to be larger due to register based architecture for storing addresses ‣header ‣string_ids ‣type_ids ‣proto_ids ‣field_ids ‣method_ids ‣class_defs ‣call_site_ids ‣method_handles ‣data ‣link_data
  11. @_jitinsharma / jitinsharma.in dex - Uses 16 bit instruction format.

    - Bytecode tends to be larger due to register based architecture for storing addresses ‣header ‣string_ids ‣type_ids ‣proto_ids ‣field_ids ‣method_ids ‣class_defs ‣call_site_ids ‣method_handles ‣data ‣link_data Contains verification signatures
  12. @_jitinsharma / jitinsharma.in public class Data { int id; String

    name = ""; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
  13. @_jitinsharma / jitinsharma.in public int getId() { return id; }

    .METHOD getId : int .MODIFIERS public .REGISTERS 2 .CODE iget v0, v1 field@8046 return v0 .METHOD setName : void .PARAM java.lang.String .MODIFIERS public .REGISTERS 2 .CODE iput-object v1, v0 field@8047 return-void public void setName(String name) { this.name = name; }
  14. @_jitinsharma / jitinsharma.in private fun forEachTest(): Int { var sum

    = 0 (0..50).forEach { sum += it } return sum } private fun forTest(): Int { var sum = 0 for (i in 0 until 50) { sum += i } return sum } VS
  15. @_jitinsharma / jitinsharma.in private fun forTest(): Int { var sum

    = 0 for (i in 0 until 50) { sum += i } return sum } .METHOD forTest : int .MODIFIERS private final .REGISTERS 4 .CODE const/4 v0, #0 const/4 v1, #0 const/16 v2, #50 if-ge v1, v2, 6 add-int/2addr v1, v0 add-int/lit8 v1, v1, #1 goto -7 return v0
  16. @_jitinsharma / jitinsharma.in private fun forEachTest(): Int { var sum

    = 0 (0..50).forEach { sum += it } return sum } .METHOD forEachTest : int .MODIFIERS private final .REGISTERS 8 .CODE const/4 v0, #0 new-instance v1, type@2439 const/4 v2, #0 const/16 v3, #50 invoke-direct {v1, v2, v3}, meth@20280 check-cast v1, type@1851 move v3, v2 invoke-interface {v1}, meth@15176 move-result-object v4 invoke-interface {v4}, meth@15640 move-result v5 if-eqz v5, 13 move-object v5, v4 check-cast v5, type@2142 invoke-virtual {v5}, meth@18564 move-result v5 move v6, v5 add-int/2addr v6, v0 goto -16 return v0
  17. @_jitinsharma / jitinsharma.in A.class B.class Z.class classes.dex - Reduction in

    bytecode size during conversion. - Duplicate constants from class files are converted to a single one. - Each method needs have a reference which is limited by dex instruction format of 16 bits, hence total method references which a single dex file can hold is 65,536 (2^16)
  18. @_jitinsharma / jitinsharma.in dexopt -Verification and Optimization of contents of

    .dex file -Result differ from device to device. -Starts a small version of VM to optimize dex files. -Contents of system/framework/ can be used to optimize dex files outside Android OS. -Code is optimized for runtime not memory.
  19. @_jitinsharma / jitinsharma.in Multidex.install(this) classes.dex classes2.dex classes3.dex …. System Application

    Must have Application class Library dependencies classesN.dex Inject values into ClassLoader
  20. @_jitinsharma / jitinsharma.in Multidex.install(this) classes.dex classes2.dex classes3.dex …. System Application

    Must have Application class Library dependencies (multiDexKeepFile) classesN.dex Inject values into ClassLoader
  21. @_jitinsharma / jitinsharma.in DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader(dexFile.getAbsolutePath(), getCodeCacheDir().getAbsolutePath(),

    null, context.getClassLoader()); Class<?> dynamicClass = dexClassLoader.loadClass("com.example.myapplication.runtime"); Method dynamicMethod = dynamicClass.getDeclaredMethod("executeDataLoad", Integer.class, Integer.class); Integer value = (Integer) dynamicMethod.invoke(dynamicClass.newInstance(), 1, 2); Network
  22. @_jitinsharma / jitinsharma.in DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader(dexFile.getAbsolutePath(), getCodeCacheDir().getAbsolutePath(),

    null, context.getClassLoader()); Class<?> dynamicClass = dexClassLoader.loadClass("com.example.myapplication.runtime"); Method dynamicMethod = dynamicClass.getDeclaredMethod("executeDataLoad", Integer.class, Integer.class); Integer value = (Integer) dynamicMethod.invoke(dynamicClass.newInstance(), 1, 2); Network public Integer executeDataLoad(Integer value1, Integer value2) { // return result; }
  23. @_jitinsharma / jitinsharma.in DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader(dexFile.getAbsolutePath(), getCodeCacheDir().getAbsolutePath(),

    null, context.getClassLoader()); Class<?> dynamicClass = dexClassLoader.loadClass("com.example.myapplication.runtime"); Method dynamicMethod = dynamicClass.getDeclaredMethod("executeDataLoad", Integer.class, Integer.class); Integer value = (Integer) dynamicMethod.invoke(dynamicClass.newInstance(), 1, 2); Network public Integer executeDataLoad(Integer value1, Integer value2) { // return result; }
  24. @_jitinsharma / jitinsharma.in JIT private void handleIntent() { ... public

    RequestQueue getRequestQueue() { if (requestQueue == null) { requestQueue = Volley.newRequestQueue(this); } return requestQueue; } for (String s : networkRequestMap.keySet()) { if (BuildConfig.DEBUG) { //
  25. @_jitinsharma / jitinsharma.in Revamped GC -ART keeps track of application

    going in non-UI state. -Seperate types of GC for low memory and non-low memory devices. -GC pauses reduced from 2 to 1. -Larger objects like bitmap have seperate heap for faster allocation. -Young Objects are moved to stack for faster reclamation of memory.
  26. @_jitinsharma / jitinsharma.in AOT Compilation classes.dex classes2.dex classesN.dex .oat dex2oat

    - AOT works on principle of compile once and then run. - Compilation takes significant time and memory since whole bytecode is converted to machine code at installation. - AOT compilers do not benefit from profile guided compilation which JIT compilers have instead use a restrictive approach to do compilation. Android Runtime VM
  27. @_jitinsharma / jitinsharma.in DEX CODE: 0x0000: 5210 e10f | iget

    v0, v1, I com.example.myapplication.Data.id // field@4065 0x0002: 0f00 | return v0 public int getId() { return id; } Java
  28. @_jitinsharma / jitinsharma.in CODE: (code_offset=0x00d6a3fc size_offset=0x00d6a3f8 size=68)... 0x00d6a3fc: 4885842400E0FFFF testq

    rax, [rsp + -8192] suspend point dex PC: 0x0000 GC map objects: v1 ([sp + #40]) 0x00d6a404: 4883EC18 subq rsp, 24 0x00d6a408: 48893C24 movq [rsp], rdi 0x00d6a40c: 89742428 mov [rsp + 40], esi 0x00d6a410: 6566833C250000000000 cmpw gs:[0], 0 ; state_and_flags 0x00d6a41a: 0F8516000000 jnz/ne +22 (0x00d6a436) 0x00d6a420: 8B442428 mov eax, [rsp + 40] 0x00d6a424: 8500 test eax, [rax] suspend point dex PC: 0x0000 GC map objects: v1 ([sp + #40]) 0x00d6a426: 8B480C mov ecx, [rax + 12] suspend point dex PC: 0x0000 GC map objects: v1 ([sp + #40]) 0x00d6a429: 894C2410 mov [rsp + 16], ecx 0x00d6a42d: 8B442410 mov eax, [rsp + 16] 0x00d6a431: 4883C418 addq rsp, 24 0x00d6a435: C3 ret 0x00d6a436: 65FF142538040000 call gs:[1080] ; pTestSuspend ……..
  29. @_jitinsharma / jitinsharma.in JIT/AOT Hybrid ‣System boot after OS update

    will no longer optimise the app, a job solely performed by JIT. ‣dex2oat is still used for .oat creation but it uses profile data generated by JIT to do so. ‣Code may be executed by directed .dex interpretation or JIT cache or AOT compiled. ‣At any time JIT compiled data will be more efficient because of availability of better environment data than AOT compiled code, and the same is preferred if both are present.
  30. @_jitinsharma / jitinsharma.in DEX CODE: 0x0000: 5210 e10f | iget

    v0, v1, I com.example.myapplication.Data.id // field@4065 0x0002: 0f00 | return v0 public int getId() { return id; } Java CODE: (code_offset=0x00000000 size_offset=0x00aea110 size=0) NO CODE!