[JVM] runtime data area (runtime data area structure, thread, program counter, PC)

🔰 Learning video 🔰

Shang Silicon Valley song Hongkang JVM complete tutorial (detailed explanation of java virtual machine)

Number of episodes: 39-43

1, Foreword

This section mainly talks about the runtime data area, that is, the part in the figure below, which is the stage after the class is loaded

When we pass the previous steps: class loading -- > verification -- > preparation -- > parsing -- > initialization, the execution engine will be used to use our class, and the execution engine will use our runtime data area

By analogy, that is, when the chef cooks, you can compare the things behind the chef (cut dishes, knives and spices) to the runtime data area. The chef can make exquisite dishes by preparing things similar to the execution engine. After dinner, you need to clean the table, which is similar to memory recycling.

2, Runtime data area structure

2.1 runtime data area and memory

  1. Memory is a very important system resource. It is the intermediate warehouse and bridge between hard disk and CPU. It carries the real-time operation of operating system and applications. The JVM memory layout specifies the strategies of memory application, allocation and management during Java operation, ensuring the efficient and stable operation of the JVM. Different JVMs have some differences in memory partition and management mechanism. Combined with the JVM virtual machine specification, let's discuss the classic JVM memory layout.

  2. The data we get through disk or network IO needs to be loaded into the memory first, and then the CPU obtains the data from the memory for reading, that is, the memory acts as a bridge between the CPU and the disk

Simplified runtime data area:

The following figure is from Alibaba manual JDK8

2.2 thread memory space

  1. Java virtual machine defines several runtime data areas that will be used during program running: some of them will be created as the virtual machine starts and destroyed as the virtual machine exits. Others correspond to threads one by one. These data areas corresponding to threads will be created and destroyed as the thread starts and ends.
  2. Gray is private to a single thread, and red is shared by multiple threads. Namely:
    Thread unique: program counter (PC), local method stack (NMS), virtual machine stack (VMS)
    Sharing among threads: heap and method area (also known as external memory, perpetual generation, meta space and code cache)


For example, if a process contains three threads, the three threads share a method area and heap, with three groups of program counters (PC), local method stack (NMS) and virtual machine stack (VMS).

Due to the common part, thread safety will be involved.

2.3 Runtime class

There is only one Runtime instance per JVM. That is, the Runtime environment, which is equivalent to the frame in the middle of the memory structure: the Runtime environment.

3, Thread

3.1 JVM threads

  1. A thread is the running unit of a program. The JVM allows an application to have multiple threads executing in parallel
  2. In the Hotspot JVM, each thread is mapped directly to the local thread of the operating system
    When a Java thread is ready to execute, a local thread of the operating system is also created. After the execution of the Java thread terminates, the local thread will also recycle
  3. The operating system is responsible for scheduling threads to any available CPU. Once the local thread is initialized successfully, it calls the run() method in the Java thread

3.2 JVM system threads

If you use jconsole or any debugging tool, you can see many threads running in the background. These background threads do not include the main thread calling public static void main(String []) and all threads created by the main thread itself.

These main background system threads are mainly the following in the Hotspot JVM:

  1. Virtual machine thread: the operation of this thread requires the JVM to reach the safety point. These operations must occur in different threads because they all need the JVM to reach a safe point so that the heap will not change. The execution types of this thread include "stop the world" garbage collection, thread stack collection, thread suspension and partial lock revocation
  2. Periodic task threads: these threads are the embodiment of time periodic events (such as interrupts). They are generally used for the scheduling and execution of periodic operations
  3. GC thread: this thread supports different kinds of garbage collection behavior in the JVM
  4. Compilation thread: this thread will compile bytecode into local code at run time
  5. Signal scheduling thread: this thread receives signals and sends them to the JVM, which processes them internally by calling appropriate methods

4, Program counter (PC register)

Official document website: https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

4.1 introduction to PC register

  • In the Program Counter Register in the JVM, the name of the Register comes from the CPU Register, which stores field information related to instructions. The CPU can run only when it loads data into registers.
  • Here, it is not a physical register in a broad sense. Perhaps it will be more appropriate to translate it into PC counter (or instruction counter) (also known as program hook), and it is not easy to cause some unnecessary misunderstandings. The PC register in the JVM is an abstract simulation of the physical PC register.

4.2 function of PC register

