Core Java

ByteCode primer for Java Class Files

Photo of JJJJFebruary 6th, 2018Last Updated: February 6th, 2018
0 223 12 minutes read

It is often stated that to be good at the programming language you code in, one should at least dig and understand one abstraction level lower than the one in which you are coding. For Java, that would be the JVM, which would imply byte code.

In this article we will kick start our adventures in Java byte code. Byte code makes it possible for Java applications to run on varying hardware architectures. Very often we ignore the byte code layer. Understanding it just a little, can go a long way to help us write better Java code.

In this article we will also look at some snippets of Java code and examine their byte code equivalents giving us some insight into what runs under the hood.

1. Introduction

If someone were to ask you, is Java a compiled language or an interpreted language, what would your answer be? Difficult to answer considering it could be seen as both.

Both being the fact that the code we write is initially compiled into JVM byte code to  beinterpreted  by the JVM at run time. At times the code can also occasionally be compiled into machine code,  by theJIT compiler , when deemedhot.

In this article I hope to stimulate some curiosity into what actually happens at a byte code level. For a listing of the different JVM byte codes seehere.

2. Technologies used

The example code in this article was built and run using:

    • Java 8
    • Maven 3.3.9
    • STS (3.9.0.RELEASE)
    • Ubuntu 16.04
    • xxd

3. Setup

For this article, the examples will make use of thejavap andxxd command line tools so in addition to ensuring that Java and maven are installed we need to ensure thejavap andxxd tools are accessible on the command line as well.

Confirm Java, Maven and JavaP

$ java -versionjava version "1.8.0_101"Java(TM) SE Runtime Environment (build 1.8.0_101-b13)Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)$ mvn -versionApache Maven 3.3.9Maven home: /usr/share/mavenJava version: 1.8.0_101, vendor: Oracle CorporationJava home: /home/jean-jay/runtimes/jdk1.8.0_101/jreDefault locale: en_ZA, platform encoding: UTF-8OS name: "linux", version: "4.13.0-26-generic", arch: "amd64", family: "unix"$ javap -version1.8.0_151$ xxd -versionxxd V1.10 27oct98 by Juergen Weigert

4. Stack Machines

The JVM is astack machine based virtual machine which helps underscore the design philosophy of the JVM.

Because the JVM was designed to be as platform as possible and initially was meant to have as small a footprint as possible (think applets over the wire), a stack machine was used.

This went a long way to facilitating the design goals of the JVM by virtue of having a small(ish) instruction set (compact to send over the wire) and making no assumptions about the underlying hardware architecture (platform).

4.1 How do they work

The JVM uses a LIFO stack for it’s operands and instructions. Some operands are pushed directly on to the stack whereas others are referred to from the classconstant pool or variable array

In any event the next instruction is always the next item to be popped from the stack making it an extremely simple mechanism upon which the JVM operates.

The operand stack is a 32 bit word sized instruction stack meaning each instruction / operand on the stack can be at most 32 bits. This means that operands / instructions that exceed 32 bits (thinklong ordouble) will take up 2 slots on the stack.

 

Stack Machine Functioning

In the above diagram we can see from left to right the sequence of instructions that unfold on the JVM stack when we add two integer values.

iconst_1 andiconst_2 represent the opcodes of pushing the integer value1and2 respectively on the stack.iadd is the opcode that pops the 2 top values from the stack and adds them and pushes the result back on the stack.

For more detail on how a stack machine works seehere but this should be enough to illustrate the concept so that we can continue further with the “nitty gritty” of analyzing our byte code from a class file.

5. Javap and xxd

javap is a command line tool that comes with the JDK. To be able to view the byte code output / instructions of our java code we will use thejavap tool to disassemble a Java class file.

For more detail on the tool itself and the various command line options seehere.

For the purposes of this article, particularly for the inspection of the byte code we will be using the following command line arguments withjavap

  • -c : Prints disassembled code (byte code)
  • -l : Prints line and local variable tables
  • -v : Prints verbose / additional information

xxd is the command line tool to create a HEX dump of a file. Using it is as simple ascat <filename> | xxd. This will allow us to view the hex output of the class file.

