Introduction
Many of us, be it we are Java developers or not, use software made with Java, and it is everywhere around us. The people that do write it notice something interesting: the code we write runs on every platform (meaning any operating system) that has the JDK (Java Development Kit). In this article I will be taking a general overview of how this is possible, and how our Java source code manages to work like it does.
The magic happens in the Java Virtual Machine (JVM), which is the cornerstone of the Java ecosystem. It is, from my personal perspective, what makes Java so great and it is not as mentioned as it should be. It is Java's true selling point.
Some clarifications and introductory definitions
This article will be just the starting point, since we will be going through all of the most important parts of the JVM. Another important mention is that this series of articles regarding the JVM will be catered towards The HotSpot JVM, since it is the standard implementation.
Before we go further, a quick distinction for clarity: the JDK (Java Development Kit) is the toolbox that contains the Java Compiler (javac), debugger, and other utilities needed to write code. The JVM is the engine inside of this toolbox that actually runs the code we have written. This series will focus entirely on that engine.
How I got into reading about this?
When I first encountered Java I did not really think that much about how the code ran. Eclipse did its thing, I pressed the green button to build and run, and it did its job. After some time I got curious about the specific differences between statically and dynamically typed languages, as well as weakly and strongly typed languages, and how they differ from each other. After some reading came the concept of runtime engines and JIT (Just-In-Time) compilers. At the time both of these were loaded terms, so I had to slow down and read through some definitions. The first term I was met with was The Virtual Machine. Many languages implement this concept and in different ways. That is when the term "Java Virtual Machine" kind of clicked for me and I got the basic idea of how it worked.
That "click" moment came from understanding the Java Virtual Machine not as a mysterious black box, but as a system of distinct, cooperating parts. Once I saw the blueprint, everything made sense.
Without further ado, let's get into some definitions of what a virtual machine is, what it does, and what its parts are.
What Exactly Is a Virtual Machine?
In the broadest sense, a Virtual Machine (VM) is a software emulator of a physical computer. The word emulate means to mimic. Basically, a Virtual Machine mimics through software the way in which a physical computer (hardware) thinks. This is useful because computers are expensive, especially certain parts today (RAM)...
With this definition we know this much: Virtual Machines let us run many mimicked "machines" on one physical computer.
This exact type of Virtual Machines is called System Virtualization, where we for example mimic a Linux machine on our Windows physical machine. This is done through software such as Oracle VirtualBox, a desktop application with a window, inside of which there is an operating system we want to mimic.
These Virtual Machines are really heavy, and they are great tools, but they are not the center of attention in this series. What we really want to talk about are Process Virtual Machines.
When we run a Java program, we aren't spinning up an entire operating system in a separate window, what we are doing is basically just running a regular program on our computer. Since this is not a heavy computational task, at least in comparison to running an entire operating system, we would like to mimic a smaller unit of execution, and that would be a process, to be precise, an operating system's process. That is where the word process in Process Virtual Machines comes from.
So to recap and avoid any confusion: the Java Virtual Machine is a Process Virtual Machine.
What Does a Process Virtual Machine Actually Do?
I will keep the answer short and simple, as we will see the nuances of this whole process as we go through the series.
A Process Virtual Machine takes our code written in any programming language that targets it, and translates it into a format that our computer's hardware can read and understand. That format depends exclusively on our computer's CPU Architecture (that will be an article for the future). The magic of this engine is that it can translate the same original code we wrote and make it run on almost any CPU out there.
So, What's Inside This Process Virtual Machine (JVM)?
After this proper literary introduction, it is time to do some heavy lifting and check out what the JVM has to offer and what the gears inside of this engine are!
Disclaimer: I am going to separate each major part of the Java Virtual Machine into a separate article, which will be linked inside of this main introductory article. That way we can have an organized series and we don't have to suffer through scrolling one large article for all the information.
Let's Begin!
I have organized this series into five core chapters, each covering a specific part of the JVM. You can click on the title of the chapter you want to read and you will jump to that specific article.
Part 1: The Class Loader Subsystem
The Customs Agent. Before any code runs, the JVM must find it, verify it, and bring it into memory. We'll explore the delegation hierarchy (Bootstrap, Extension, Application) and answer the question: Why do I get a ClassNotFoundException?
Part 2: Runtime Data Areas — The Memory Model
The Blueprint. Where do objects, primitives, and static variables actually live? We'll draw a map of the Heap, Stack, and Metaspace. This is the chapter that finally explains the difference between a StackOverflowError and an OutOfMemoryError.
Part 3: The Execution Engine
The Translator. How does a .class file full of bytecode turn into fast machine code? We'll meet the Interpreter and the Just-In-Time (JIT) Compiler, and watch how "hot" code gets optimized on the fly.
Part 4: Garbage Collection Visualized
Our Favorite Janitor. The JVM cleans up after us so we don't have to manage memory manually. We'll visualize Reachability Analysis, the Generational Hypothesis, and the core algorithms (Mark-Sweep, Mark-Copy, Mark-Compact) with step-by-step diagrams.
Part 5: Concurrency & The Java Memory Model (JMM)
The Rules of Engagement. There's a huge difference between where data is stored (JVM Memory) and how threads see it (JMM). We'll demystify volatile, synchronized, and why count++ isn't safe across multiple threads.