Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Architecture and operations

Processes

What is a process, and what is separate when you run a program twice?

Counter.class, made by compiling a program, is a file stored on disk. When you run this file with the command java Counter, something that is running comes into being. That is a process. The operating system gives each process a number (PID) and gives it memory that only that process uses. In the code, static int count is a variable in that memory, and add() raises count by 3 and then prints the PID of this process and count. The flow that runs code inside a process is called a thread. In this lab you run the same Counter.class in two terminals, run it again after it ends, or run it once and let threads inside it run add one after another. You see how many processes are created and what count each one saw. (A program such as a web server is also one process when you run it with the java command. static is not covered in this lab.)

Checked against real execution · Ran all six combinations as real javac and java processes on JDK 21 and checked against the results

Concepts in this lab

Programs and processes
A program is a file stored on disk, and a process is that file being run. In Java, Counter.class made by javac is a program file, and the command java Counter makes the operating system create one process that runs that file. Even if there is 1 file, one process is created each time you run it, so running the same file twice gives two processes. A process exists only while it is running, and it disappears when it ends. A process has its own execution environment.
PID (process ID)
A PID is the identification number that the operating system gives to a process. In Java, ProcessHandle.current().pid() reads the PID of the process that is running now. Processes that are alive at the same time have different PIDs. The threads inside one process belong to the same process, so they have one PID. The operating system may reuse a number after the process ends, and the time until it is reused is not fixed.
Memory space of a process
The Java Tutorials explain that each process has its own memory space. A variable inside a process, for example static int count, is in the memory of that process. So even if you run the same program twice, each process has its own count, and a value changed by one process cannot be seen from the other. When a process ends, the values in its memory can no longer be used, and even if you run the same program again, the count of the new process starts from the initial value. In contrast, the threads inside one process share the memory of that process.
Processes and threads
A process is a running program, and the operating system gives it its own independent memory space. A thread is a flow of execution that does the actual work inside a process. The threads of one process share memory (heap, code, and data), and each has only its own stack. This makes it easy for threads to exchange data, but it also causes problems when they touch the same data at the same time.

Try these

Running with the default settings

  1. In the terminal, the command javac Counter.java makes javac read Counter.java and create the file Counter.class. This file is only a file stored on disk, and nothing is running now. A running program is called a process, and a process comes into being only when this file is run.
  2. In terminal 1, the command java Counter makes the operating system create one java (JVM) process, and that JVM reads and runs Counter.class. The number (PID) of this process is 4120. The operating system gives this process memory, and the count variable that we wrote starts at 0 in that memory. The process is created not by the code we wrote but by the java command and the operating system.
  3. When main calls add(), the three count++ lines inside add run, and count becomes 3 in the memory of this process. println prints "PID 4120 count 3". Then main waits 2 seconds in Thread.sleep(2000), so this process has not ended yet.
  4. While the earlier process is still running, you type the command java Counter once more in terminal 2. The file is the same single Counter.class, but the operating system creates one more process and gives it a new number. The PID of this process is 7352. The operating system gives this process its own memory too, so the count of this process also starts at 0.
  5. This process also runs add(). The three count++ lines raise the count in the memory of this process, so it goes from 0 to 3, and "PID 7352 count 3" is printed. The count of the earlier process is still 3. A value raised by one process cannot be seen from another process.
  6. After 2 seconds, main of each process ends, and all the processes end. The count of an ended process can no longer be used. 2 processes were created, but the file Counter.class is still the 1 file it was at the start.

Result of this run

Code

# Counter.java
public class Counter {
    // A variable in the memory of this process
    static int count = 0;

    // Raise count by 3, and print the PID of this process and count
    static void add() {
        count++;
        count++;
        count++;
        System.out.println("PID " + ProcessHandle.current().pid()
                + " count " + count);
    }

    public static void main(String[] args) throws Exception {
        add();
        // Keep the run going for 2 seconds
        Thread.sleep(2000);
    }
}

# Terminal
# Compile: the file Counter.class is created
javac Counter.java
# Run it in terminal 1
java Counter
# In terminal 2, run it before the earlier run ends
java Counter

Assumptions of this model

The environment is JDK 21.0.12.1 (javac 21, java 21) on Windows. All six combinations were checked against real execution. For each combination, the same text as the code on the screen (the # Counter.java section and the # Terminal section) was written into an empty folder, the source file and the commands were read from that text, and the real javac program and java program were started as processes, and the results were compared. For each java Counter line in the terminal section, the line directly above it was read as a comment. If it said "after the earlier run ends", the check waited for all earlier processes to end before starting it, and otherwise it started it at once without waiting. For "several terminals", the check did not open several terminals but started the processes one after another instead. What was checked is the number of processes actually started, the count printed by each process, whether any process ever saw a count larger than 3 (that is, whether memory is shared), the maximum number of live processes counted each time a process was started, whether the PID that was printed equals the process ID seen by the side that ran it, whether the PIDs of the processes alive together are different from each other, the exit code and the standard error, and whether Counter.class is still 1 file after running. The PIDs 4120, 7352, and 9081 on the screen are example numbers, and the real numbers differ on every run. On the PC used to write this lab, the PID of a process that was run again after the earlier one ended was different from all the earlier ones. But the Java SE 21 API documentation of ProcessHandle (the description of pid()) says that the operating system may reuse the number of an ended process and does not fix when it does so, so the screen does not say that "the number of the process that is run again is always different". That a program such as a web server is also one process when you run it with the java command was checked by running a small server program that receives requests (com.sun.net.httpserver of the JDK) with the java command and sending it two requests. The PIDs in the two responses were the same and equal to the process ID seen by the side that ran it, and count continued as 1, 2. This server is not shown on the screen, and a real server product such as Tomcat was not run. The documentation was opened and checked directly. From "Processes and Threads" in The Java Tutorials, the lab follows that a process has its own execution environment and memory space (the Processes section), that a thread lives inside a process and shares the resources of the process, including memory (the Threads section), and that most implementations of the Java virtual machine run as a single process. The distinction between a program file and a process (the file is on disk and the process is that file being run) is not defined by this document. It is an explanation that follows the execution results above and the general use of the terms. The code on the screen uses a static variable because it is meant to show that the variable count inside a process is in the memory of the process, and it makes the threads run one after another with join() to avoid the problem that occurs when two or more threads raise count at the same time (a race condition). Thread.sleep(2000) is there so that a process does not end at once and several processes are alive at the same time, and a real program does not need it. The code on the screen needs no import lines, so nothing was left out. This lab covers only that the operating system gives each process separate memory on one computer. It does not cover how processes pass values to each other, what it costs to create a process, or what happens to other processes when one process dies. These are covered in the in-depth lab "Processes and threads", and containers, which isolate processes and run them as a bundle, are covered in the in-depth lab "Containers and virtual machines". What could not be checked by opening the documentation: how the operating system decides a process number (PID), and how the operating system takes back memory when a process ends. The documentation was not opened for these, and the screen does not say anything about them.

Previous lab: Input and injection · Next lab: Calling another server

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these