6. Disassembled Output

Before getting stuck into the “nitty gritty” of the byte code, a brief preface of theclass file structure is required.

Class file structure

ClassFile {    u4             magic;    u2             minor_version;    u2             major_version;    u2             constant_pool_count;    cp_info        constant_pool[constant_pool_count-1];    u2             access_flags;    u2             this_class;    u2             super_class;    u2             interfaces_count;    u2             interfaces[interfaces_count];    u2             fields_count;    field_info     fields[fields_count];    u2             methods_count;    method_info    methods[methods_count];    u2             attributes_count;    attribute_info attributes[attributes_count];}

A short description of some of these attributes follow:

  • In the beginning of the class file we havemagic which takes up 4 bytes and this is represented by the hex value0xCAFEBABE which has a very interestingstory.
  • u[0-9] : meansunsigned and the number represents bytes sou4 would be unsigned 4 bytes. This would mean that we could deduce the index in the class file of where certain structures start and end. For example from32 bits up until48 bits we can find the minor version information for the class file. Inspecting this in a hex editor will reveal it.
  • access_flags represents the access modifiers of the class itself
  • this_class represents the index in the constant pool which contains the fully qualified class name of this class
  • interfaces[interfaces_count] represents an array of indexes in the constant pool of all the interfaces that this class implements
  • fields[field_count] represents an array of indexes of in the constant pool representing a description of each field

Below follows the disassembled output (byte code) of thePerson class using thejavap command line tool. To generate this output do the following:

  1. Download the sample project and navigate to the project root folder once uncompressed.
  2. Build the project:mvn clean install package
  3. Navigate to <project root folder>/target/classes/com/javacodegeeks/bytecode_primer and executejavap -c -l -v Person.class. This will generate the disassembled output of the class as shown below.

Disassembled Output of Person class

Classfile /home/jean-jay/Documents/github-projects/codegeeks/bytecode-primer/target/classes/com/javacodegeeks/bytecode_primer/Person.class  Last modified 29 Jan 2018; size 910 bytes  MD5 checksum a2f21e47c5dabe433049d1e4c515fdf1  Compiled from "Person.java"public final class com.javacodegeeks.bytecode_primer.Person  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPERConstant pool:   #1 = Methodref          #5.#27         // java/lang/Object."<init>":()V   #2 = Fieldref           #8.#28         // com/javacodegeeks/bytecode_primer/Person.name:Ljava/lang/String;   #3 = Fieldref           #8.#29         // com/javacodegeeks/bytecode_primer/Person.age:I   #4 = String             #30            // [name %s\t : age %d]   #5 = Class              #31            // java/lang/Object   #6 = Methodref          #32.#33        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   #7 = Methodref          #34.#35        // java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;   #8 = Class              #36            // com/javacodegeeks/bytecode_primer/Person   #9 = Utf8               name  #10 = Utf8               Ljava/lang/String;  #11 = Utf8               age  #12 = Utf8               I  #13 = Utf8               <init>  #14 = Utf8               (Ljava/lang/String;I)V  #15 = Utf8               Code  #16 = Utf8               LineNumberTable  #17 = Utf8               LocalVariableTable  #18 = Utf8               this  #19 = Utf8               Lcom/javacodegeeks/bytecode_primer/Person;  #20 = Utf8               getName  #21 = Utf8               ()Ljava/lang/String;  #22 = Utf8               getAge  #23 = Utf8               ()I  #24 = Utf8               toString  #25 = Utf8               SourceFile  #26 = Utf8               Person.java  #27 = NameAndType        #13:#37        // "<init>":()V  #28 = NameAndType        #9:#10         // name:Ljava/lang/String;  #29 = NameAndType        #11:#12        // age:I  #30 = Utf8               [name %s\t : age %d]  #31 = Utf8               java/lang/Object  #32 = Class              #38            // java/lang/Integer  #33 = NameAndType        #39:#40        // valueOf:(I)Ljava/lang/Integer;  #34 = Class              #41            // java/lang/String  #35 = NameAndType        #42:#43        // format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;  #36 = Utf8               com/javacodegeeks/bytecode_primer/Person  #37 = Utf8               ()V  #38 = Utf8               java/lang/Integer  #39 = Utf8               valueOf  #40 = Utf8               (I)Ljava/lang/Integer;  #41 = Utf8               java/lang/String  #42 = Utf8               format  #43 = Utf8               (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;{  public com.javacodegeeks.bytecode_primer.Person(java.lang.String, int);    descriptor: (Ljava/lang/String;I)V    flags: ACC_PUBLIC    Code:      stack=2, locals=3, args_size=3         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: aload_0         5: aload_1         6: putfield      #2                  // Field name:Ljava/lang/String;         9: aload_0        10: iload_2        11: putfield      #3                  // Field age:I        14: return      LineNumberTable:        line 8: 0        line 9: 4        line 10: 9        line 11: 14      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      15     0  this   Lcom/javacodegeeks/bytecode_primer/Person;            0      15     1  name   Ljava/lang/String;            0      15     2   age   I  java.lang.String getName();    descriptor: ()Ljava/lang/String;    flags:    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #2                  // Field name:Ljava/lang/String;         4: areturn      LineNumberTable:        line 14: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       5     0  this   Lcom/javacodegeeks/bytecode_primer/Person;  int getAge();    descriptor: ()I    flags:    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #3                  // Field age:I         4: ireturn      LineNumberTable:        line 18: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       5     0  this   Lcom/javacodegeeks/bytecode_primer/Person;  public java.lang.String toString();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=5, locals=1, args_size=1         0: ldc           #4                  // String [name %s\t : age %d]         2: iconst_2         3: anewarray     #5                  // class java/lang/Object         6: dup         7: iconst_0         8: aload_0         9: getfield      #2                  // Field name:Ljava/lang/String;        12: aastore        13: dup        14: iconst_1        15: aload_0        16: getfield      #3                  // Field age:I        19: invokestatic  #6                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;        22: aastore        23: invokestatic  #7                  // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;        26: areturn      LineNumberTable:        line 23: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      27     0  this   Lcom/javacodegeeks/bytecode_primer/Person;}SourceFile: "Person.java"

