Welcome on the first part of this introduction to the Java Virtual Machine !
In this introduction, we will discover how the Java Virtual Machine works, its structure, how programs are loaded and executed, the Java bytecode and what are class files.
This introduction expects that you know how to program in Java. Some background with executable and bytecode can help you to grasp faster some concepts but this is not required.
But first, what is the Java Virtual Machine ?
What is the Java Virtual Machine ?
#NOTAPROGAM
The Java Virtual Machine (JVM) is a specification that describes a computer architecture that enables its user to run programs written in languages that compiles to Java bytecode (like Java, Kotlin, Clojure etc…).
Meaning: it is just a paper that describes how class files (ending with .class) are loaded, how they are executed and in what environment. Meaning: This is not a program by itself.
You can see the JVM as a RFC (Request For Comments): a paper on how it should work.
Programs that you have on your computer that are called “JVM” are implementations of the JVM; the most known implementations are HotSpot from Oracle and OpenJDK from OpenJDK.
Overview
The JVM is made of multiple parts, we won’t look at them in detail in this article but here is a good graphical overview (from Wikipedia) :
Just to summarize:
- The Class Loader is responsible to load the content from class files, linking (loading other classes used in the file for example) and initializing the static variables contained in the file.
- The JVM memory contains everything that the executable will need to run properly (like code, constants, variables in the current method etc…)
- The Execution Engine is simply the interpreter, the JIT compiler (if there is one) and the garbage collector
- The Native Method Interface (also known as JNI) is an interface that gives the ability to the JVM to execute code written in C or C++ for example
- Native Method Libraries are code written in C, C++ or other native programming languages
I will explain all these parts in future articles as each parts are important and complex.
Note that in this introduction, I will be using the JVM 16 specification available here. The implementation I will be using doesn’t matter as the JVM is just a specification, but just to let you know: openjdk version “16” 2021-03-16.
The javap command and ASM.jar
To visualize what is inside a class file, we will need tools.
That’s where the javap command and the ASM library come into play.
You can try these tools on this class file compiled from this file. Note that they are purely test files and not meant to do something useful at all.
Javap
Javap is a command to disassemble class files. It is part of JDK (Java Development Kit).
You can use it like this:
javap -c -v Test.class
-c: disassemble the code
-v: disassemble the methods (stack size, number of locals and arguments)
You can find the documentation here.
ASM
ASM is “a java bytecode manipulation tool”. It is a Java library that enables you to load and write class files, get, modify and create methods and their codes and get various metadata from the file.
Here an example that uses this library, it that take a path to a class file as argument and gives some information about it:
package dev.aeonn.jvmintroduction;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class App {
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("You need to specify a .class file");
return ;
}
ClassReader clsReader;
try {
clsReader = new ClassReader(new FileInputStream(new File(args[0])));
} catch (FileNotFoundException e) {
System.err.println("Could not find " + args[0]);
return ;
} catch (IOException e) {
e.printStackTrace();
return ;
}
ClassNode classNode = new ClassNode();
clsReader.accept(classNode, 0);
System.out.println("Source code: " + classNode.sourceFile);
System.out.println("Name: " + classNode.name);
System.out.println("Access: " + classNode.access);
System.out.println("----");
System.out.println("\nMethodes:");
for(MethodNode method : classNode.methods) {
System.out.println("\t" + method.name + " " + method.desc);
}
System.out.println("----");
System.out.println("\nAttributes:");
if(classNode.attrs != null) {
for(Attribute attr : classNode.attrs) {
System.out.println("\t" + attr.type);
}
} else {
System.out.println("\t<empty>");
}
System.out.println("----");
System.out.println("\nFields:");
if(classNode.fields != null) {
for(FieldNode field: classNode.fields) {
System.out.println("\t" + field.access + " " + field.name + " " + field.desc);
}
} else {
System.out.println("<empty>");
}
System.out.println("----");
}
}
To help you throughout this JVM introduction, here is a maven project configurated with ASM and the example just above.
If you are not familiar with maven, here is how to use it:
- Download the archive
- Extract it and move into it
- Compile and execute:
mvn compile exec:java "-Dexec.arguments=Test.class"
You might have to change the version to compile into in the maven.compiler.source and maven.compiler.target tags in the pom.xml file (you can get the correct version from this command: java -version
).
Conclusion
Now you will have all the requirements and tools to begin learning how the JVM works !