SlideShare une entreprise Scribd logo
1  sur  42
Making Java more dynamic:
runtime code generation
for the JVM
interface Framework {
<T> Class<? extends T> secure(Class<T> type);
}
class Service {
@Secured(user = "ADMIN")
void deleteEverything() {
// delete everything...
}
}
@interface Secured {
String user();
}
class UserHolder {
static String user = "ANONYMOUS";
}
does not know aboutdepends on
discoversatruntime
class Service {
@Secured(user = "ADMIN")
void deleteEverything() {
if(!"ADMIN".equals(UserHolder.user)) {
throw new IllegalStateException("Wrong user");
}
// delete everything...
}
}
redefine class
(build time, agent)
create subclass
(Liskov substitution)
class SecuredService extends Service {
@Override
void deleteEverything() {
if(!"ADMIN".equals(UserHolder.user)) {
throw new IllegalStateException("Wrong user");
}
super.deleteEverything();
}
}
class Service {
@Secured(user = "ADMIN")
void deleteEverything() {
// delete everything...
}
}
0xCAFEBABE
source code
byte code
JVM
javac scalac groovyc jrubyc
JIT compilerinterpreter
class loader
creates
reads
runs
class Method {
Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException;
}
class Class {
Method getDeclaredMethod(String name,
Class<?>... parameterTypes)
throws NoSuchMethodException,
SecurityException;
}
Isn’t reflection meant for this?
Reflection implies neither type-safety nor a notion of fail-fast.
Note: there are no performance gains when using code generation over reflection!
Thus, runtime code generation only makes sense for user type enhancement: While
the framework code is less type safe, this type-unsafety does not spoil the user‘s code.
Do-it-yourself as an alternative?
class Service {
void deleteEverything() {
if(!"ADMIN".equals(UserHolder.user)) {
throw new IllegalStateException("Wrong user");
}
// delete everything...
}
}
At best, this makes testing an issue.
Maybe still the easiest approach for simple cross-cutting concerns.
In general, declarative programming often results in readable and modular code.
The “black magic” prejudice.
var service = {
/* @Secured(user = "ADMIN") */
deleteEverything: function () {
// delete everything ...
}
}
function run(service) {
service.deleteEverything();
}
In dynamic languages (also those running on the JVM) this concept is applied a lot!
For framework implementors, type-safety is conceptually impossible.
But with type information available, we are at least able to fail fast when generating
code at runtime in case that types do not match.
No type, no problem.
(“duck typing”)
The performance myth.
int compute() {
return i * ConstantHolder.value;
}
There is no point in “byte code optimization”.
It’s not true that “reflection is slower than generated code”.
Method::invoke
NativeMethodAccessor
GeneratedMethodAccessor###
The JIT compiler knows its job pretty well. NEVER “optimize” byte code.
Never use JNI for something you could also express as byte code.
However, avoid reflective member lookup.
-Dsun.reflect.inflationThreshold=#
int foo() {
return 1 + 2;
}
ICONST_1
ICONST_2
IADD
operand stack 1
2
13
IRETURN
0x04
0x05
0x60
0xAC
Java source code Java byte code
MethodVisitor methodVisitor = ...
methodVisitor.visitInsn(Opcodes.ICONST_1);
methodVisitor.visitInsn(Opcodes.ICONST_2);
methodVisitor.visitInsn(Opcodes.IADD);
methodVisitor.visitInsn(Opcodes.IRETURN);
MethodNode methodNode = ...
InsnList insnList = methodNode.instructions;
insnList.add(new InsnNode(Opcodes.ICONST_1));
insnList.add(new InsnNode(Opcodes.ICONST_2));
insnList.add(new InsnNode(Opcodes.IADD));
insnList.add(new InsnNode(Opcodes.IRETURN));
ASM / BCEL Javassist cglib Byte Buddy
visitorAPItreeAPI
• Byte code-level API gives full freedom
• Requires knowledge of byte code
(stack metaphor, JVM type system)
• Requires a lot of manual work
(stack sizes / stack map frames)
• Byte code-level APIs are not type safe
(jeopardy of verifier errors, visitor call order)
• Byte code itself is little expressive
• Low overhead (visitor APIs)
• ASM is currently more popular than BCEL
(used by the OpenJDK, considered as public API)
• Versioning issues for ASM (especially v3 to v4)
ASM / BCEL Javassist cglib Byte Buddy
ASM / BCEL Javassist cglib Byte Buddy
int foo() {
return 1 + 2;
}
"int foo() {" +
" return 1 + 2;" +
"}"
• Strings are not typed (“SQL quandary”)
• Specifically: Security problems!
• Makes debugging difficult
(unlinked source code, exception stack traces)
• Bound to Java as a language
• The Javassist compiler lags behind javac
• Requires special Java source code instructions
for realizing cross-cutting concerns
class SecuredService extends Service {
@Override
void deleteEverything() {
methodInterceptor.intercept(this,
Service.class.getDeclaredMethod("deleteEverything"),
new Object[0],
new $MethodProxy());
}
class $MethodProxy implements MethodProxy {
// inner class semantics, can call super
}
}
ASM / BCEL Javassist cglib Byte Buddy
class SecuredService extends Service {
@Override
void deleteEverything() {
if(!"ADMIN".equals(UserHolder.user)) {
throw new IllegalStateException("Wrong user");
}
super.deleteEverything();
}
}
genericdelegation
interface MethodInterceptor {
Object intercept(Object object,
Method method,
Object[] arguments,
MethodProxy proxy)
throws Throwable
}
ASM / BCEL Javassist cglib Byte Buddy
• Discards all available type information
• JIT compiler struggles with two-way-boxing
(check out JIT-watch for evidence)
• Interface dependency of intercepted classes
• Delegation requires explicit class initialization
(breaks build-time usage / class serialization)
• Subclass instrumentation only
(breaks annotation APIs / class identity)
• “Feature complete” / little development
• Little intuitive user-API
ASM / BCEL Javassist cglib Byte Buddy
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(named("toString"))
.intercept(value("Hello World!"))
.make()
.load(getClass().getClassLoader())
.getLoaded();
assertThat(dynamicType.newInstance().toString(),
is("Hello World!"));
ASM / BCEL Javassist cglib Byte Buddy
class MyInterceptor {
static String intercept() {
return "Hello World";
}
}
identifiesbestmatch
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(named("toString"))
.intercept(to(MyInterceptor.class))
.make()
.load(getClass().getClassLoader())
.getLoaded();
ASM / BCEL Javassist cglib Byte Buddy
Annotations that are not visible to a class loader are ignored at runtime.
Thus, Byte Buddy’s classes can be used without Byte Buddy on the class path.
class MyInterceptor {
static String intercept(@Origin Method m) {
return "Hello World from " + m.getName();
}
}
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(named("toString"))
.intercept(to(MyInterceptor.class))
.make()
.load(getClass().getClassLoader())
.getLoaded();
providesarguments
ASM / BCEL Javassist cglib Byte Buddy
@Origin Method|Class<?>|String Provides caller information
@SuperCall Runnable|Callable<?> Allows super method call
@DefaultCall Runnable|Callable<?> Allows default method call
@AllArguments T[] Provides boxed method arguments
@Argument(index) T Provides argument at the given index
@This T Provides caller instance
@Super T Provides super method proxy
ASM / BCEL Javassist cglib Byte Buddy
class Foo {
String bar() { return "bar"; }
}
Foo foo = new Foo();
new ByteBuddy()
.redefine(Foo.class)
.method(named("bar"))
.intercept(value("Hello World!"))
.make()
.load(Foo.class.getClassLoader(),
ClassReloadingStrategy.installedAgent());
assertThat(foo.bar(), is("Hello World!"));
The instrumentation API does not allow introduction of new methods.
This might change with JEP-159: Enhanced Class Redefiniton.
class Foo {
String bar() { return "bar"; }
}
assertThat(new Foo().bar(), is("Hello World!"));
public static void premain(String arguments,
Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(named("Foo"))
.transform((builder, type, loader, module) ->
builder.method(named("bar"))
.intercept(value("Hello World!"));
)
.installOn(instrumentation);
}
Byte Buddy: Java agents
class TimerAdvice {
@OnMethodEnter
static long enter() {
return System.nanoTime();
}
@OnMethodExit
static void exit(@Enter long start, @Origin String method) {
long duration = System.nanoTime() - start;
System.out.println(method + " took " + duration);
}
}
class Foo {
String bar() {
long $start = System.nanoTime();
return "bar";
}
}
class Foo {
String bar() {
long $start = System.nanoTime();
String $result = "bar";
return $result;
}
}
class Foo {
String bar() {
long $start = System.nanoTime();
String $result = "bar";
long $duration = System.nanoTime() – $start;
System.out.println("Foo.bar()" + " took " + $duration);
return $result;
}
}
class Foo {
String bar() {
return "bar";
}
}
Byte Buddy: adding code to a method using advice
class Foo {
String bar() { return "bar"; }
}
System.out.println(new Foo().bar());
public static void agentmain(String arguments,
Instrumentation instrumentation) {
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges()
.type(named("Foo"))
.transform((builder, type, loader, module) ->
builder.visit(Advice.to(TimerAdvice.class)
.on(named("bar")));
)
.installOn(instrumentation);
}
Byte Buddy: Java runtime agents
// bar
// Foo.bar() took 1630
Build-time instrumentation: API
public class SimplePlugin implements Plugin {
@Override
public boolean matches(TypeDescription target) {
return target.getName().equals("Foo");
}
@Override
public DynamicType.Builder<?> apply(
DynamicType.Builder<?> builder,
TypeDescription typeDescription) {
return builder.method(named("bar"))
.intercept(value("Hello World!"));
}
}
Build plugins can be applied as Java agent.
Adapters allow build to agent transformation. (Agent APIs are more specific.)
Build-time instrumentation with Maven.
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<configuration>
<transformations>
<transformation>
<plugin>pkg.SimplePlugin</plugin>
</transformation>
</transformations>
</configuration>
</plugin>
Build-time instrumentation with Maven.
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<configuration>
<transformations>
<transformation>
<plugin>pkg.SimplePlugin</plugin>
<artifactId>foo</artifactId>
</transformation>
</transformations>
</configuration>
</plugin>
Build-time instrumentation with Gradle.
buildscript {
repositories { jCenter() }
dependencies {
classpath "net.bytebuddy:byte-buddy-gradle-plugin:+"
}
}
apply plugin: "net.bytebuddy.byte-buddy"
configurations {
simplePlugin
}
dependencies {
simplePlugin "bar:foo:1.0"
}
byteBuddy {
transformation {
plugin = "pkg.SimplePlugin"
classPath = configurations.simplePlugin
}
}
Reality check: Reinvent Java?
“Plain old Java applications” (POJAs)
Working with POJOs reduces complexity. Reducing infrastructure code as a goal
Many applications are built around a central infrastructure. A lot of code does not
solve domain problems but bridges between domain and infrastructure.
Java agents allow to add a decentralized infrastructure at runtime. In the source code,
the infrastructure is only declared.
Java virtual
machine
[stack, JIT]
Dalvik virtual
machine
[register, JIT]
Android
runtime
[register, AOT]
Android makes things more complicated.
Solution: Embed the Android SDK’s dex compiler (Apache 2.0 license).
Unfortunately, only subclass instrumentation possible.
baseline Byte Buddy cglib Javassist Java proxy
(1) 0 142 515 193 70
(2a) 0 1‘126 960 1’070 1’060
(2b) 0.002 0.002 0.003 0.011 0.008
(3a) 0 882
5’408
1‘632 683 n/a
(3b) 0.004 0.004
0.004
0.021 0.025 n/a
Results are measured in nanoseconds (rounded).
All benchmarks run with JMH, source code: https://github.com/raphw/byte-buddy
(1) Extending the Object class without any methods but with a default constructor
(2a) Implementing an interface with 18 methods, method stubs
(2b) Executing a method of this interface
(3a) Extending a class with 18 methods, super method invocation
(3b) Executing a method of this class
Performance: library comparison
I O
foo()
bar()
qux()
Performance: delegation vs. advice
foo()
bar()
qux()
qux()
intercept()
-XX:MaxInlineSize=35 (auto-reduced)
-XX:FreqInlineSize=325
-XX:InlineSmallCode=2000
-XX:MaxInlineLevel=9
-XX:InlineSynchronizedMethods=true
-XX:ReservedCodeCacheSize=48M
-XX:+PrintCodeCache
-XX:+PrintCodeCacheOnCompilation
foo()
[module A]
bar()
[module A]
qux()
[module A]
qux()
[module A]
intercept()
[module B]
Module moduleA = findModule("A"),
moduleB = findModule("B");
moduleA.addReads(moduleB);
Java 9: instrumentation meets modules
instrumentation.addReads(moduleA, moduleB);
Loading and leaking runtime classes
boot extension system wrapper
Foo
Bar
[wrapper]
Bar
[inject]
public class Foo {
/* package-private */ String bar() { return "foo"; }
}
public class Bar extends Foo {
@Override
/* package-private */ String bar() { return "bar"; }
}
Caching runtime classes
WeakHashMap<Class<?>, Class<?>> proxies;
Foo Foo$Proxy
via CL
WeakHashMap<Class<?>, SoftReference<Class<?>>>
Map<Class<?>, Class<?>>
WeakHashMap<Class<?>, WeakReference<Class<?>>>
Value references key strongly (parent class). Requires active life-cycle management.
When injecting into the source class loader, this is the only meaningful solution.
Proxy class is collected when there is no instance available.
Proxy class is collected when the heap is filled and there is no instance available.
The only proper solution would be ephemerons which are not available in the JVM.
Discussed in January 2016 / OpenJDK mailing list (core-libs-dev).
class Foo {
void bar(Object arg) { /* do something */ }
}
class Bar extends Foo {
@Override
void bar(Number arg) { /* do something */ }
/* bridge */
void bar(Object arg) { this.bar((Number) arg); }
}
class Foo<T> {
void bar(T arg) { /* do something */ }
}
class Bar<S extends Number> extends Foo<S> {
@Override
void bar(S arg) { /* do something */ }
}
Dealing with bridge methods
class Bar$Proxy extends Bar {
@Override void bar(Number arg) { super.bar(arg); }
@Override void bar(Object arg) { super.bar(arg); }
}
class Foo {
public void bar() { /* do something */ }
}
public class Bar extends Foo {
/* bridge */
public void bar() { super.bar(); }
}
class Foo {
public void bar() { /* do something */ }
}
public class Bar extends Foo { }
Dealing with bridge methods: visibility bridges
class Qux {
public final void bar() { /* do something */ }
}
public class Baz extends Qux { }
new Baz().bar(); // works
Baz.class.getMethod("bar").invoke(new Baz()); // access error
Generic runtime types and dealing with meta data
new ByteBuddy()
.subclass(parameterizedType(Bar.class,
String.class).build())
.method(named("foo"))
.intercept(value("bar"))
.attribute(ForInstrumentedMethod.INCLUDING_RECEIVER)
.make();
@interface Foo {}
interface Bar<T> {
@Foo
void foo(@Foo this, T arg);
}
Byte Buddy handles meta data and generic types transparently.
Not only important for “build-time” instrumentation but also for meta data APIs.
class $MethodProxy implements MethodProxy {
@Override
public Object invokeSuper(Object self,
Method method,
Object[] arguments) {
if (method.equals($bar)) {
((Foo) self).bar((Integer) arguments[0]);
return null;
} else if (method.equals($qux)) {
return ((Foo) self).qux((String) arguments[0]);
} else {
throw new IllegalStateException(); }
}
}
Super method proxies: cglib
interface Foo {
void bar(int arg);
String qux(String arg);
}
class $BarMethodProxy implements Runnable {
final Foo self; final int arg; // constructor omitted
@Override
public void run() {
self.bar(arg);
}
}
Super method proxies: cglib
interface Foo {
void bar(int arg);
String qux(String arg);
}
class $QuxMethodProxy implements Callable<String> {
final Foo self; final String arg; // constructor omitted
@Override
public String call() {
return self.bar(arg);
}
}
Problems caused by “optional” types
class SomeFramework {
void doFrameworkStuff() {
if (isFancyPluginAvailable()) {
init(new OptionalPluginType());
}
/* do work */
}
private void init(OptionalPluginType plugin) {
/* do work */
}
}
// causes NoClassDefFoundError
SomeFramework.class.getMethods();
Not requiring loaded types: an reflection API alternative
interface TypeDescription
interface MethodDescription
interface FieldDescription
interface AnnotationDescription
When a class file transformer is applied, a class is not yet loaded and it is therefore
not possible to access loaded types. To provide an API for matchers and transformers,
Byte Buddy offers an abstraction to the Java reflection API:
Any reference of such a description is resolved lazily. This also allows working with
optional types. The Byte Buddy agent builder offers an automatic fallback as using
loaded types is more performant.
http://rafael.codes
@rafaelcodes
http://documents4j.com
https://github.com/documents4j/documents4j
http://bytebuddy.net
https://github.com/raphw/byte-buddy

