Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

10 Reasons Why Java Rocks More Than Ever: Part 6 – Bytecode

mastering java bytecode

In many ways, bytecode is what made me explore alternative approaches in the Java world. Without bytecode, my RIFE framework wouldn’t have existed. I wouldn’t have created the first implementation of continuations in Java, I wouldn’t have worked on the meta-data model that RIFE introduced and … I wouldn’t have been elected as a Java champion.

So remember, be good to The Bytecode and The Bytecode will be good to you!

Obviously, bytecode is very much part of why I think that Java rocks, as a reminder here are my top 10 favorite things about the Java platform again:

  1. The Java Compiler
  2. The Core API
  3. Open-Source
  4. The Java Memory Model
  5. High-Performance JVM
  6. Bytecode
  7. Intelligent IDEs
  8. Profiling Tools
  9. Backwards Compatibility
  10. Maturity With Innovation

What rocks about Bytecode?

You most certainly know that bytecode allows the Java compiler to express instructions in a format that is directly understood by the Java Virtual Machine.

Some people compare bytecode to assembly, but I found that it’s nowhere near as complicated. Thankfully you get all the goodness of the JVM and its verifier, drastically reducing the potentially harmful instructions that you can write. Once you overcome the initial learning curve, you’ll find that when using a good tool like ASM, generating and manipulating bytecode is not that different from interacting with the structure of the Java language itself. You typically start with Java source code you know and then visualize what it would look like in the ASM API. This makes it very convenient to begin work from a familiar place and then gradually incorporate the new bytecode logic that you need.

Even if you don’t use tools like ASM, the Java platform has actually everything included to allow you to inspect the bytecode of any class. Let’s take a quick look, for instance this very complex piece of Java code:

public class HelloWorld {
  public HelloWorld() {
  }
 
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
}

It will look like this in bytecode after using the JDK’s javap command:

javap -p -c HelloWorld

public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1  // Method java/lang/Object."<init>":()V
       4: return
 
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3  // String Hello World!
       5: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

Looks quite familiar, doesn’t it?

Bytecode Manipulation changed the Java platform

Since it’s so close to Java, you might wonder why you’d want to get interested in bytecode manipulation and generation. Well, in fact you’ve most certainly been using it all over the place. Since the JVM can modify bytecode and use new bytecode while it is running, this generated a whole universe of languages and tools that by far surpasses the initial intent of the Java language.

Some examples are:

  • FindBugs inspects bytecode for static code analysis
  • languages like Groovy, Scala, Clojure generate bytecode from different source code
  • ORM tools like Hibernate can instrument your setters and getters to automatically perform the required database operations
  • dependency injection frameworks like Spring use it to seamlessly weave your application lifecycle together
  • language extensions like AspectJ can augment the capabilities of Java by modifying the classes that the Java compiler generated
  • ZeroTurnaround’s tool JRebel instruments your classes so that it can instantly reload changes without you having to go through lengthy compile and redeploy cycles

Your imagination is the limit

The Java platform provides you with many ways to work with bytecode, for instance:

  • You can write your own compiler for a language you come up with
  • You can write a static transformer that pre-processes existing classes as part of the build process
  • You can write an instrumentation agent that plugs right into the JVM and performs bytecode manipulation on-the-fly
  • You can write custom classloaders but that’s nowadays strongly discouraged

With so many options available, one of these will certainly fit any experiment that you want to play around with. The goodness comes from the fact that you get the whole of the JVM for free and that you can slot in your idea exactly where it’s needed while reusing the rest of the Java platform.

This is what excites me most, as a developer you can really focus on your crazy idea that is not supported by the Java language, but you don’t have to write an entire platform to make it come to life. Certainly this has been one of the key areas why the Java community has constantly been experimenting with new ways to push the programming toolset further.

Further reading and resources

When writing this blog post I remembered that I actually gave a few talks in 2008 and 2009 about bytecode manipulation best-practices. The slides are slide available from one of my blogs, as well as the abstract.

A great in-depth talk about bytecode manipulation is done by my colleague Anton and you can find the slides on SlideShare, plus I recommend checking out the full-length report by RebelLabs on Java bytecode:

Check out 'Mastering Java Bytecode' by Anton Arhipov

Do you think that bytecode manipulation is still a hot topic? What kind of topics would you like to see us cover about bytecode manipulation? Are there any cool projects that we should be aware of that push the boundaries of the Java world even further with these techniques? Leave your comments below, and ping me at @gbevin or @rebellabs with feedback :-)

  • http://ruedigermoeller.github.io/ Rüdiger Möller

    Why are custom-classloaders discouraged and how to replace that functionaility ?

  • gbevin

    It’s very hard to get the classloader hierarchy right and to ensure that there’s only one authoritative source of resource loading and retrieval. Java agents should be a good replacement for almost all use-cases that class loaders were traditionally used for. The only real case for a custom classloader is if you really create custom classes from scratch, probably by loading them for a dedicated location. You can find a lot of details in our dedicated report about classloaders: http://zeroturnaround.com/rebellabs/rebel-labs-tutorial-do-you-really-get-classloaders/