The disassembled output above can be grouped into 3 main sections, the header section (containing version information), the constant pool section and the methods.

6.1 Header section

In the header section we have the version information which indicates the class format version among other things. Thus the JVM can support the particular class file format if it’s version falls in the range minor:major of the JVM.

6.2 Constant pool

Per class / type a constant pool is managed containing data typically too big to store in byte code’s itself or that is used in multiple places in a class.

Sample byte code to understand constant pool lookup (construct an object)

0: aload_01: invokespecial #1                  // Method java/lang/Object."":()V4: aload_05: aload_16: putfield      #2                  // Field name:Ljava/lang/String;9: aload_010: iload_211: putfield      #3                  // Field age:I14: return      LineNumberTable:        line 8: 0        line 9: 4        line 10: 9        line 11: 14      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      15     0  this   Lcom/javacodegeeks/bytecode_primer/Person;            0      15     1  name   Ljava/lang/String;            0      15     2   age   I
  • line 1: an opcode that loads a reference onto the stack. The index part0 represents the index in the local variable table for the method / constructor (explained later), the first index is always the instance type itself. Remember the code being executed belongs to a method or constructor of the class. The local variable table is a table containing all the method / constructor arguments that were passed during invocation. Soaload_0 means load the first argument on the stack, the first argument in a variable table is always thethis reference.
  • line 2: represents an instance initialization method, it contains an index reference to the constant pool#1 and also consumes the top of the stack (from line 1 the reference type being constructed –this /Person) . If we navigate to index 1 in the constant pool above we see it refers to a method reference(#1)on classjava.lang.Object (#5)which is the constructor (<init>)(#27). So what happens is that we pass the reference from line 1 (Person) as argument for the instance initialization, meaning we construct aPerson instance.
  • line 3: we again load thePerson reference from the local variable table on to the stack. (aload_0)
  • line 4 we load the second argument in the local variable on to the stack, theString name variable
  • line 5:putfield pops the two top values from the stack (currentlyPerson and the name value) and then stores it in the reference denoted by the constant pool index#2
  • line 6: we again load thePerson reference from the local variable table on to the stack. (aload_0)
  • line 7: we load the third argument in the local variable table, the age value, this is an integer hence the opcode is slightly different,iload_2
  • line 8:putfield pops the top two values from the stack and stores the value of age in the constant pool reference at index#3.
  • line 9: because this is a constructor, hence no return value, we returnvoid hence the opcodereturn