Contenu connexe

Tendances

Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryMauro Rocco
 
ProxySQL High Availability (Clustering)
ProxySQL High Availability (Clustering)ProxySQL High Availability (Clustering)
ProxySQL High Availability (Clustering)Mydbops
 
Pwning the Enterprise With PowerShell
Pwning the Enterprise With PowerShellPwning the Enterprise With PowerShell
Pwning the Enterprise With PowerShellBeau Bullock
 
Going realtime with Socket.IO
Going realtime with Socket.IOGoing realtime with Socket.IO
Going realtime with Socket.IOChristian Joudrey
 
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법Ji-Woong Choi
 
Find your own iOS kernel bug
Find your own iOS kernel bugFind your own iOS kernel bug
Find your own iOS kernel bugGustavo Martinez
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Iocp 기본 구조 이해
Iocp 기본 구조 이해Iocp 기본 구조 이해
Iocp 기본 구조 이해Nam Hyeonuk
 
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and Fanout
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and FanoutOpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and Fanout
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and FanoutSaju Madhavan
 
Java Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyJava Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyAnton Keks
 
Super Easy Memory Forensics
Super Easy Memory ForensicsSuper Easy Memory Forensics
Super Easy Memory ForensicsIIJ
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxNeoClova
 
JMockit Framework Overview
JMockit Framework OverviewJMockit Framework Overview
JMockit Framework OverviewMario Peshev
 
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현NAVER Engineering
 

