SlideShare a Scribd company logo
GraalVM
One compiler to rule them all
Agenda
GraalVM? What’s that?
Building native apps in Java (or other JVM languages)
Case study: a CLI app
Reflections on reflection
Building a Docker image from scratch
Conclusion
Who’s this guy?
Sylvain Wallez - @bluxte
Software Engineer, Cloud team at Elastic
Member of the Apache Software Foundation
Toulouse JUG co-founder
- we’re hiring!
Started using Java in 1997!
GraalVM: an ecosystem
The traditional JVM
Java HotSpot VM
Bytecode interpreter C1 compiler C2 compiler
GC std lib
GraalVM: a new compiler
Java HotSpot VM
JVM Compiler Interface (JVMCI - JEP 243)
Graal compiler
GC std lib
Truffle framework
Sulong (LLVM)
Replacing the bytecode compiler
Problems with C1 & C2
● Code base is old C++ that is hard to understand
● Evolution stalled. Recent changes: mostly intrinsics provided by Intel
Graal is a bytecode compiler written in Java
● Higher level, cleanly organized
● Better inlining and escape analysis
GraalVM ecosystem
Truffle framework:
● Easily build language interpreters from an AST
● Support for JS, Ruby, Python, R & WebAssembly
Sulong:
● An LLVM bitcode interpreter
● Run any LLVM language on the JVM: C, C++, Rust, etc.
Release schedule
First GA version in May 2019
A release every 3 months
Legal & Pricing
Community Edition
● GPL + Classpath Exception (can safely link with it)
Enterprise edition
● Long term support
● More code optimisations, profiling-guided optimizer
● 25$ (Java SE) + 18$ (GraalVM) per month per processor
SubstrateVM
Look Ma! No JVM!
GraalVM
Java HotSpot VM
JVM Compiler Interface (JVMCI - JEP 243)
Graal compiler
GC std lib
Truffle framework
Sulong (LLVM)
SubstrateVM
Java HotSpot VM
JVM Compiler Interface (JVMCI)
Graal compiler
Truffle framework
Sulong (LLVM)
Substrate VMGCstd lib
Why SubstrateVM?
AOT: ahead of time compilation
Produces native executables
● Instant start: no bytecode compilation
→ Great for CLIs and Cloud functions
● Low memory footprint: no metaspace
→ Great for micro services
Why SubstrateVM?
https://twitter.com/brunoborges/status/1114486749722443776
Why not SubstrateVM?
Dynamic Class Loading / Unloading Not supported
Reflection Supported (Requires Configuration)
Dynamic Proxy Supported (Requires Configuration)
InvokeDynamic and Method Handles Not supported
Finalizers Not supported
Serialization Not supported
References Mostly supported
Security Manager Not supported
JVMTI, JMX, other native VM interfaces Not supported
Profiling-based optimization Use GraalVM EE
Getting started
Install GraalVM
● MacOS: brew cask install graalvm/tap/graalvm-ce-java11
● Docker: docker pull oracle/graalvm-ce:19.3.0-java11
● sdkman: sdk install java 19.3.0.r11-grl
Then install SubstrateVM
● gu install native-image
Use case: zkstat
zkstat
A CLI utility to collect statistics on the data stored in ZooKeeper
● Elastic Cloud has 27 ZooKeeper clusters
● Each contains several million nodes
Features
● Dump list of nodes for ingestion in Elasticsearch
● Aggregate statistics by node pattern (count, size, stdev, versions, etc)
● Uses Picocli to parse CLI arguments
zkstat: main & picocli
public class ZkStatCLIApp {
public static void main(String[] args) throws Exception {
new ZkStatCLIApp().run(args);
}
public void run(String... args) throws Exception {
CommandLine commandLine = new CommandLine(new TopLevelCommand());
commandLine.addSubcommand("logs-stats", new LogStatsCommand());
commandLine.addSubcommand("node-stats", new NodeStatsCommand());
commandLine.addSubcommand("nodetype-stats", new NodeTypeStatsCommand());
commandLine.addSubcommand("nodetype-stats-csv", new NodeTypeCSVStatsCommand());
System.exit(commandLine.execute(args));
}
...
zkstat: main & picocli
@CommandLine.Command(name = "zkstat", mixinStandardHelpOptions = true,
description = "A tool to dump ZooKeeper statistics from ZooKeeper storage folder"
)
class TopLevelCommand implements Callable<Integer> {
public Integer call() throws Exception { return 0; }
}
@CommandLine.Command(name = "logs-stats", description = "Collects transaction log changes into JSON")
class LogStatsCommand implements Callable<Integer> {
@CommandLine.Parameters(index = "0", description = "ZooKeeper datadir")
private String dataDir;
@CommandLine.Parameters(index = "1", description = "Output filename ('-' for stdout)", defaultValue = "-")
private String outputFilename;
@CommandLine.Option(names = {"-t", "--target-index"}, description = "Target index", defaultValue = "zktranlog")
private String targetIndexName;
@Override
public Integer call() throws Exception {
// Do something useful here
}
}
zkstat: picocli in action
$ zkstat logs-stats
Missing required parameters: <dataDir>, <outputFilename>
Usage: zkstat logs-stats [-bc] [-t=<targetIndexName>] <dataDir> <outputFilename>
Collects transaction log changes into JSON
<dataDir> ZooKeeper datadir
<outputFilename> Output filename ('-' for stdout)
-b, --bulk Format JSON as a bulk request to ingest into ElasticSearch
-c, --compress Gzipped output
-t, --target-index=<targetIndexName>
Target index
zkstat: Gradle build (Kotlin edition)
plugins {
id("java")
}
group = "co.elastic.cloud.zookeeper.stats"
version = "1.0.0"
val appName = "zkstat"
val mainClass = "co.elastic.cloud.zookeeper.stats.ZkStatCLIApp"
repositories {
mavenCentral()
}
dependencies {
implementation("org.apache.zookeeper:zookeeper:3.5.3")
implementation("org.slf4j:slf4j-simple:1.7.25")
implementation("info.picocli:picocli:4.0.4")
testImplementation("junit:junit:4.12")
}
From fat jar to native image
tasks.register<Jar>("fatJar") {
archiveBaseName.set("$appName-full")
manifest {
attributes["Main-Class"] = mainClass
}
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
with(tasks.jar.get())
}
$ gradlew fatJar
...
$ ls -lh build/libs/zkstat-full-1.0.0.jar
-rw-r--r-- 1 sylvain staff 2.7M Dec 25 13:37 build/libs/zkstat-full-1.0.0.jar
$ java -jar build/lib/zkstat-full-1.0.0.jar
Usage: zkstat [-hV] [COMMAND]
...
From fat jar to native image
plugins {
id("org.mikeneck.graalvm-native-image") version "0.1.1"
}
nativeImage {
setGraalVmHome(System.getProperty("java.home"))
setMainClass(mainClass)
setExecutableName(appName)
if (System.getProperty("os.name") == "Linux") arguments("--static") // To allow "FROM scratch"
}
$ gradlew nativeImage
...
From fat jar to native image
$ gradlew nativeImage
> Task :nativeImage
Shutdown Server(pid: 30312, port: 49557)
Build on Server(pid: 36613, port: 53328)*
[zkstat:36613] classlist: 2,303.05 ms
....
[zkstat:36613] universe: 928.73 ms
Warning: Reflection method java.lang.Class.forName invoked at picocli.CommandLine$BuiltIn$ClassConverter.convert(Co
Warning: Reflection method java.lang.Class.newInstance invoked at picocli.CommandLine$DefaultFactory.create(Command
Warning: Reflection method java.lang.Class.getMethods invoked at picocli.CommandLine.getCommandMethods(CommandLine.
Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine.getCommandMethods(Comm
Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine$Model$CommandReflectio
Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.cre
Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.cre
Warning: Reflection method java.lang.Class.getDeclaredFields invoked at picocli.CommandLine$Model$CommandReflection
Warning: Aborting stand-alone image build due to reflection use without configuration.
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Build on Server(pid: 36613, port: 53328)
[zkstat:36613] classlist: 210.49 ms
...
[zkstat:36613] [total]: 22,315.84 ms
Warning: Image 'zkstat' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallba
Let’s try with this --no-fallback parameter
From fat jar to native image
$ build/native-image/zkstat logs-stats src dest
Exception in thread "main" picocli.CommandLine$InitializationException:
picocli.CommandLine$AutoHelpMixin is not a command: it has no @Command, @Option, @Parameters or
@Unmatched annotations
at picocli.CommandLine$Model$CommandReflection.validateCommandSpec(CommandLine.java:9731)
at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9566)
at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116)
at picocli.CommandLine$Model$CommandSpec.mixinStandardHelpOptions(CommandLine.java:5858)
at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9549)
at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116)
at picocli.CommandLine.<init>(CommandLine.java:223)
at picocli.CommandLine.<init>(CommandLine.java:196)
at co.elastic.cloud.zookeeper.stats.ZkStatCLIApp.run(ZkStatCLIApp.java:44)
at co.elastic.cloud.zookeeper.stats.ZkStatCLIApp.main(ZkStatCLIApp.java:40)
Configuring reflection
We have to tell native-image:
● what classes are used with reflection
● what classes are proxied
● what resources are loaded from the classpath
● what libraries are load with JNI
Some good builtin heuristics, but cannot guess everything
→ use the tracing agent to create the configs!
$ java -agentlib:native-image-agent=config-output-dir=./graal-config -jar
build/libs/zkstat-full-1.0.0.jar
...
$ ls graal-config
jni-config.json proxy-config.json reflect-config.json resource-config.json
Configuring reflection
reflect-config.json
[
{
"name":"co.elastic.cloud.zookeeper.stats.LogStatsCommand",
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allPublicMethods":true
},
{
"name":"co.elastic.cloud.zookeeper.stats.NodeStatsCommand",
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allPublicMethods":true
},
{
"name":"co.elastic.cloud.zookeeper.stats.NodeTypeCSVStatsCommand",
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allPublicMethods":true
},
{
"name":"co.elastic.cloud.zookeeper.stats.NodeTypeStatsCommand",
"allDeclaredFields":true,
"allDeclaredMethods":true,
Configuring reflection
Picocli comes with an annotation processor that does the job for us!
dependencies {
...
implementation("info.picocli:picocli:4.0.4")
annotationProcessor("info.picocli:picocli-codegen:4.0.4")
}
$ gradlew build
...
$ ls build/classes/java/main/META-INF/native-image/picocli-generated
proxy-config.json reflect-config.json resource-config.json
Configs in META-INF are automatically used by native-image!
Configuring reflection
reflect-config.json
[
{
"name" : "co.elastic.cloud.zookeeper.stats.LogStatsCommand",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "bulk" },
{ "name" : "compress" },
{ "name" : "dataDir" },
{ "name" : "outputFilename" },
{ "name" : "targetIndexName" }
]
},
{
"name" : "co.elastic.cloud.zookeeper.stats.NodeStatsCommand",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "compress" },
From fat jar to native image
$ gradlew nativeImage
> Task :nativeImage
Build on Server(pid: 36613, port: 53328)
[zkstat:36613] classlist: 1,004.58 ms
...
[zkstat:36613] write: 370.40 ms
[zkstat:36613] [total]: 27,443.07 ms
BUILD SUCCESSFUL in 32s
4 actionable tasks: 2 executed, 2 up-to-date
$ build/native-image/zkstat
Usage: zkstat [-hV] [COMMAND]
...
$ ls -lh build/native-image/zkstat
-rwxr-xr-x 1 sylvain staff 13M Dec 25 13:37 build/native-image/zkstat*
a yummy standalone executable!
Fat jar edition
Startup time - essential for a CLI!
$ time java -jar build/libs/zkstat-full-1.0.0.jar
Usage: zkstat [-hV] [COMMAND]
A tool to dump ZooKeeper statistics from ZooKeeper storage folder
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
logs-stats Collects transaction logs changes into JSON
node-stats Collects paths and stats objects from a ZK data dir as CSV
nodetype-stats Computes and outputs as CSV statistics by path type from
a ZK data dir
nodetype-stats-csv Computes and outputs as CSV statistics by path type from
CSV
real 0m0.339s
user 0m0.617s
sys 0m0.080s
RSS 48032 kB
Native image edition
Startup time - essential for a CLI!
$ time build/native-image/zkstat
Usage: zkstat [-hV] [COMMAND]
A tool to dump ZooKeeper statistics from ZooKeeper storage folder
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
logs-stats Collects transaction logs changes into JSON
node-stats Collects paths and stats objects from a ZK data dir as CSV
nodetype-stats Computes and outputs as CSV statistics by path type from
a ZK data dir
nodetype-stats-csv Computes and outputs as CSV statistics by path type from
CSV
real 0m0.013s
user 0m0.006s
sys 0m0.004s
RSS 5664 kB
real 0m0.339s
user 0m0.617s
sys 0m0.080s
RSS 48032 kB
Docker build
FROM oracle/graalvm-ce:19.3.0-java11 as build
RUN gu install native-image
WORKDIR /project
# Download and cache Gradle
COPY gradlew .
COPY gradle ./gradle
RUN ./gradlew
# Download dependencies and cache them separately from the main source code
COPY build.gradle.kts .
RUN ./gradlew downloadDependencies
# Compile and build native image
COPY src ./src
RUN ./gradlew nativeImage
#------------------------------------------------------------------------------
FROM scratch
COPY --from=build /project/build/native-image/zkstat .
CMD ["/zkstat"]
tasks.register("downloadDependencies") {
println("Downloading dependencies")
configurations.testRuntimeClasspath.get().files
}
Docker build
$ docker build -t zkstat .
Sending build context to Docker daemon 140.8kB
Step 1/13 : FROM oracle/graalvm-ce:19.3.0-java11 as build
---> bc6f2b723104
Step 2/13 : RUN gu install native-image
---> Using cache
---> a296590b05e6
...
Step 13/13 : CMD ["/zkstat"]
---> Running in 7109549122e8
Removing intermediate container 7109549122e8
---> 3894ce2f74ad
Successfully built 3894ce2f74ad
Successfully tagged zkstat:latest
$ docker image ls zkstat
REPOSITORY TAG IMAGE ID CREATED SIZE
zkstat latest 6547de4b0068 3 hours ago 14.2MB
Conclusion
Native-image pros
● Small executable
● Small RAM footprint
● Instant start time
Native-image cons
● May require some setup (reflection)
● We lose a lot of management features
● May not provide optimal performance - but is your code optimal?
Thanks!
Questions?
Sylvain Wallez - @bluxte

More Related Content

PDF
GraalVM Native Images by Oleg Selajev @shelajev
PDF
GraalVM: Run Programs Faster Everywhere
PDF
Being Functional on Reactive Streams with Spring Reactor
PPTX
Apache Flink and what it is used for
PDF
GraalVM Overview Compact version
PPTX
HOW AND WHY GRAALVM IS QUICKLY BECOMING RELEVANT FOR YOU
PDF
Secrets of Performance Tuning Java on Kubernetes
PDF
GraphQL Fundamentals
GraalVM Native Images by Oleg Selajev @shelajev
GraalVM: Run Programs Faster Everywhere
Being Functional on Reactive Streams with Spring Reactor
Apache Flink and what it is used for
GraalVM Overview Compact version
HOW AND WHY GRAALVM IS QUICKLY BECOMING RELEVANT FOR YOU
Secrets of Performance Tuning Java on Kubernetes
GraphQL Fundamentals

What's hot (20)

PDF
Spring Boot
PPTX
JVM++: The Graal VM
PPT
Spring Core
PPTX
Spring Boot
PDF
JPA and Hibernate
PDF
Introduction to java (revised)
ODP
Using ANTLR on real example - convert "string combined" queries into paramete...
PPT
Heap & thread dump
PDF
Etsy Activity Feeds Architecture
PDF
Understanding Reactive Programming
PDF
REST vs GraphQL
PDF
Battle of the frameworks : Quarkus vs SpringBoot
PDF
GraalVM Native and Spring Boot 3.0
PDF
A Deep Dive into Query Execution Engine of Spark SQL
PDF
Loom Virtual Threads in the JDK 19
PPT
Oracle WebLogic Server Basic Concepts
PPTX
Exactly-Once Financial Data Processing at Scale with Flink and Pinot
PDF
From Java 11 to 17 and beyond.pdf
PPTX
Intro to React
PDF
Models for hierarchical data
Spring Boot
JVM++: The Graal VM
Spring Core
Spring Boot
JPA and Hibernate
Introduction to java (revised)
Using ANTLR on real example - convert "string combined" queries into paramete...
Heap & thread dump
Etsy Activity Feeds Architecture
Understanding Reactive Programming
REST vs GraphQL
Battle of the frameworks : Quarkus vs SpringBoot
GraalVM Native and Spring Boot 3.0
A Deep Dive into Query Execution Engine of Spark SQL
Loom Virtual Threads in the JDK 19
Oracle WebLogic Server Basic Concepts
Exactly-Once Financial Data Processing at Scale with Flink and Pinot
From Java 11 to 17 and beyond.pdf
Intro to React
Models for hierarchical data
Ad

Similar to Native Java with GraalVM (20)

PPT
An introduction to maven gradle and sbt
PDF
2018 (codeone) Graal VM and MicroProfile a polyglot microservices solution [d...
PDF
GraalVM - MadridJUG 2019-10-22
PDF
GraalVM - OpenSlava 2019-10-18
PDF
Running Spring Boot Applications as GraalVM Native Images
PDF
Adopting GraalVM - Scale by the Bay 2018
PDF
Jan Stępień - GraalVM: Fast, Polyglot, Native - Codemotion Berlin 2018
PDF
Spring Boot Native written by software developers
PPTX
All you need to know about Spring Boot and GraalVM
PDF
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?
PDF
Polyglot Applications with GraalVM
PDF
GraalVM and MicroProfile - A Polyglot Microservices Solution
PDF
Gradle Introduction
PDF
Gradleintroduction 111010130329-phpapp01
PPTX
Lean microservices through ahead of time compilation (Tobias Piper, Loveholid...
PPTX
Road to sbt 1.0: Paved with server (2015 Amsterdam)
PPTX
Road to sbt 1.0 paved with server
PDF
Run Scala Faster with GraalVM on any Platform / GraalVMで、どこでもScalaを高速実行しよう by...
PDF
Everything you need to know about GraalVM Native Image
An introduction to maven gradle and sbt
2018 (codeone) Graal VM and MicroProfile a polyglot microservices solution [d...
GraalVM - MadridJUG 2019-10-22
GraalVM - OpenSlava 2019-10-18
Running Spring Boot Applications as GraalVM Native Images
Adopting GraalVM - Scale by the Bay 2018
Jan Stępień - GraalVM: Fast, Polyglot, Native - Codemotion Berlin 2018
Spring Boot Native written by software developers
All you need to know about Spring Boot and GraalVM
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?
Polyglot Applications with GraalVM
GraalVM and MicroProfile - A Polyglot Microservices Solution
Gradle Introduction
Gradleintroduction 111010130329-phpapp01
Lean microservices through ahead of time compilation (Tobias Piper, Loveholid...
Road to sbt 1.0: Paved with server (2015 Amsterdam)
Road to sbt 1.0 paved with server
Run Scala Faster with GraalVM on any Platform / GraalVMで、どこでもScalaを高速実行しよう by...
Everything you need to know about GraalVM Native Image
Ad

More from Sylvain Wallez (13)

PDF
Inside the JVM - Follow the white rabbit! / Breizh JUG
PDF
Developing web applications in Rust
PDF
Black friday logs - Scaling Elasticsearch
PDF
Elastic - From 50 to 270, how to scale a distributed engineering team
PDF
Inside the JVM - Follow the white rabbit!
PDF
Introduction au langage Go
PDF
Kibana + timelion: time series with the elastic stack
PDF
2016 05 iot - apero web
PDF
Brown Bag Lunch sur Hazelcast
PDF
Lucene - 10 ans d'usages plus ou moins classiques
PDF
2012 11 Toulibre - Open Hardware
PDF
Play Framework - Toulouse JUG - nov 2011
PDF
Développement avec Java Micro Edition
Inside the JVM - Follow the white rabbit! / Breizh JUG
Developing web applications in Rust
Black friday logs - Scaling Elasticsearch
Elastic - From 50 to 270, how to scale a distributed engineering team
Inside the JVM - Follow the white rabbit!
Introduction au langage Go
Kibana + timelion: time series with the elastic stack
2016 05 iot - apero web
Brown Bag Lunch sur Hazelcast
Lucene - 10 ans d'usages plus ou moins classiques
2012 11 Toulibre - Open Hardware
Play Framework - Toulouse JUG - nov 2011
Développement avec Java Micro Edition

Recently uploaded (20)

PDF
Digital Strategies for Manufacturing Companies
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
System and Network Administraation Chapter 3
PPTX
CHAPTER 2 - PM Management and IT Context
PPTX
ISO 45001 Occupational Health and Safety Management System
PPTX
L1 - Introduction to python Backend.pptx
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Nekopoi APK 2025 free lastest update
Digital Strategies for Manufacturing Companies
Softaken Excel to vCard Converter Software.pdf
2025 Textile ERP Trends: SAP, Odoo & Oracle
PTS Company Brochure 2025 (1).pdf.......
Odoo POS Development Services by CandidRoot Solutions
How Creative Agencies Leverage Project Management Software.pdf
How to Choose the Right IT Partner for Your Business in Malaysia
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
System and Network Administraation Chapter 3
CHAPTER 2 - PM Management and IT Context
ISO 45001 Occupational Health and Safety Management System
L1 - Introduction to python Backend.pptx
Which alternative to Crystal Reports is best for small or large businesses.pdf
ManageIQ - Sprint 268 Review - Slide Deck
Operating system designcfffgfgggggggvggggggggg
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Nekopoi APK 2025 free lastest update

Native Java with GraalVM

  • 1. GraalVM One compiler to rule them all
  • 2. Agenda GraalVM? What’s that? Building native apps in Java (or other JVM languages) Case study: a CLI app Reflections on reflection Building a Docker image from scratch Conclusion
  • 3. Who’s this guy? Sylvain Wallez - @bluxte Software Engineer, Cloud team at Elastic Member of the Apache Software Foundation Toulouse JUG co-founder - we’re hiring! Started using Java in 1997!
  • 5. The traditional JVM Java HotSpot VM Bytecode interpreter C1 compiler C2 compiler GC std lib
  • 6. GraalVM: a new compiler Java HotSpot VM JVM Compiler Interface (JVMCI - JEP 243) Graal compiler GC std lib Truffle framework Sulong (LLVM)
  • 7. Replacing the bytecode compiler Problems with C1 & C2 ● Code base is old C++ that is hard to understand ● Evolution stalled. Recent changes: mostly intrinsics provided by Intel Graal is a bytecode compiler written in Java ● Higher level, cleanly organized ● Better inlining and escape analysis
  • 8. GraalVM ecosystem Truffle framework: ● Easily build language interpreters from an AST ● Support for JS, Ruby, Python, R & WebAssembly Sulong: ● An LLVM bitcode interpreter ● Run any LLVM language on the JVM: C, C++, Rust, etc.
  • 9. Release schedule First GA version in May 2019 A release every 3 months
  • 10. Legal & Pricing Community Edition ● GPL + Classpath Exception (can safely link with it) Enterprise edition ● Long term support ● More code optimisations, profiling-guided optimizer ● 25$ (Java SE) + 18$ (GraalVM) per month per processor
  • 12. GraalVM Java HotSpot VM JVM Compiler Interface (JVMCI - JEP 243) Graal compiler GC std lib Truffle framework Sulong (LLVM)
  • 13. SubstrateVM Java HotSpot VM JVM Compiler Interface (JVMCI) Graal compiler Truffle framework Sulong (LLVM) Substrate VMGCstd lib
  • 14. Why SubstrateVM? AOT: ahead of time compilation Produces native executables ● Instant start: no bytecode compilation → Great for CLIs and Cloud functions ● Low memory footprint: no metaspace → Great for micro services
  • 16. Why not SubstrateVM? Dynamic Class Loading / Unloading Not supported Reflection Supported (Requires Configuration) Dynamic Proxy Supported (Requires Configuration) InvokeDynamic and Method Handles Not supported Finalizers Not supported Serialization Not supported References Mostly supported Security Manager Not supported JVMTI, JMX, other native VM interfaces Not supported Profiling-based optimization Use GraalVM EE
  • 17. Getting started Install GraalVM ● MacOS: brew cask install graalvm/tap/graalvm-ce-java11 ● Docker: docker pull oracle/graalvm-ce:19.3.0-java11 ● sdkman: sdk install java 19.3.0.r11-grl Then install SubstrateVM ● gu install native-image
  • 19. zkstat A CLI utility to collect statistics on the data stored in ZooKeeper ● Elastic Cloud has 27 ZooKeeper clusters ● Each contains several million nodes Features ● Dump list of nodes for ingestion in Elasticsearch ● Aggregate statistics by node pattern (count, size, stdev, versions, etc) ● Uses Picocli to parse CLI arguments
  • 20. zkstat: main & picocli public class ZkStatCLIApp { public static void main(String[] args) throws Exception { new ZkStatCLIApp().run(args); } public void run(String... args) throws Exception { CommandLine commandLine = new CommandLine(new TopLevelCommand()); commandLine.addSubcommand("logs-stats", new LogStatsCommand()); commandLine.addSubcommand("node-stats", new NodeStatsCommand()); commandLine.addSubcommand("nodetype-stats", new NodeTypeStatsCommand()); commandLine.addSubcommand("nodetype-stats-csv", new NodeTypeCSVStatsCommand()); System.exit(commandLine.execute(args)); } ...
  • 21. zkstat: main & picocli @CommandLine.Command(name = "zkstat", mixinStandardHelpOptions = true, description = "A tool to dump ZooKeeper statistics from ZooKeeper storage folder" ) class TopLevelCommand implements Callable<Integer> { public Integer call() throws Exception { return 0; } } @CommandLine.Command(name = "logs-stats", description = "Collects transaction log changes into JSON") class LogStatsCommand implements Callable<Integer> { @CommandLine.Parameters(index = "0", description = "ZooKeeper datadir") private String dataDir; @CommandLine.Parameters(index = "1", description = "Output filename ('-' for stdout)", defaultValue = "-") private String outputFilename; @CommandLine.Option(names = {"-t", "--target-index"}, description = "Target index", defaultValue = "zktranlog") private String targetIndexName; @Override public Integer call() throws Exception { // Do something useful here } }
  • 22. zkstat: picocli in action $ zkstat logs-stats Missing required parameters: <dataDir>, <outputFilename> Usage: zkstat logs-stats [-bc] [-t=<targetIndexName>] <dataDir> <outputFilename> Collects transaction log changes into JSON <dataDir> ZooKeeper datadir <outputFilename> Output filename ('-' for stdout) -b, --bulk Format JSON as a bulk request to ingest into ElasticSearch -c, --compress Gzipped output -t, --target-index=<targetIndexName> Target index
  • 23. zkstat: Gradle build (Kotlin edition) plugins { id("java") } group = "co.elastic.cloud.zookeeper.stats" version = "1.0.0" val appName = "zkstat" val mainClass = "co.elastic.cloud.zookeeper.stats.ZkStatCLIApp" repositories { mavenCentral() } dependencies { implementation("org.apache.zookeeper:zookeeper:3.5.3") implementation("org.slf4j:slf4j-simple:1.7.25") implementation("info.picocli:picocli:4.0.4") testImplementation("junit:junit:4.12") }
  • 24. From fat jar to native image tasks.register<Jar>("fatJar") { archiveBaseName.set("$appName-full") manifest { attributes["Main-Class"] = mainClass } from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) with(tasks.jar.get()) } $ gradlew fatJar ... $ ls -lh build/libs/zkstat-full-1.0.0.jar -rw-r--r-- 1 sylvain staff 2.7M Dec 25 13:37 build/libs/zkstat-full-1.0.0.jar $ java -jar build/lib/zkstat-full-1.0.0.jar Usage: zkstat [-hV] [COMMAND] ...
  • 25. From fat jar to native image plugins { id("org.mikeneck.graalvm-native-image") version "0.1.1" } nativeImage { setGraalVmHome(System.getProperty("java.home")) setMainClass(mainClass) setExecutableName(appName) if (System.getProperty("os.name") == "Linux") arguments("--static") // To allow "FROM scratch" } $ gradlew nativeImage ...
  • 26. From fat jar to native image $ gradlew nativeImage > Task :nativeImage Shutdown Server(pid: 30312, port: 49557) Build on Server(pid: 36613, port: 53328)* [zkstat:36613] classlist: 2,303.05 ms .... [zkstat:36613] universe: 928.73 ms Warning: Reflection method java.lang.Class.forName invoked at picocli.CommandLine$BuiltIn$ClassConverter.convert(Co Warning: Reflection method java.lang.Class.newInstance invoked at picocli.CommandLine$DefaultFactory.create(Command Warning: Reflection method java.lang.Class.getMethods invoked at picocli.CommandLine.getCommandMethods(CommandLine. Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine.getCommandMethods(Comm Warning: Reflection method java.lang.Class.getDeclaredMethods invoked at picocli.CommandLine$Model$CommandReflectio Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.cre Warning: Reflection method java.lang.Class.getDeclaredConstructor invoked at picocli.CommandLine$DefaultFactory.cre Warning: Reflection method java.lang.Class.getDeclaredFields invoked at picocli.CommandLine$Model$CommandReflection Warning: Aborting stand-alone image build due to reflection use without configuration. Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception Build on Server(pid: 36613, port: 53328) [zkstat:36613] classlist: 210.49 ms ... [zkstat:36613] [total]: 22,315.84 ms Warning: Image 'zkstat' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallba
  • 27. Let’s try with this --no-fallback parameter From fat jar to native image $ build/native-image/zkstat logs-stats src dest Exception in thread "main" picocli.CommandLine$InitializationException: picocli.CommandLine$AutoHelpMixin is not a command: it has no @Command, @Option, @Parameters or @Unmatched annotations at picocli.CommandLine$Model$CommandReflection.validateCommandSpec(CommandLine.java:9731) at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9566) at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116) at picocli.CommandLine$Model$CommandSpec.mixinStandardHelpOptions(CommandLine.java:5858) at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:9549) at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:5116) at picocli.CommandLine.<init>(CommandLine.java:223) at picocli.CommandLine.<init>(CommandLine.java:196) at co.elastic.cloud.zookeeper.stats.ZkStatCLIApp.run(ZkStatCLIApp.java:44) at co.elastic.cloud.zookeeper.stats.ZkStatCLIApp.main(ZkStatCLIApp.java:40)
  • 28. Configuring reflection We have to tell native-image: ● what classes are used with reflection ● what classes are proxied ● what resources are loaded from the classpath ● what libraries are load with JNI Some good builtin heuristics, but cannot guess everything → use the tracing agent to create the configs! $ java -agentlib:native-image-agent=config-output-dir=./graal-config -jar build/libs/zkstat-full-1.0.0.jar ... $ ls graal-config jni-config.json proxy-config.json reflect-config.json resource-config.json
  • 30. Configuring reflection Picocli comes with an annotation processor that does the job for us! dependencies { ... implementation("info.picocli:picocli:4.0.4") annotationProcessor("info.picocli:picocli-codegen:4.0.4") } $ gradlew build ... $ ls build/classes/java/main/META-INF/native-image/picocli-generated proxy-config.json reflect-config.json resource-config.json Configs in META-INF are automatically used by native-image!
  • 31. Configuring reflection reflect-config.json [ { "name" : "co.elastic.cloud.zookeeper.stats.LogStatsCommand", "allDeclaredConstructors" : true, "allPublicConstructors" : true, "allDeclaredMethods" : true, "allPublicMethods" : true, "fields" : [ { "name" : "bulk" }, { "name" : "compress" }, { "name" : "dataDir" }, { "name" : "outputFilename" }, { "name" : "targetIndexName" } ] }, { "name" : "co.elastic.cloud.zookeeper.stats.NodeStatsCommand", "allDeclaredConstructors" : true, "allPublicConstructors" : true, "allDeclaredMethods" : true, "allPublicMethods" : true, "fields" : [ { "name" : "compress" },
  • 32. From fat jar to native image $ gradlew nativeImage > Task :nativeImage Build on Server(pid: 36613, port: 53328) [zkstat:36613] classlist: 1,004.58 ms ... [zkstat:36613] write: 370.40 ms [zkstat:36613] [total]: 27,443.07 ms BUILD SUCCESSFUL in 32s 4 actionable tasks: 2 executed, 2 up-to-date $ build/native-image/zkstat Usage: zkstat [-hV] [COMMAND] ... $ ls -lh build/native-image/zkstat -rwxr-xr-x 1 sylvain staff 13M Dec 25 13:37 build/native-image/zkstat* a yummy standalone executable!
  • 33. Fat jar edition Startup time - essential for a CLI! $ time java -jar build/libs/zkstat-full-1.0.0.jar Usage: zkstat [-hV] [COMMAND] A tool to dump ZooKeeper statistics from ZooKeeper storage folder -h, --help Show this help message and exit. -V, --version Print version information and exit. Commands: logs-stats Collects transaction logs changes into JSON node-stats Collects paths and stats objects from a ZK data dir as CSV nodetype-stats Computes and outputs as CSV statistics by path type from a ZK data dir nodetype-stats-csv Computes and outputs as CSV statistics by path type from CSV real 0m0.339s user 0m0.617s sys 0m0.080s RSS 48032 kB
  • 34. Native image edition Startup time - essential for a CLI! $ time build/native-image/zkstat Usage: zkstat [-hV] [COMMAND] A tool to dump ZooKeeper statistics from ZooKeeper storage folder -h, --help Show this help message and exit. -V, --version Print version information and exit. Commands: logs-stats Collects transaction logs changes into JSON node-stats Collects paths and stats objects from a ZK data dir as CSV nodetype-stats Computes and outputs as CSV statistics by path type from a ZK data dir nodetype-stats-csv Computes and outputs as CSV statistics by path type from CSV real 0m0.013s user 0m0.006s sys 0m0.004s RSS 5664 kB real 0m0.339s user 0m0.617s sys 0m0.080s RSS 48032 kB
  • 35. Docker build FROM oracle/graalvm-ce:19.3.0-java11 as build RUN gu install native-image WORKDIR /project # Download and cache Gradle COPY gradlew . COPY gradle ./gradle RUN ./gradlew # Download dependencies and cache them separately from the main source code COPY build.gradle.kts . RUN ./gradlew downloadDependencies # Compile and build native image COPY src ./src RUN ./gradlew nativeImage #------------------------------------------------------------------------------ FROM scratch COPY --from=build /project/build/native-image/zkstat . CMD ["/zkstat"] tasks.register("downloadDependencies") { println("Downloading dependencies") configurations.testRuntimeClasspath.get().files }
  • 36. Docker build $ docker build -t zkstat . Sending build context to Docker daemon 140.8kB Step 1/13 : FROM oracle/graalvm-ce:19.3.0-java11 as build ---> bc6f2b723104 Step 2/13 : RUN gu install native-image ---> Using cache ---> a296590b05e6 ... Step 13/13 : CMD ["/zkstat"] ---> Running in 7109549122e8 Removing intermediate container 7109549122e8 ---> 3894ce2f74ad Successfully built 3894ce2f74ad Successfully tagged zkstat:latest $ docker image ls zkstat REPOSITORY TAG IMAGE ID CREATED SIZE zkstat latest 6547de4b0068 3 hours ago 14.2MB
  • 37. Conclusion Native-image pros ● Small executable ● Small RAM footprint ● Instant start time Native-image cons ● May require some setup (reflection) ● We lose a lot of management features ● May not provide optimal performance - but is your code optimal?