Using this method of chaining the look ups we are able to infer the exact value (eventually) of a constant pool reference.

6.3 Method section

In the method sections we list the various methods / constructors for the class. Most of this was touched on in the section above. One feature provided for in the method section(s) is theLineNumberTable which is really just an aid to debuggers to help identify the line number in the code.

7. Hex Dump

Below follows a hex dump of the same class file from the sample project. To create a hex dump we use thexxd command line tool. This can be done by executing the following:

  1. Navigate to the project root folder and build the projectmvn clean install package
  2. Navigate to the<project root folder>/target/classes/com/javacodegeeks/bytecode_primer/
  3. Execute the following:cat Person.class | xxd

Hex dump of Person class

00000000: cafe babe 0000 0034 002c 0a00 0500 1b09  .......4.,......00000010: 0008 001c 0900 0800 1d08 001e 0700 1f0a  ................00000020: 0020 0021 0a00 2200 2307 0024 0100 046e  . .!..".#..$...n00000030: 616d 6501 0012 4c6a 6176 612f 6c61 6e67  ame...Ljava/lang00000040: 2f53 7472 696e 673b 0100 0361 6765 0100  /String;...age..00000050: 0149 0100 063c 696e 6974 3e01 0016 284c  .I......(L00000060: 6a61 7661 2f6c 616e 672f 5374 7269 6e67  java/lang/String00000070: 3b49 2956 0100 0443 6f64 6501 000f 4c69  ;I)V...Code...Li00000080: 6e65 4e75 6d62 6572 5461 626c 6501 0012  neNumberTable...00000090: 4c6f 6361 6c56 6172 6961 626c 6554 6162  LocalVariableTab000000a0: 6c65 0100 0474 6869 7301 002a 4c63 6f6d  le...this..*Lcom000000b0: 2f6a 6176 6163 6f64 6567 6565 6b73 2f62  /javacodegeeks/b000000c0: 7974 6563 6f64 655f 7072 696d 6572 2f50  ytecode_primer/P000000d0: 6572 736f 6e3b 0100 0767 6574 4e61 6d65  erson;...getName000000e0: 0100 1428 294c 6a61 7661 2f6c 616e 672f  ...()Ljava/lang/000000f0: 5374 7269 6e67 3b01 0006 6765 7441 6765  String;...getAge00000100: 0100 0328 2949 0100 0874 6f53 7472 696e  ...()I...toStrin00000110: 6701 000a 536f 7572 6365 4669 6c65 0100  g...SourceFile..00000120: 0b50 6572 736f 6e2e 6a61 7661 0c00 0d00  .Person.java....00000130: 250c 0009 000a 0c00 0b00 0c01 0013 5b6e  %.............[n00000140: 616d 6520 2573 0920 3a20 6167 6520 2564  ame %s. : age %d00000150: 5d01 0010 6a61 7661 2f6c 616e 672f 4f62  ]...java/lang/Ob00000160: 6a65 6374 0700 260c 0027 0028 0700 290c  ject..&..'.(..).00000170: 002a 002b 0100 2863 6f6d 2f6a 6176 6163  .*.+..(com/javac00000180: 6f64 6567 6565 6b73 2f62 7974 6563 6f64  odegeeks/bytecod00000190: 655f 7072 696d 6572 2f50 6572 736f 6e01  e_primer/Person.000001a0: 0003 2829 5601 0011 6a61 7661 2f6c 616e  ..()V...java/lan000001b0: 672f 496e 7465 6765 7201 0007 7661 6c75  g/Integer...valu000001c0: 654f 6601 0016 2849 294c 6a61 7661 2f6c  eOf...(I)Ljava/l000001d0: 616e 672f 496e 7465 6765 723b 0100 106a  ang/Integer;...j000001e0: 6176 612f 6c61 6e67 2f53 7472 696e 6701  ava/lang/String.000001f0: 0006 666f 726d 6174 0100 3928 4c6a 6176  ..format..9(Ljav00000200: 612f 6c61 6e67 2f53 7472 696e 673b 5b4c  a/lang/String;[L00000210: 6a61 7661 2f6c 616e 672f 4f62 6a65 6374  java/lang/Object00000220: 3b29 4c6a 6176 612f 6c61 6e67 2f53 7472  ;)Ljava/lang/Str00000230: 696e 673b 0031 0008 0005 0000 0002 0012  ing;.1..........00000240: 0009 000a 0000 0012 000b 000c 0000 0004  ................00000250: 0001 000d 000e 0001 000f 0000 0059 0002  .............Y..00000260: 0003 0000 000f 2ab7 0001 2a2b b500 022a  ......*...*+...*00000270: 1cb5 0003 b100 0000 0200 1000 0000 1200  ................00000280: 0400 0000 0800 0400 0900 0900 0a00 0e00  ................00000290: 0b00 1100 0000 2000 0300 0000 0f00 1200  ...... .........000002a0: 1300 0000 0000 0f00 0900 0a00 0100 0000  ................000002b0: 0f00 0b00 0c00 0200 0000 1400 1500 0100  ................000002c0: 0f00 0000 2f00 0100 0100 0000 052a b400  ..../........*..000002d0: 02b0 0000 0002 0010 0000 0006 0001 0000  ................000002e0: 000e 0011 0000 000c 0001 0000 0005 0012  ................000002f0: 0013 0000 0000 0016 0017 0001 000f 0000  ................00000300: 002f 0001 0001 0000 0005 2ab4 0003 ac00  ./........*.....00000310: 0000 0200 1000 0000 0600 0100 0000 1200  ................00000320: 1100 0000 0c00 0100 0000 0500 1200 1300  ................00000330: 0000 0100 1800 1500 0100 0f00 0000 4500  ..............E.00000340: 0500 0100 0000 1b12 0405 bd00 0559 032a  .............Y.*00000350: b400 0253 5904 2ab4 0003 b800 0653 b800  ...SY.*......S..00000360: 07b0 0000 0002 0010 0000 0006 0001 0000  ................00000370: 0017 0011 0000 000c 0001 0000 001b 0012  ................00000380: 0013 0000 0001 0019 0000 0002 001a       ..............