Tendances (20)

PostgreSQL Replication Tutorial
PostgreSQL Replication TutorialPostgreSQL Replication Tutorial
PostgreSQL Replication Tutorial
 
Europython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & CeleryEuropython 2011 - Playing tasks with Django & Celery
Europython 2011 - Playing tasks with Django & Celery
 
ProxySQL High Availability (Clustering)
ProxySQL High Availability (Clustering)ProxySQL High Availability (Clustering)
ProxySQL High Availability (Clustering)
 
Pwning the Enterprise With PowerShell
Pwning the Enterprise With PowerShellPwning the Enterprise With PowerShell
Pwning the Enterprise With PowerShell
 
Going realtime with Socket.IO
Going realtime with Socket.IOGoing realtime with Socket.IO
Going realtime with Socket.IO
 
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법
[오픈소스컨설팅]Day #1 MySQL 엔진소개, 튜닝, 백업 및 복구, 업그레이드방법
 
Find your own iOS kernel bug
Find your own iOS kernel bugFind your own iOS kernel bug
Find your own iOS kernel bug
 
Why HATEOAS
Why HATEOASWhy HATEOAS
Why HATEOAS
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Iocp advanced
Iocp advancedIocp advanced
Iocp advanced
 
Completable future
Completable futureCompletable future
Completable future
 
