The computer cannot run directly java code (.java), it must be compiled into bytecode (.class), then it can be run by the java virtual machine.
JVM not only supports Java, but also many programming languages, such as Groovy, Scala, Koltin …

What You Need
- About 10 minutes
- A favorite text editor or IDE
- Java 8 or later
Class File
The class file is essentially a binary stream based on 8-bit bytes, the jvm parses it according to its specific rules to obtain relevant information.
Below is a simple java file example :
1public class ByteCodeTest {
2 public static void main(String[] args) {
3 int a = 1;
4 int b = 2;
5 int c = add(a, b);
6
7 System.out.println(c);
8 }
9
10 private static int add(int a, int b) {
11 return a + b;
12 }
13}
We can use command javac to obtain its .class file :
ovo@ovo:~/github/BlogTests/java.jvm.bytecode$ javac ByteCodeTest.java
ovo@ovo:~/github/BlogTests/java.jvm.bytecode$ ls
ByteCodeTest.class ByteCodeTest.java
Open the generated class file in a hex editor or make a hexdump (xxd .class), the content is as follows :

The 4 bytes at the beginning of the file (CAFE BABE) are called magic number.
These 4 bytes are the identification of the bytecode file.
Only the class file starting with CAFE BABE can be accepted by the virtual machine.
The next 2 bytes are the major version number of the JDK that the class file is using.
Below is all the major version number of jdks :
JDK | MAJOR VERSION NUMBER |
---|---|
Java SE 17 | 61 (0x3D hex) |
Java SE 16 | 60 (0x3C hex) |
Java SE 15 | 59 (0x3B hex) |
Java SE 13 | 57 (0x39 hex) |
Java SE 12 | 56 (0x38 hex) |
Java SE 11 | 55 (0x37 hex) |
Java SE 10 | 54 (0x36 hex) |
Java SE 9 | 53 (0x35 hex) |
Java SE 8 | 52 (0x34 hex) |
Java SE 7 | 51 (0x33 hex) |
Java SE 6 | 50 (0x32 hex) |
Java SE 5 | 49 (0x31 hex) |
JDK 1.4 | 48 (0x30 hex) |
JDK 1.3 | 47 (0x2F hex) |
JDK 1.2 | 46 (0x2E hex) |
JDK 1.1 | 45 (0x2D hex) |
Let us now decompile this class file and continue to look at it.
We use the built-in Java decompilation tool javap :
javap -v -p -l ByteCodeTest.class
Below is the decompiled output :
1Classfile /home/yan/github/BlogTests/java.jvm.bytecode/ByteCodeTest.class
2 Last modified May 23, 2025; size 482 bytes
3 SHA-256 checksum 238817dfe5cc08380cb7936bc791b4be3afa105490471cb51d20330bbb722b03
4 Compiled from "ByteCodeTest.java"
5public class ByteCodeTest
6 minor version: 0
7 major version: 65
8 flags: (0x0021) ACC_PUBLIC, ACC_SUPER
9 this_class: #8 // ByteCodeTest
10 super_class: #2 // java/lang/Object
11 interfaces: 0, fields: 0, methods: 3, attributes: 1
12Constant pool:
13 #1 = Methodref #2.#3 // java/lang/Object."<init>":()V
14 #2 = Class #4 // java/lang/Object
15 #3 = NameAndType #5:#6 // "<init>":()V
16 #4 = Utf8 java/lang/Object
17 #5 = Utf8 <init>
18 #6 = Utf8 ()V
19 #7 = Methodref #8.#9 // ByteCodeTest.add:(II)I
20 #8 = Class #10 // ByteCodeTest
21 #9 = NameAndType #11:#12 // add:(II)I
22 #10 = Utf8 ByteCodeTest
23 #11 = Utf8 add
24 #12 = Utf8 (II)I
25 #13 = Fieldref #14.#15 // java/lang/System.out:Ljava/io/PrintStream;
26 #14 = Class #16 // java/lang/System
27 #15 = NameAndType #17:#18 // out:Ljava/io/PrintStream;
28 #16 = Utf8 java/lang/System
29 #17 = Utf8 out
30 #18 = Utf8 Ljava/io/PrintStream;
31 #19 = Methodref #20.#21 // java/io/PrintStream.println:(I)V
32 #20 = Class #22 // java/io/PrintStream
33 #21 = NameAndType #23:#24 // println:(I)V
34 #22 = Utf8 java/io/PrintStream
35 #23 = Utf8 println
36 #24 = Utf8 (I)V
37 #25 = Utf8 Code
38 #26 = Utf8 LineNumberTable
39 #27 = Utf8 main
40 #28 = Utf8 ([Ljava/lang/String;)V
41 #29 = Utf8 SourceFile
42 #30 = Utf8 ByteCodeTest.java
43{
44 public ByteCodeTest();
45 descriptor: ()V
46 flags: (0x0001) ACC_PUBLIC
47 Code:
48 stack=1, locals=1, args_size=1
49 0: aload_0
50 1: invokespecial #1 // Method java/lang/Object."<init>":()V
51 4: return
52 LineNumberTable:
53 line 1: 0
54
55 public static void main(java.lang.String[]);
56 descriptor: ([Ljava/lang/String;)V
57 flags: (0x0009) ACC_PUBLIC, ACC_STATIC
58 Code:
59 stack=2, locals=4, args_size=1
60 0: iconst_1
61 1: istore_1
62 2: iconst_2
63 3: istore_2
64 4: iload_1
65 5: iload_2
66 6: invokestatic #7 // Method add:(II)I
67 9: istore_3
68 10: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
69 13: iload_3
70 14: invokevirtual #19 // Method java/io/PrintStream.println:(I)V
71 17: return
72 LineNumberTable:
73 line 3: 0
74 line 4: 2
75 line 5: 4
76 line 7: 10
77 line 8: 17
78
79 private static int add(int, int);
80 descriptor: (II)I
81 flags: (0x000a) ACC_PRIVATE, ACC_STATIC
82 Code:
83 stack=2, locals=2, args_size=2
84 0: iload_0
85 1: iload_1
86 2: iadd
87 3: ireturn
88 LineNumberTable:
89 line 11: 0
90}
91SourceFile: "ByteCodeTest.java"
Global Information
The first section (line 1 – line 11) represents the global information of the class file, it includes below lines :
Line 1 – The current location of the Class file
Line 2 – The last modification time and the file size
Line 3 – The MD5 value
Line 4 – From which file it was compiled
Line 5 – The fully qualified name of the class
Line 6 – The jdk minor version number
Line 7 – The jdk major version number
Line 8 is the access flags :
NAME | VALUE | DESCRIPTION |
---|---|---|
ACC_PUBLIC | 0x0001 | if it is public, may be accessed from outside its package |
ACC_FINAL | 0x0010 | if it is final, no subclasses allowed |
ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the invokespecial instruction, it was introduced to correct a problem with the invocation of super methods |
ACC_INTERFACE | 0x0200 | if it is an interface, not a class, if this flag is not set, then it indicates that it is a class, not an interface, otherwise ACC_ABSTRACT flag must also be set, and it must not have ACC_FINAL, ACC_SUPER or ACC_ENUM flags set |
ACC_ABSTRACT | 0x0400 | if it is abstract, may not be instantiated |
ACC_SYNTHETIC | 0x1000 | if it was generated by a compiler and does not appear in source code |
ACC_ANNOTATION | 0x2000 | if it is annotation, if this flag is set, the ACC_INTERFACE flag must be set as well and it cannot have both ACC_FINAL and ACC_ABSTRACT flags set |
ACC_ENUM | 0x4000 | if it is an enum |
Line 9 is this_class : it is the class name and its value must be a valid index into the constant_pool table.
Line 10 is super_class : it is the super class of the class file and its value must be a valid index into the constant_pool table. If its value is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.
Line 11 contains the count of interfaces, fields, methods and attributes of the class file.
Constant Pool
The second section (line 13 – line 42) is constant pool.
There are two main types of constants: literal (Literal) and symbolic reference (Symbolic References).
Literals are similar to constant concepts in java, such as text strings, final constants etc.
Symbolic references belong to the concept of compilation principles, including the following :
- Fully Qualified Name of classes and interfaces;
- Field name and descriptor;
- Method name and descriptor.
JVM performs dynamic linking when loading the Class file, which means that these field and method symbol references can only obtain the real memory entry address after the runtime conversion.
When the virtual machine is running, it needs to obtain the corresponding symbol reference from the constant pool, and then parse and translate it into the specific memory address when the class is created or run.
Let us have a look at the first constant in the pool.
It is a method reference whose value points to constant 2 and 3 which are separated by a comma.
And so on for the constant 2 (points to constant 4) and 3 (points to constant 5 and 6).
Finally, it obtains the comment content on the right side of the first constant :
13 #1 = Methodref #2.#3 // java/lang/Object."<init>":()V
14 #2 = Class #4 // java/lang/Object
15 #3 = NameAndType #5:#6 // "<init>":()V
16 #4 = Utf8 java/lang/Object
17 #5 = Utf8 <init>
18 #6 = Utf8 ()V
It is the declaration of the instance constructor of this class.
Since this class does not override any constructor, the constructor of the parent class (Object) is called.
The default return value of this method is V, which is void, with no return value.
V is also called bytecode type.
Below is a list of bytecode types :
CHARACTER | DESCRIPTION |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
S | short |
Z | boolean |
V | void |
L<classname>; | Reference, an instance of class <classname>, e.g. Ljava/lang/object; |
[ | Array, for instance, to define a java.lang.String[][], it will be [[Ljava/lang/String; |
Methods
Next section (line 43 – line 90) is the description of the methods inside the class.
For instance, below is the source code of method add:
private static int add(int a, int b) {
return a + b;
}
Below is bytecode description of this method add:
79 private static int add(int, int);
80 descriptor: (II)I
81 flags: (0x000a) ACC_PRIVATE, ACC_STATIC
82 Code:
83 stack=2, locals=2, args_size=2
84 0: iload_0
85 1: iload_1
86 2: iadd
87 3: ireturn
88 LineNumberTable:
89 line 11: 0
Line 79 is the signature of this method including the access flag and return type.
Line 80 is method descriptors which represents the parameters that the method takes and the value that it returns.
MethodDescriptor: ( ParameterDescriptor* ) ReturnDescriptor
For this add method, it has two integer parameters and it returns an integer value, so its method descriptior is (II)I.
Line 81 is the access flags of this method.
From line 82, it is the Code Attribute block.
It includes below fields :
FIELD | DESCRIPTION |
---|---|
stack | the maximum depth of the operand stack |
locals | the number of local variables in the local variable array allocated upon invocation of this method, including the local variables used to pass parameters to the method on its invocation |
args_size | the number of method parameters, for non static method, the default number is 1, because each instance method will have a hidden parameter this |
attribute_info | the jvm instructions of the current method, line 84 and line 85 means the local variable at 0 and 1 are pushed onto the operand stack, line 86 means the two int values are poped from stack, then an integer add of the two values is processed and the result is pushed back to stack, line 87 means the result value is popped from the operand stack of the current frame and pushed onto the operand stack of the frame of the invoker, any other values on the operand stack of the current method are discarded |
LineNumberTable | describe the correspondence between source line numbers and bytecode line numbers |
Similarly, other methods in the class can be analyzed the same way.
Attributes
Last section is the attributes table of the class.
Here line 91 is SourceFile attribute whose value is the name of the code source file of the current class.
91 SourceFile: "ByteCodeTest.java"