The PC register is used to store the address pointing to the next instruction and the instruction code to be executed. The execution engine reads the next instruction and executes it.

  • It is a small memory space that can be almost ignored. It is also the fastest storage area.
  • In the JVM specification, each thread has its own program counter, which is thread private, and the life cycle is consistent with the thread life cycle.
  • At any time, a thread has only one method executing, that is, the so-called current method. The program counter will store the JVM instruction address of the Java method being executed by the current thread; Or, if the native method is being executed, the value is unspecified.
  • It is the indicator of program control flow. The basic functions such as branch, loop, jump, exception handling and thread recovery need to be completed by this counter.
  • The bytecode interpreter works by changing the value of this counter to select the next bytecode instruction to be executed.
  • It is the only area where no out of memory error condition is specified in the Java virtual machine specification.
  • No garbage collection GC

4.3 examples

public class PCRegisterTest {
    public static void main(String[] args) {
        int i = 10;
        int j = 20;
        int k = i + j;

        String s = "abc";
        System.out.println(i);
        System.out.println(k);
    }
}

Compile it into a class file, and then decompile it to generate bytecode

javap -verbose PCRegisterTest.class

Bytecode:

Classfile /E:/java/code/studyNote/jvm/target/classes/chapter4/PCRegisterTest.class
  Last modified 2022-1-4; size 659 bytes
  MD5 checksum 4cbea470209d835d218de7a8aceb6665
  Compiled from "PCRegisterTest.java"
public class chapter4.PCRegisterTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#26         // java/lang/Object."<init>":()V
   #2 = String             #27            // abc
   #3 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #30.#31        // java/io/PrintStream.println:(I)V
   #5 = Class              #32            // chapter4/PCRegisterTest
   #6 = Class              #33            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lchapter4/PCRegisterTest;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               i
  #19 = Utf8               I
  #20 = Utf8               j
  #21 = Utf8               k
  #22 = Utf8               s
  #23 = Utf8               Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               PCRegisterTest.java
  #26 = NameAndType        #7:#8          // "<init>":()V
  #27 = Utf8               abc
  #28 = Class              #34            // java/lang/System
  #29 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #30 = Class              #37            // java/io/PrintStream
  #31 = NameAndType        #38:#39        // println:(I)V
  #32 = Utf8               chapter4/PCRegisterTest
  #33 = Utf8               java/lang/Object
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (I)V
{
  public chapter4.PCRegisterTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lchapter4/PCRegisterTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=5, args_size=1
         0: bipush        10
         2: istore_1
         3: bipush        20
         5: istore_2
         6: iload_1
         7: iload_2
         8: iadd
         9: istore_3
        10: ldc           #2                  // String abc
        12: astore        4
        14: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        17: iload_1
        18: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        21: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        24: iload_3
        25: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
        28: return
      LineNumberTable:
        line 16: 0
        line 17: 3
        line 18: 6
        line 20: 10
        line 21: 14
        line 22: 21
        line 23: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  args   [Ljava/lang/String;
            3      26     1     i   I
            6      23     2     j   I
           10      19     3     k   I
           14      15     4     s   Ljava/lang/String;
}
SourceFile: "PCRegisterTest.java"

The number on the left represents the instruction address (instruction offset), that is, the value that may be stored in the PC register, and then the execution engine reads the value in the PC register and executes the instruction

4.4 two common questions

What's the use of using PC registers to store bytecode instruction addresses? Or why use the PC register to record the execution address of the current thread?

  1. Because the CPU needs to constantly switch threads, after switching back, you have to know where to continue execution
  2. The bytecode interpreter of the JVM needs to change the value of the PC register to determine what bytecode instruction should be executed next


Why is the PC register set to private?

  1. We all know that the so-called multithreading method will only execute one thread in a specific time period, and the CPU will keep switching tasks, which will inevitably lead to frequent interruption or recovery. How to ensure no difference? In order to accurately record the current bytecode instruction address being executed by each thread, the best way is naturally to allocate a PC register for each thread, so that each thread can calculate independently, so as not to interfere with each other.
  2. Due to the limitation of CPU time slice, during the concurrent execution of many threads, at any given time, a processor or a kernel in a multi-core processor will only execute one instruction in a thread.
  3. This will inevitably lead to frequent interruption or recovery. How to ensure no difference? After each thread is created, it will generate its own program counter and stack frame. The program counter does not affect each other among threads.

4.5 CPU time slice

  1. CPU time slice is the time allocated by the CPU to each program. Each thread is allocated a time period, which is called its time slice.
  2. Macroscopically: we can open multiple applications at the same time, and each program runs side by side at the same time.
  3. But at the micro level: because there is only one CPU, it can only process part of the program requirements at a time. One way to deal with fairness is to introduce time slices, and each program is executed in turn.

Keywords: Java jvm Back-end

Added by Bob_PHP_Builder on Tue, 04 Jan 2022 13:56:30 +0200