Iocp 기본 구조 이해
Iocp 기본 구조 이해Iocp 기본 구조 이해
Iocp 기본 구조 이해
 
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and Fanout
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and FanoutOpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and Fanout
OpenStack Oslo Messaging RPC API Tutorial Demo Call, Cast and Fanout
 
JSON Web Tokens
JSON Web TokensJSON Web Tokens
JSON Web Tokens
 
Java Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyJava Course 10: Threads and Concurrency
Java Course 10: Threads and Concurrency
 
Super Easy Memory Forensics
Super Easy Memory ForensicsSuper Easy Memory Forensics
Super Easy Memory Forensics
 
Sql injection
Sql injectionSql injection
Sql injection
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptx
 
JMockit Framework Overview
JMockit Framework OverviewJMockit Framework Overview
JMockit Framework Overview
 
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
 

Similaire à Making Java more dynamic: runtime code generation for the JVM

The definitive guide to java agents
The definitive guide to java agentsThe definitive guide to java agents
The definitive guide to java agentsRafael Winterhalter
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languagesRafael Winterhalter
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
New features and enhancement
New features and enhancementNew features and enhancement
New features and enhancementRakesh Madugula
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql JOYITAKUNDU1
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?Doug Hawkins
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHave Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHoward Lewis Ship
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 KotlinVMware Tanzu
 