8. Summary

In this article we covered what byte code is and what role it plays in making your Java program work.

We briefly touched upon the JVM as a stack machine and how it functions when executing our code instructions. To make the concept more tangible we also analyzed the byte code generated by a simple class in the sample project.

We analysed these byte code listings to understand how it is interpreted by the JVM.

9. Download the Source Code

This was a ByteCode primer for Java Class Files.

Download
You can download the full source code of this example here:ByteCode primer for Java Class Files
Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Tags
Photo of JJJJFebruary 6th, 2018Last Updated: February 6th, 2018
0 223 12 minutes read
Photo of JJ

JJ

Jean-Jay Vester graduated from the Cape Peninsula University of Technology, Cape Town, in 2001 and has spent most of his career developing Java backend systems for small to large sized companies both sides of the equator.He has an abundance of experience and knowledge in many varied Java frameworks and has also acquired some systems knowledge along the way.Recently he has started developing his JavaScript skill set specifically targeting Angularjs and also bridged that skill to the backend with Nodejs.

Related Articles

Bipartite Graph

Java not equal Example

January 17th, 2020
Bipartite Graph

Java API Tutorial

October 26th, 2020
Bipartite Graph

Java Struct Example

January 8th, 2020
Bipartite Graph

Java Node Example

November 20th, 2019
Bipartite Graph

Java Swing MVC Example

January 26th, 2016
Bipartite Graph

How to call a method in Java

December 26th, 2019
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.