CodeFest 2019. Никита Липский (Excelsior) — Невыносимая легкость AOT-компиляции Spring приложений

CodeFest 2019. Никита Липский (Excelsior) — Невыносимая легкость AOT-компиляции Spring приложений

Spring framework использует, пожалуй, весь набор динамических свойств Java: Spring Boot приложения грузятся своим загрузчиком классов, повсеместно используются reflection, порождение и загрузка Java байт-кода на лету и т.д.

С другой стороны, в мире микросервисов набирает популярность статическая AOT компиляция Java приложений, для решения проблем быстрого старта, предсказуемой производительности, мгновенного достижения пиковой производительности. Становится интересно: может ли динамичность Spring framework ужиться со статической компиляцией?

Ответ — да, может. В этом докладе я на примере поддержки Spring Boot приложений в AOT-центричной JVM Excelsior JET покажу, как динамические свойства Java, используемые в Spring framework, могут работать совместно с AOT-порожденным машинным кодом.

16b6c87229eaf58768d25ed7b2bbbf52?s=128

CodeFest

April 06, 2019
Tweet

Transcript

  1. Невыносимая легкость AOT компиляции Spring Boot приложений Никита Липский Excelsior

  2. Spring @Autowired Serviсe service;

  3. Spring @Autowired Serviсe service;

  4. Spring Boot <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>

  5. Spring Boot <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>

  6. Spring Boot <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> … и у вас

    полетела Kafka.
  7. Магия

  8. Магия А что если не работает?

  9. Никита Липский ? •  Инициатор проекта Excelsior JET –  работал

    над проектом более 18 лет –  как идейный вдохновитель –  компиляторный инженер –  руководитель –  и много в каких еще ролях •  twitter: @pjBooms •  team blog: https://www.excelsiorjet.com/blog 9
  10. Excelsior JET? •  Полная реализация Java SE –  c 2005

    года cертифицирована как Java Compatible •  AOT compiler + Java Runtime –  смешанная компиляция: AOT + JIT –  поддержка нестандартных загрузчиков классов в AOT режиме (для Eclipse RCP, Tomcat) •  Toolkit –  Startup Optimizer –  Deployment 10
  11. Никита Липский ? •  Инициатор проекта Excelsior JET –  работал

    над проектом более 18 лет –  как идейный вдохновитель –  компиляторный инженер –  руководитель –  и много в каких еще ролях •  twitter: @pjBooms •  team blog: https://www.excelsiorjet.com/blog 11
  12. Секретная биография ? •  1.5 года руководства разработкой интернет сервиса

    –  Spring (MVC, Security, AOP, etc.) –  JPA (EclipseLink) –  MySQL –  Liquibase –  ELK –  Angular –  Amazon EC2, EBS 12
  13. План доклада •  Spring –  context, configuration, classpath scanning, bean

    definitions processing, DI •  Spring Boot –  auto configuration, @SpringBootApplication, Spring Boot executable archive •  Как Spring работает на уровне JVM –  reflection, dynamic proxies •  Как Spring AOT компилируется –  почему все продолжает работать •  Бенчмарки на производительность, стартап
  14. Что делает JVM на старте 14

  15. Application run on JVM Execution time Performance

  16. Native application Performance Execution time

  17. JVM with AOT 17

  18. None
  19. None
  20. https://spring.io/blog/2018/10/02/the-evolution-of-spring-fu

  21. None
  22. None
  23. None
  24. Spring Application Context @Component class MyComponent @Service class MyService @Configuration

    class MyConfig { @Bean MyBean myBean(){ new MyBean(); } } Context
  25. Classpath Scanning @ComponentScan(basePackages = "com.foo.myPackage") MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar

    spring-web.jar
  26. Classpath Scanning MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar Context

  27. Classpath Scanning Context MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar

  28. MyClasses spring-kafka.jar spring-data.jar Classpath Scanning MyBean Context spring-mvc.jar spring-core.jar spring-web.jar

  29. Context Classpath Scanning MyBean MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar

  30. Context Classpath Scanning MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar MyBean

  31. MyClasses spring-kafka.jar spring-data.jar Context Classpath Scanning KafkaTemplate spring-mvc.jar spring-core.jar spring-web.jar

    MyBean
  32. Context Classpath Scanning KafkaTemplate MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar

    MyBean
  33. Context Classpath Scanning MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar spring-web.jar KafkaTemplate

    MyBean
  34. Bean Definition Processing Bean Def1 Bean Def2 Bean Def3 Bean

    Def4 Bean Def5
  35. @Autowired Object postProcess(Object bean, String beanName) { Field[] fields =

    bean.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); ReflectionUtils.setField(field, bean, cxt.getBean(field.getType()); } } return bean; }
  36. None
  37. @Configuration @Configuration class MyConfig { @Bean MyBean1 myBean1() { new

    MyBean1(); } @Bean MyBean2 myBean2() { new MyBean2(myBean1()); } }
  38. Auto @Configuration @Configuration // Some conditions class MyAutoConfiguration { //

    Auto-configured beans @Configuration @ConditionalOnClass(EmbeddedAcmeService.class) static class EmbeddedConfiguration { @Bean @ConditionalOnMissingBean public EmbeddedAcmeService embeddedAcmeService() { ... } } }
  39. @SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan @SpringBootApplication public class

    Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
  40. Spring Boot Executable архив +---org | \---springframework | \---boot |

    \---loader | | JarLauncher.class | | LaunchedURLClassLoader.class | +---jar | | JarFile.class | \---BOOT-INF +---classes | \—example | Example.class | +---lib | spring-core.jar, spring-boot.jar, spring-web.jar
  41. Spring Boot Executable архив •  LaunchedURLClassLoader грузит классы из BOOT-INF/classes,

    BOOT-INF/lib/*
  42. Spring Boot Executable архив •  LaunchedURLClassLoader грузит классы из BOOT-INF/classes,

    BOOT-INF/lib/* •  Доступ к классам жаров из BOOT-INF/lib осуществляется через свою реализацию JarFile
  43. Spring Boot •  Все такое динамичное: –  Везде reflection – 

    Разбор аннотаций –  Генерация и загрузка proxy классов –  Рантайм связывание Как такой AOT?
  44. Spring Boot •  Все такое динамичное: –  Везде reflection – 

    Разбор аннотаций –  Генерация и загрузка proxy классов –  Рантайм связывание Как такой AOT?
  45. Reflection

  46. Reflection Class beanClass = classIsKnown? Class.forName("MyBean"): //we have bean instance

    bean.getClass();
  47. Reflection class.isAnnotationPresent(Service.class); Field[] flds = class.getDeclaredFields(); Constructor[] constrs = class.getDeclaredConstructors();

    Method[] methods = class.getDeclaredMethods();
  48. Reflection field.set(bean, cxt.getBean("beanName")); Parameter[] cparams = constr.getParameters(); //if cparams.length ==

    1 constr.newInstance( cxt.getBean(cparams[0].getName())); method.invoke(bean, cxt.getBean(mparams[0].getName()));
  49. Reflection //from java/lang/Class.java private native Field[] getDeclaredFields0(boolean publicOnly); private native

    Method[] getDeclaredMethods0(boolean publicOnly); private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
  50. JVM OS + CPU Monitoring AOT Class files Classloading engine

    Bytecode Verification Execution engine: interpreter, JIT Threading Synchronization Meta information Memory management, Garbage Collection Native methods
  51. JVM AOT Class files Classloading engine Reflection JNI Metaspace Class1

    Class2 Class3 Class4 Class5 Class6
  52. JVM AOT Class files Classloading engine Reflection JNI Metaspace Class1

    Class2 Class3 Class4 Class5 Class6
  53. JVM AOT Class files Classloading engine Reflection JNI Metaspace Class1

    Class2 Class3 Class4 Class5 Class6
  54. JVM AOT Class files Classloading engine Reflection JNI Metaspace Class1

    Class2 Class3 Class4 Class5 Class6
  55. JVM AOT Class files Classloading engine Reflection JNI Metaspace Class1

    Class2 Class3 Class4 Class5 Class6
  56. JVM •  JVM при загрузке классов строит их рантайм представление

    и складывает результат в Metaspace •  Reflection реализован через прямой доступ к Metaspace
  57. JVM +AOT AOT Class files Classloading engine Reflection JNI Metaspace

    Class1 Class2 Class3 Class4 Class5 Class6
  58. JVM +AOT AOT Class files Classloading engine Reflection JNI Metaspace

    Class1 Class2 Class3 Class4 Class5 Class6
  59. JVM +AOT AOT Class files Classloading engine Reflection JNI Metaspace

    Class1 Class2 Class3 Class4 Class5 Class6
  60. JVM +AOT AOT Class files Classloading engine Reflection JNI Metaspace

    Class1 Class2 Class3 Class4 Class5 Class6
  61. JVM +AOT AOT Class files Classloading engine Reflection JNI Metaspace

    Class1 Class2 Class3 Class4 Class5 Class6
  62. JVM + AOT •  В JVM с AOT компилятором Reflection

    работает практически также как в обычной JVM за тем лишь исключением, что рантайм представление класса строится AOT компилятором до исполнения
  63. Java application Classes Jar1 Jar2 AOT compiler Executable Meta data

    Resources Native code
  64. Class Meta data name, modifiers super class, interfaces methods, fields

    annotations Java bytecode
  65. Executable Meta data Native code Resources

  66. Class Meta data Java bytecode

  67. Class Meta data Java bytecode Executable Meta data Native code

    Resources
  68. Class Meta data Java bytecode Executable Meta data Native code

    Resources AOT
  69. Jar Classes Resources

  70. Jar Classes Resources

  71. Jar Classes Resources Executable Meta data Native code Resources

  72. Jar Classes Resources Executable Meta data Native code Resources AOT

  73. Jar Classes Resources Executable Meta data Native code Resources AOT

  74. Executable Meta data Native code Resources

  75. Executable Meta data Native code Resources Java Runtime JDK classes

    (native code) JVM
  76. Executable Meta data Native code Resources Java Runtime JDK classes

    (native code) JVM
  77. Executable Meta data Native code Resources Java Runtime JDK classes

    (native code) JVM OS+CPU
  78. Java Runtime JDK classes JVM Metaspace C1 C2 C3 C4

    C5 C6 Reflection Executable Meta data Native code GC JIT Threading
  79. Classpath

  80. Classpath java –cp dep1.jar;dep2.jar Main

  81. Classpath java –cp dep1.jar;dep2.jar Main MyClasses spring-kafka.jar spring-data.jar spring-mvc.jar spring-core.jar

    spring-web.jar
  82. Classpath getClass().getClassloader().getPath() - набор URL

  83. URL Java file OS File

  84. Classpath getClass().getClassloader().getPath() - набор URL Что получит AOT-скомпилированный код?

  85. Java application Jar1 Jar2 Jar3

  86. Java application Jar1 Jar2 Jar3 Executable Meta data Native code

    Resources
  87. Java application Jar1 Jar2 Jar3 Executable Meta data Native code

    Resources Jar1 Jar2 Jar3
  88. Java Runtime Java File System OS OS File System

  89. Java Runtime Java File System OS OS File System

  90. Java Runtime Java File System OS OS File System Executable

    Meta data Native code Embedded File System Jar1 Jar2 Jar3
  91. Classpath •  Jars внутри Exe формируют свою (виртуальную) файловую систему

    и монтируются к Java файловой системе вместе с OS файловой системой.
  92. Classpath •  URL из classpath ccылаются на эти виртуальные файлы.

    •  getResource() загрузчика через java.util.zip находит ресурсы внутри jars зашитых в executable.
  93. Classpath Но в jars лежат не только ресурсы, но и

    классы.
  94. Classpath Но в jars лежат не только ресурсы, но и

    классы. Они тоже попадают в Exe?
  95. Synthetic Class Files

  96. Synthetic Class Files Проблема: спрингу нужны классы приложения во время

    исполнения. Решение: порождать их из метаинформации
  97. Synthetic Class Files Проблема: спрингу нужны классы приложения во время

    исполнения. Решение: порождать их из метаинформации
  98. Class Meta data Java bytecode Executable Meta data Native code

    Resources AOT
  99. Jar Classes Resources Executable Meta data Native code Resources AOT

  100. Executable Meta data Resources getResource(“MyClass.class”) Jar MyClass.class MyClass

  101. Executable Meta data Resources getResource(“MyClass.class”) Jar MyClass.class MyClass

  102. Executable Meta data Resources getResource(“MyClass.class”) .getInputStream() Jar MyClass.class MyClass

  103. Executable Meta data Resources getResource(“MyClass.class”) .getInputStream() Jar MyClass.class MyClass JVM

  104. Executable Meta data Resources getResource(“MyClass.class”) .getInputStream() Jar MyClass.class MyClass JVM

  105. Executable Meta data Resources getResource(“MyClass.class”) .getInputStream() Jar MyClass.class MyClass JVM

  106. Executable Meta data Resources getResource(“MyClass.class”) .getInputStream() Jar MyClass.class MyClass JVM

    Synthetic MyClass.class
  107. AOT + JIT

  108. AOT + JIT Spring порождает классы: •  Spring AOP – Порождение

    Proxy класса, который перед выполнением кода проксируемого класса исполняет код аспекта (пример @Transactional)
  109. AOT + JIT Spring порождает классы: •  Для @Configuration классов

    – порождение наследника переопределяющего @Bean методы
  110. AOT + JIT Вопрос: как загружаются классы порожденные Spring в

    случае AOT? Ответ: c помощью JIT компилятора (линковка с AOT кодом через metaspace)
  111. Executable Meta data Native code Resources Java Runtime JDK classes

    JVM OS+CPU
  112. Executable Meta data Native code Resources Java Runtime JDK classes

    OS+CPU getProxy() JVM
  113. Executable Meta data Native code Resources Java Runtime JDK classes

    OS+CPU DynProxy Classes getProxy() JVM
  114. Executable Meta data Native code Resources Java Runtime JDK classes

    JVM OS+CPU DynProxy JITed code Classes JIT getProxy()
  115. AOT + JIT Вопрос: как загружаются классы порожденные Spring в

    случае AOT? Ответ: c помощью JIT компилятора (линковка с AOT кодом через metaspace)
  116. AOT + JIT Вопрос: как загружаются классы порожденные Spring в

    случае AOT? Ответ: c помощью JIT компилятора (линковка с AOT кодом через metaspace)
  117. Custom Classloaders и AOT

  118. Нестандартные загрузчики классов •  Переопределяют умолчательную логику разрешений ссылок между

    классами •  Уникальное пространство имен •  Позволяют управлять зависимостями •  В случае Spring Boot позволяют прятать зависимости внутрь Spring Boot executable jar 118
  119. Нестандартные загрузчики классов Как компилировать статически? •  Компилировать каждый класс

    изолировано от других –  Плохо для производительности •  Изучить логику разрешения ссылок для популярных загрузчиков 119
  120. Нестандартные загрузчики классов Схема поддержки загрузчиков в AOT: •  Во

    время AOT компиляции: –  Разрешение ссылок между классами согласно логики загрузчика •  Во время исполнения: –  Назначение каждому предкомпилированному классу соответсвующего инстанса загрузчика Работает для Eclipse RCP и Tomcat. В Spring Boot один загрузчик – все много проще. 120
  121. Java application Jar1 Jar2 Jar3 Executable Native code Resources Jar1

    Jar2 Jar3 Java Runtime JDK classes JVM Reflection GC JIT Meta data
  122. Промежуточный итог Хоть все и не просто, но это все

    реально работает! Вопрос, но как быстро?
  123. Промежуточный итог Хоть все и не просто, но это все

    реально работает! Вопрос, но как быстро?
  124. Производительность 124

  125. Spring Boot Startup

  126. Spring Boot Startup •  Dave Syer бенчмарк (https://github.com/dsyer/spring-boot-startup-bench) –  configserver

    – launched on Spring Boot 1.4 and 1.5 framework versions –  demo application – launched on Spring Boot 1.4, 1.5 and 2.0 versions –  minimal – launched on Spring Boot 1.5. Disabled auto configuration –  Petclinic – classic Petclinic sample launched on Spring Boot 1.5.9. –  Petclinic-latest – the same app launched on the latest Spring Boot (2.1.0)
  127. TL;DR How do I make my app go faster? https://spring.io/blog/2018/12/12/how-fast-is-spring

  128. Spring Boot Startup 0% 20% 40% 60% 80% 100% 120%

    ConfigServer14x ConfigServer15x Minimal Petclinic Petclinic21x SpringBoot14x SpringBoot15x SpringBoot20x Average Fat jar HotSpot Fat jar JET Exploded HotSpot Exploded JET
  129. Spring Boot Startup Вопрос: Как синтетические класс файлы влияют на

    стартап. Ответ: Ускоряют (незначительно) Причина: Синтетические классы ~ в два раза меньше оригинальных – время на их синтез меньше, чем издержки на дальнейшее чтение
  130. Spring Boot Startup Вопрос: Как синтетические класс файлы влияют на

    стартап. Ответ: Ускоряют (незначительно) Причина: Синтетические классы ~ в два раза меньше оригинальных – время на их синтез меньше, чем издержки на дальнейшее чтение
  131. Spring Boot Startup Вопрос: Как синтетические класс файлы влияют на

    стартап. Ответ: Ускоряют (незначительно) Причина: Синтетические классы ~ в два раза меньше оригинальных – время на их синтез меньше, чем издержки на дальнейшее чтение
  132. Spring Boot Startup Вопрос: Как джитование динамически порожденных классов влияет

    на стартап. Ответ: Значительно. На текущий момент 40% времени старта Решение: Мы подумываем над реализацией интерпретатора
  133. Spring Boot Startup Вопрос: Как джитование динамически порожденных классов влияет

    на стартап. Ответ: Значительно. На текущий момент 40% времени старта Решение: Мы подумываем над реализацией интерпретатора
  134. Spring Boot Startup Вопрос: Как джитование динамически порожденных классов влияет

    на стартап. Ответ: Значительно. На текущий момент 40% времени старта Решение: Мы подумываем над реализацией интерпретатора
  135. Spring Boot Startup •  Spring Boot Startup – это производительность

    – Spring Boot на старте очень много чего делает – Очень много кода успевает прогреться – Изучение этого кода дает мотивацию для некоторых оптимизаций
  136. Spring Boot Startup https://github.com/shopizer-ecommerce/shopizer

  137. https://github.com/dsyer/spring-boot-aot Spring Boot Startup

  138. https://github.com/dsyer/spring-boot-aot •  Open JDK 8u181 Spring Boot Startup 2018-11-30 13:04:39.577

    INFO 12484 --- [ main] com.acme.SampleApplication : Started SampleApplication in 0.456 seconds (JVM running for 0.885)
  139. https://github.com/dsyer/spring-boot-aot •  Excelsior JET 15.3 (Java 8u181) 2018-11-30 12:59:28.155 INFO

    3300 --- [ main] com.acme.SampleApplication : Started SampleApplication in 0.028 seconds (JVM running for 0.167) Spring Boot Startup
  140. https://github.com/dsyer/spring-boot-aot •  Excelsior JET 15.3 (Java 8u181) 2018-11-30 12:59:28.155 INFO

    3300 --- [ main] com.acme.SampleApplication : Started SampleApplication in 0.028 seconds (JVM running for 0.167) Spring Boot Startup Опасайтесь замеров Хелловорлдов!
  141. Spring Boot Performance https://dzone.com/articles/raw-performance- numbers-spring-boot-2-webflux-vs-s https://github.com/bijukunjummen/boot2-load- demo

  142. Spring Boot Performance •  5000 concurrent users Excelsior JET 15.3

    (Java 8u181) OpenJDK HotSpot 8u181
  143. Spring Boot Performance

  144. Заключение •  Spring Boot очень удобная платформа для создания микросервисов.

    •  Не смотря на динамичность Spring, AOT вполне может справится с этой динамикой
  145. Заключение •  Чем больше микросервисов, тем важнее стартап тайм и

    производительность – CPU стоит денег в облаках •  AOT -- многообещающий подход для ускорения старта и производительности
  146. Excelsior JET •  Бесплатная пробная версия (90 дней) – https://www.excelsiorjet.com/evaluate • 

    Бесплатная версия Standard Edition (32- бита) •  Team Blog – https://www.excelsiorjet.com/blog
  147. Никита Липский, Excelsior nlipsky@excelsior-usa.com twitter: @pjBooms Team blog: https://www.excelsiorjet.com/blog 147