A topology of memory leaks on the JVM
A topology of memory leaks on the JVMA topology of memory leaks on the JVM
A topology of memory leaks on the JVMRafael Winterhalter
 
Object Calisthenics em Go
Object Calisthenics em GoObject Calisthenics em Go
Object Calisthenics em GoElton Minetto
 

Similaire à Making Java more dynamic: runtime code generation for the JVM (20)

The definitive guide to java agents
The definitive guide to java agentsThe definitive guide to java agents
The definitive guide to java agents
 
Java byte code in practice
Java byte code in practiceJava byte code in practice
Java byte code in practice
 
Code generation for alternative languages
Code generation for alternative languagesCode generation for alternative languages
Code generation for alternative languages
 
Griffon @ Svwjug
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ Svwjug
 
Testing in android
Testing in androidTesting in android
Testing in android
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
JDK Power Tools
JDK Power ToolsJDK Power Tools
JDK Power Tools
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
New features and enhancement
New features and enhancementNew features and enhancement
New features and enhancement
 
code for quiz in my sql
code for quiz  in my sql code for quiz  in my sql
code for quiz in my sql
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHave Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 Kotlin
 
Jersey Guice AOP
Jersey Guice AOPJersey Guice AOP
Jersey Guice AOP
 
Nantes Jug - Java 7
Nantes Jug - Java 7Nantes Jug - Java 7
Nantes Jug - Java 7
 
TDD per Webapps
TDD per WebappsTDD per Webapps
TDD per Webapps
 
A topology of memory leaks on the JVM
A topology of memory leaks on the JVMA topology of memory leaks on the JVM
A topology of memory leaks on the JVM
 
Object Calisthenics em Go
Object Calisthenics em GoObject Calisthenics em Go
Object Calisthenics em Go
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 

Plus de Rafael Winterhalter

Java and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemJava and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemRafael Winterhalter
 
Event-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMEvent-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMRafael Winterhalter
 
Getting started with Java 9 modules
Getting started with Java 9 modulesGetting started with Java 9 modules
Getting started with Java 9 modulesRafael Winterhalter
 
Monitoring distributed (micro-)services
Monitoring distributed (micro-)servicesMonitoring distributed (micro-)services
Monitoring distributed (micro-)servicesRafael Winterhalter
 
An introduction to JVM performance
An introduction to JVM performanceAn introduction to JVM performance
An introduction to JVM performanceRafael Winterhalter
 
Understanding Java byte code and the class file format
Understanding Java byte code and the class file formatUnderstanding Java byte code and the class file format
Understanding Java byte code and the class file formatRafael Winterhalter
 

Plus de Rafael Winterhalter (12)

Java and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystemJava and OpenJDK: disecting the ecosystem
Java and OpenJDK: disecting the ecosystem
 
Byte code field report
Byte code field reportByte code field report
Byte code field report
 
Event-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVMEvent-Sourcing Microservices on the JVM
Event-Sourcing Microservices on the JVM
 
Java 10, Java 11 and beyond
Java 10, Java 11 and beyondJava 10, Java 11 and beyond
Java 10, Java 11 and beyond
 
Getting started with Java 9 modules
Getting started with Java 9 modulesGetting started with Java 9 modules
Getting started with Java 9 modules
 
Monitoring distributed (micro-)services
Monitoring distributed (micro-)servicesMonitoring distributed (micro-)services
Monitoring distributed (micro-)services
 
An Overview of Project Jigsaw
An Overview of Project JigsawAn Overview of Project Jigsaw
An Overview of Project Jigsaw
 
Migrating to JUnit 5
Migrating to JUnit 5Migrating to JUnit 5
Migrating to JUnit 5
 
The Java memory model made easy
The Java memory model made easyThe Java memory model made easy
The Java memory model made easy
 
An introduction to JVM performance
An introduction to JVM performanceAn introduction to JVM performance
An introduction to JVM performance
 
Unit testing concurrent code
Unit testing concurrent codeUnit testing concurrent code
Unit testing concurrent code
 
Understanding Java byte code and the class file format
Understanding Java byte code and the class file formatUnderstanding Java byte code and the class file format
Understanding Java byte code and the class file format
 

Dernier

Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...Akihiro Suda
 
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfInnovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfYashikaSharma391629
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROmotivationalword821
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 

Dernier (20)

Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
20240415 [Container Plumbing Days] Usernetes Gen2 - Kubernetes in Rootless Do...
 
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdfInnovate and Collaborate- Harnessing the Power of Open Source Software.pdf
Innovate and Collaborate- Harnessing the Power of Open Source Software.pdf
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTRO
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 

Making Java more dynamic: runtime code generation for the JVM

  • 1. Making Java more dynamic: runtime code generation for the JVM
  • 2. interface Framework { <T> Class<? extends T> secure(Class<T> type); } class Service { @Secured(user = "ADMIN") void deleteEverything() { // delete everything... } } @interface Secured { String user(); } class UserHolder { static String user = "ANONYMOUS"; } does not know aboutdepends on discoversatruntime
  • 3. class Service { @Secured(user = "ADMIN") void deleteEverything() { if(!"ADMIN".equals(UserHolder.user)) { throw new IllegalStateException("Wrong user"); } // delete everything... } } redefine class (build time, agent) create subclass (Liskov substitution) class SecuredService extends Service { @Override void deleteEverything() { if(!"ADMIN".equals(UserHolder.user)) { throw new IllegalStateException("Wrong user"); } super.deleteEverything(); } } class Service { @Secured(user = "ADMIN") void deleteEverything() { // delete everything... } }
  • 4. 0xCAFEBABE source code byte code JVM javac scalac groovyc jrubyc JIT compilerinterpreter class loader creates reads runs
  • 5. class Method { Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; } class Class { Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException; } Isn’t reflection meant for this? Reflection implies neither type-safety nor a notion of fail-fast. Note: there are no performance gains when using code generation over reflection! Thus, runtime code generation only makes sense for user type enhancement: While the framework code is less type safe, this type-unsafety does not spoil the user‘s code.
  • 6. Do-it-yourself as an alternative? class Service { void deleteEverything() { if(!"ADMIN".equals(UserHolder.user)) { throw new IllegalStateException("Wrong user"); } // delete everything... } } At best, this makes testing an issue. Maybe still the easiest approach for simple cross-cutting concerns. In general, declarative programming often results in readable and modular code.
  • 7. The “black magic” prejudice. var service = { /* @Secured(user = "ADMIN") */ deleteEverything: function () { // delete everything ... } } function run(service) { service.deleteEverything(); } In dynamic languages (also those running on the JVM) this concept is applied a lot! For framework implementors, type-safety is conceptually impossible. But with type information available, we are at least able to fail fast when generating code at runtime in case that types do not match. No type, no problem. (“duck typing”)
  • 8.
  • 9. The performance myth. int compute() { return i * ConstantHolder.value; } There is no point in “byte code optimization”. It’s not true that “reflection is slower than generated code”. Method::invoke NativeMethodAccessor GeneratedMethodAccessor### The JIT compiler knows its job pretty well. NEVER “optimize” byte code. Never use JNI for something you could also express as byte code. However, avoid reflective member lookup. -Dsun.reflect.inflationThreshold=#
  • 10. int foo() { return 1 + 2; } ICONST_1 ICONST_2 IADD operand stack 1 2 13 IRETURN 0x04 0x05 0x60 0xAC Java source code Java byte code
  • 11. MethodVisitor methodVisitor = ... methodVisitor.visitInsn(Opcodes.ICONST_1); methodVisitor.visitInsn(Opcodes.ICONST_2); methodVisitor.visitInsn(Opcodes.IADD); methodVisitor.visitInsn(Opcodes.IRETURN); MethodNode methodNode = ... InsnList insnList = methodNode.instructions; insnList.add(new InsnNode(Opcodes.ICONST_1)); insnList.add(new InsnNode(Opcodes.ICONST_2)); insnList.add(new InsnNode(Opcodes.IADD)); insnList.add(new InsnNode(Opcodes.IRETURN)); ASM / BCEL Javassist cglib Byte Buddy visitorAPItreeAPI
  • 12. • Byte code-level API gives full freedom • Requires knowledge of byte code (stack metaphor, JVM type system) • Requires a lot of manual work (stack sizes / stack map frames) • Byte code-level APIs are not type safe (jeopardy of verifier errors, visitor call order) • Byte code itself is little expressive • Low overhead (visitor APIs) • ASM is currently more popular than BCEL (used by the OpenJDK, considered as public API) • Versioning issues for ASM (especially v3 to v4) ASM / BCEL Javassist cglib Byte Buddy
  • 13. ASM / BCEL Javassist cglib Byte Buddy int foo() { return 1 + 2; } "int foo() {" + " return 1 + 2;" + "}" • Strings are not typed (“SQL quandary”) • Specifically: Security problems! • Makes debugging difficult (unlinked source code, exception stack traces) • Bound to Java as a language • The Javassist compiler lags behind javac • Requires special Java source code instructions for realizing cross-cutting concerns
  • 14. class SecuredService extends Service { @Override void deleteEverything() { methodInterceptor.intercept(this, Service.class.getDeclaredMethod("deleteEverything"), new Object[0], new $MethodProxy()); } class $MethodProxy implements MethodProxy { // inner class semantics, can call super } } ASM / BCEL Javassist cglib Byte Buddy class SecuredService extends Service { @Override void deleteEverything() { if(!"ADMIN".equals(UserHolder.user)) { throw new IllegalStateException("Wrong user"); } super.deleteEverything(); } } genericdelegation interface MethodInterceptor { Object intercept(Object object, Method method, Object[] arguments, MethodProxy proxy) throws Throwable }
  • 15. ASM / BCEL Javassist cglib Byte Buddy • Discards all available type information • JIT compiler struggles with two-way-boxing (check out JIT-watch for evidence) • Interface dependency of intercepted classes • Delegation requires explicit class initialization (breaks build-time usage / class serialization) • Subclass instrumentation only (breaks annotation APIs / class identity) • “Feature complete” / little development • Little intuitive user-API
  • 16. ASM / BCEL Javassist cglib Byte Buddy Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(named("toString")) .intercept(value("Hello World!")) .make() .load(getClass().getClassLoader()) .getLoaded(); assertThat(dynamicType.newInstance().toString(), is("Hello World!"));
  • 17. ASM / BCEL Javassist cglib Byte Buddy class MyInterceptor { static String intercept() { return "Hello World"; } } identifiesbestmatch Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(named("toString")) .intercept(to(MyInterceptor.class)) .make() .load(getClass().getClassLoader()) .getLoaded();
  • 18. ASM / BCEL Javassist cglib Byte Buddy Annotations that are not visible to a class loader are ignored at runtime. Thus, Byte Buddy’s classes can be used without Byte Buddy on the class path. class MyInterceptor { static String intercept(@Origin Method m) { return "Hello World from " + m.getName(); } } Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(named("toString")) .intercept(to(MyInterceptor.class)) .make() .load(getClass().getClassLoader()) .getLoaded(); providesarguments
  • 19. ASM / BCEL Javassist cglib Byte Buddy @Origin Method|Class<?>|String Provides caller information @SuperCall Runnable|Callable<?> Allows super method call @DefaultCall Runnable|Callable<?> Allows default method call @AllArguments T[] Provides boxed method arguments @Argument(index) T Provides argument at the given index @This T Provides caller instance @Super T Provides super method proxy
  • 20. ASM / BCEL Javassist cglib Byte Buddy class Foo { String bar() { return "bar"; } } Foo foo = new Foo(); new ByteBuddy() .redefine(Foo.class) .method(named("bar")) .intercept(value("Hello World!")) .make() .load(Foo.class.getClassLoader(), ClassReloadingStrategy.installedAgent()); assertThat(foo.bar(), is("Hello World!")); The instrumentation API does not allow introduction of new methods. This might change with JEP-159: Enhanced Class Redefiniton.
  • 21. class Foo { String bar() { return "bar"; } } assertThat(new Foo().bar(), is("Hello World!")); public static void premain(String arguments, Instrumentation instrumentation) { new AgentBuilder.Default() .type(named("Foo")) .transform((builder, type, loader, module) -> builder.method(named("bar")) .intercept(value("Hello World!")); ) .installOn(instrumentation); } Byte Buddy: Java agents
  • 22. class TimerAdvice { @OnMethodEnter static long enter() { return System.nanoTime(); } @OnMethodExit static void exit(@Enter long start, @Origin String method) { long duration = System.nanoTime() - start; System.out.println(method + " took " + duration); } } class Foo { String bar() { long $start = System.nanoTime(); return "bar"; } } class Foo { String bar() { long $start = System.nanoTime(); String $result = "bar"; return $result; } } class Foo { String bar() { long $start = System.nanoTime(); String $result = "bar"; long $duration = System.nanoTime() – $start; System.out.println("Foo.bar()" + " took " + $duration); return $result; } } class Foo { String bar() { return "bar"; } } Byte Buddy: adding code to a method using advice
  • 23. class Foo { String bar() { return "bar"; } } System.out.println(new Foo().bar()); public static void agentmain(String arguments, Instrumentation instrumentation) { new AgentBuilder.Default() .with(RedefinitionStrategy.RETRANSFORMATION) .disableClassFormatChanges() .type(named("Foo")) .transform((builder, type, loader, module) -> builder.visit(Advice.to(TimerAdvice.class) .on(named("bar"))); ) .installOn(instrumentation); } Byte Buddy: Java runtime agents // bar // Foo.bar() took 1630
  • 24. Build-time instrumentation: API public class SimplePlugin implements Plugin { @Override public boolean matches(TypeDescription target) { return target.getName().equals("Foo"); } @Override public DynamicType.Builder<?> apply( DynamicType.Builder<?> builder, TypeDescription typeDescription) { return builder.method(named("bar")) .intercept(value("Hello World!")); } } Build plugins can be applied as Java agent. Adapters allow build to agent transformation. (Agent APIs are more specific.)
  • 25. Build-time instrumentation with Maven. <plugin> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-maven-plugin</artifactId> <executions> <execution> <goals> <goal>transform</goal> </goals> </execution> </executions> <configuration> <transformations> <transformation> <plugin>pkg.SimplePlugin</plugin> </transformation> </transformations> </configuration> </plugin>
  • 26. Build-time instrumentation with Maven. <plugin> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-maven-plugin</artifactId> <executions> <execution> <goals> <goal>transform</goal> </goals> </execution> </executions> <configuration> <transformations> <transformation> <plugin>pkg.SimplePlugin</plugin> <artifactId>foo</artifactId> </transformation> </transformations> </configuration> </plugin>
  • 27. Build-time instrumentation with Gradle. buildscript { repositories { jCenter() } dependencies { classpath "net.bytebuddy:byte-buddy-gradle-plugin:+" } } apply plugin: "net.bytebuddy.byte-buddy" configurations { simplePlugin } dependencies { simplePlugin "bar:foo:1.0" } byteBuddy { transformation { plugin = "pkg.SimplePlugin" classPath = configurations.simplePlugin } }
  • 28. Reality check: Reinvent Java? “Plain old Java applications” (POJAs) Working with POJOs reduces complexity. Reducing infrastructure code as a goal Many applications are built around a central infrastructure. A lot of code does not solve domain problems but bridges between domain and infrastructure. Java agents allow to add a decentralized infrastructure at runtime. In the source code, the infrastructure is only declared.
  • 29. Java virtual machine [stack, JIT] Dalvik virtual machine [register, JIT] Android runtime [register, AOT] Android makes things more complicated. Solution: Embed the Android SDK’s dex compiler (Apache 2.0 license). Unfortunately, only subclass instrumentation possible.
  • 30. baseline Byte Buddy cglib Javassist Java proxy (1) 0 142 515 193 70 (2a) 0 1‘126 960 1’070 1’060 (2b) 0.002 0.002 0.003 0.011 0.008 (3a) 0 882 5’408 1‘632 683 n/a (3b) 0.004 0.004 0.004 0.021 0.025 n/a Results are measured in nanoseconds (rounded). All benchmarks run with JMH, source code: https://github.com/raphw/byte-buddy (1) Extending the Object class without any methods but with a default constructor (2a) Implementing an interface with 18 methods, method stubs (2b) Executing a method of this interface (3a) Extending a class with 18 methods, super method invocation (3b) Executing a method of this class Performance: library comparison
  • 31. I O foo() bar() qux() Performance: delegation vs. advice foo() bar() qux() qux() intercept() -XX:MaxInlineSize=35 (auto-reduced) -XX:FreqInlineSize=325 -XX:InlineSmallCode=2000 -XX:MaxInlineLevel=9 -XX:InlineSynchronizedMethods=true -XX:ReservedCodeCacheSize=48M -XX:+PrintCodeCache -XX:+PrintCodeCacheOnCompilation
  • 32. foo() [module A] bar() [module A] qux() [module A] qux() [module A] intercept() [module B] Module moduleA = findModule("A"), moduleB = findModule("B"); moduleA.addReads(moduleB); Java 9: instrumentation meets modules instrumentation.addReads(moduleA, moduleB);
  • 33. Loading and leaking runtime classes boot extension system wrapper Foo Bar [wrapper] Bar [inject] public class Foo { /* package-private */ String bar() { return "foo"; } } public class Bar extends Foo { @Override /* package-private */ String bar() { return "bar"; } }
  • 34. Caching runtime classes WeakHashMap<Class<?>, Class<?>> proxies; Foo Foo$Proxy via CL WeakHashMap<Class<?>, SoftReference<Class<?>>> Map<Class<?>, Class<?>> WeakHashMap<Class<?>, WeakReference<Class<?>>> Value references key strongly (parent class). Requires active life-cycle management. When injecting into the source class loader, this is the only meaningful solution. Proxy class is collected when there is no instance available. Proxy class is collected when the heap is filled and there is no instance available. The only proper solution would be ephemerons which are not available in the JVM. Discussed in January 2016 / OpenJDK mailing list (core-libs-dev).
  • 35. class Foo { void bar(Object arg) { /* do something */ } } class Bar extends Foo { @Override void bar(Number arg) { /* do something */ } /* bridge */ void bar(Object arg) { this.bar((Number) arg); } } class Foo<T> { void bar(T arg) { /* do something */ } } class Bar<S extends Number> extends Foo<S> { @Override void bar(S arg) { /* do something */ } } Dealing with bridge methods class Bar$Proxy extends Bar { @Override void bar(Number arg) { super.bar(arg); } @Override void bar(Object arg) { super.bar(arg); } }
  • 36. class Foo { public void bar() { /* do something */ } } public class Bar extends Foo { /* bridge */ public void bar() { super.bar(); } } class Foo { public void bar() { /* do something */ } } public class Bar extends Foo { } Dealing with bridge methods: visibility bridges class Qux { public final void bar() { /* do something */ } } public class Baz extends Qux { } new Baz().bar(); // works Baz.class.getMethod("bar").invoke(new Baz()); // access error
  • 37. Generic runtime types and dealing with meta data new ByteBuddy() .subclass(parameterizedType(Bar.class, String.class).build()) .method(named("foo")) .intercept(value("bar")) .attribute(ForInstrumentedMethod.INCLUDING_RECEIVER) .make(); @interface Foo {} interface Bar<T> { @Foo void foo(@Foo this, T arg); } Byte Buddy handles meta data and generic types transparently. Not only important for “build-time” instrumentation but also for meta data APIs.
  • 38. class $MethodProxy implements MethodProxy { @Override public Object invokeSuper(Object self, Method method, Object[] arguments) { if (method.equals($bar)) { ((Foo) self).bar((Integer) arguments[0]); return null; } else if (method.equals($qux)) { return ((Foo) self).qux((String) arguments[0]); } else { throw new IllegalStateException(); } } } Super method proxies: cglib interface Foo { void bar(int arg); String qux(String arg); }
  • 39. class $BarMethodProxy implements Runnable { final Foo self; final int arg; // constructor omitted @Override public void run() { self.bar(arg); } } Super method proxies: cglib interface Foo { void bar(int arg); String qux(String arg); } class $QuxMethodProxy implements Callable<String> { final Foo self; final String arg; // constructor omitted @Override public String call() { return self.bar(arg); } }
  • 40. Problems caused by “optional” types class SomeFramework { void doFrameworkStuff() { if (isFancyPluginAvailable()) { init(new OptionalPluginType()); } /* do work */ } private void init(OptionalPluginType plugin) { /* do work */ } } // causes NoClassDefFoundError SomeFramework.class.getMethods();
  • 41. Not requiring loaded types: an reflection API alternative interface TypeDescription interface MethodDescription interface FieldDescription interface AnnotationDescription When a class file transformer is applied, a class is not yet loaded and it is therefore not possible to access loaded types. To provide an API for matchers and transformers, Byte Buddy offers an abstraction to the Java reflection API: Any reference of such a description is resolved lazily. This also allows working with optional types. The Byte Buddy agent builder offers an automatic fallback as using loaded types is more performant.