JVMmemorymodel
This article mainly introduces the runtime data area (RuntimeDataAreas) described in the JVM specification. These areas are designed to store data used by the JVM itself or programs running on the JVM.
Let's first overview of the JVM, then introduce the bytecode, and finally introduce different data areas.
Overview
As an abstraction of the operating system, JVM ensures that the same code behaves consistently on different hardware or operating systems.
for example:
For the basic type int, it is a 32-bit signed integer regardless of 16-bit/32-bit/64-bit operating system. Range from -2^31 to 2^31-1
Regardless of whether the operating system or hardware is large or small byte order, it is ensured that the data in memory stored and used by the JVM is large or small byte order (read the high-bit byte first)
Different JVM implementations may differ somewhat, but are generally the same.
The above picture is an overview of a JVM
The JVM interprets the compiler-generated bytecode. Although JVM is the abbreviation of Java virtual machines, as long as it is a language that can be compiled into bytecode, it can be run based on the JVM, such as scala, groovy< �"/kf/ware/vc/" target="_blank">vcD4NCjxwPs6qwcux3MPixrW3sbXEtMXFzEkvT6Os19a92sLru+Gxu2NsYXNzbG9hZGVyvNPU2LKiu7q05rW91MvQ0Mqxyv2+3cf41tC1xNK7uPbH+NPyo6zWqrXAvNPU2Mv8tcRjbGFzc2xvYWRlcrG7z/q72bvy1d9KVk3No9a51MvQ0KGjPC 9wPg0KPHA+vNPU2LXE19a92sLrzai5/da00NDS/cfmKGV4ZWN1dGlvbiBlbmdpbmUpvfjQ0L3iys26zda00NA8L3A+DQo8cD7WtNDQ0v3H5tDo0qq05rSis8zQ8snPz8LOxKOsscjI57PM0PLWtNDQtb3ExNK70NCjrLvy1d/K/b7dvMbL47XE1tC85L3hufs8L3A+D Qo8cD7WtNDQ0v3H5tKyuLrU8LSmwO3T67XXsuOy2df3z7XNs7XEvbu7pTwvcD4NCjxwPioquty24EpWTba8yrXP1sHLvLTKsbHg0uu5psTcKEpJVD1qdXN0IGluIHRpbWUpoaNKSVS+zcrHsNG+rbOj1rTQ0LXEtPrC6yjIyLXjtPrC6ymx4NLrs8mxvrXYtPrC6yhO YXRpdmUgQ29kZSmho7Tmt8VKSVSx4NLryfqzybT6wuu1xMf40/Kzxs6qPC9wPg0KPHA+tPrC67u6tObH+ChDb2RlIENhY2gpoaO8tMqxseDS67y8yvUoSklUKby2tPO1xMzhuN/By0pWTbXE0NTE3CoqPC9wPg0KPGgyIGlkPQ=="Stack-based architecture">Stack-based architecture
JVM uses a stack-based architecture. Although the stack is transparent to developers, it has a very important role or influence on the generated bytecode and JVM.
The programs we developed will convert low-level operations and store them in bytecode. Map to operation instructions through operands in the JVM. According to the JVM specification, the parameters required by the operation instructions are obtained from the operand stack.
Let’s give an example of adding two numbers. This operation is called iadd. The following is the process of 3+4 in bytecode
First push 3 and 4 into the operand stack
Call the iadd directive
The iadd instruction pops up 2 numbers from the top of the operand stack
The result of 3+4 is pushed into the operand stack for later use
This approach is called a stack-based architecture. There are other ways to handle low-level operations, such as register-based architecture.
Bytecode
Java bytecode is the result of converting java source code into a series of low-level operations. Each operation consists of an opcode or operation code with zero or more byte length parameters (but most operations use parameters obtained through the operand stack). One byte can represent 256 numbers, from 0x00 to 0xff, and currently to java8, a total of 204 are used.
The following lists different types of bytecode opcodes, as well as their range and simple description
Constants: Push the value of the constant pool or a known value into the operand stack. 0x00 - 0x14
Loads: Push local variable values into the operand stack. 0x15 - 0x35
Stores: Loading value from operand stack to local variable 0x36 - 0x56
Stack: Process operand stack 0x57 - 0x5f
Math: Get value from operand stack for basic mathematical calculation 0x60 - 0x84
Conversions: Convert between types 0x85 - 0x 93
Comaprisons: Comparison operation of two values 0x94 - 0xa6
Controls: Execute goto, return, loop, etc. control operations 0xa7 - 0xb1
References: Executes allocation objects or arrays, and obtains or checks references to objects, methods, and static methods. Static methods can also be called. 0xb2 - oxc3
Extended: Extended: operations from the others categories that were added after. From value 0xc4 to 0xc9
(What does this sentence mean? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Reserved: The JVM implementation uses slots 0xca, oxfe, oxff
These 204 operations are very simple, give a few examples
ifeq(0x99) determines whether the two values are equal
iadd(0x60) adds two numbers
i2l (0x85) converts an int bit long
arraylength (0xbe) Returns the array length
pop (0x57) Pop a value from the top of the operand stack
We need a compiler to create bytecode files, and the standard java compiler is javac in jdk.
public class Test { public static void main(String[] args) { int a =1; int b = 15; int result = add(a,b); } public static int add(int a, int b){ int result = a + b; return result; }}You can get the bytecode file of "Test.class" through "javac Test.java". Bytecode files are binary, we can convert binary bytecode files into text form through Javap
java -verbose Test.class
Classfile /C:/TMP/Test.class Last modified 1 avr. 2015; size 367 bytes MD5 checksum adb9ff75f12fc6ce1cdde22a9c4c7426 Compiled from "Test.java"public class com.codinggeek.jvm.Test SourceFile: "Test.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Methodref #4.#15 // java/lang/Object."<init>":()V #2 = Methodref #3.#16 // com/codinggeek/jvm/Test.add:(II)I #3 = Class #17 // com/codinggeek/jvm/Test #4 = Class #18 // java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 main #10 = Utf8 ([Ljava/lang/String;)V #11 = Utf8 add #12 = Utf8 (II)I #13 = Utf8 SourceFile #14 = Utf8 Test.java #15 = NameAndType #5:#6 // "<init>":()V #16 = NameAndType #11:#12 // add:(II)I #17 = Utf8 com/codinggeek/jvm/Test #18 = Utf8 java/lang/Object{ public com.codinggeek.jvm.Test(); 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 3: 0 public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: bipush 15 4: istore_2 5: iload_1 6: iload_2 7: invokestatic #2 // Method add:(II)I 10: istore_3 11: return LineNumberTable: line 6: 0 line 7: 2 line 8: 5 line 9: 11 public static int add(int, int); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=2 0: iload_0 1: iload_1 2: iadd 3: istore_2 4: iload_2 5: ireturn LineNumberTable: line 12: 0 line 13: 4}It can be seen that bytecode is not just a simple translation of java code, it includes:
A constant pool description of the class. Constant pools are JVM data areas used to store class metadata, such as method names, parameter lists, etc. inside the class. When the JVM loads a class, the metadata will be loaded into the constant pool
Provide the specific position information of functions and Tmall variables in bytecode through row number table and local variable table.
Translation of java code (including hidden parent class constructs)
Provides more specific operations on operand stacks and more complete ways to pass and obtain parameters
Below is a simple description of the bytecode file storage information
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 attributes_count; attribute_info attributes[attributes_count];}Runtime data area
The runtime data area is the memory area designed to store data. This data is for use by developers or JVM internally.
Heap
The heap is created when the JVM is started and is shared by all JVM threads. All class instances and arrays are allocated to the heap (created by new).
The heap must be managed by a garbage collector, which is responsible for releasing objects created by the developer and will not be used again.
As for the garbage collection strategy, it is determined by the JVM implementation (for example, HotSpot provides multiple algorithms).
There is a maximum limit for heap memory. If this value is exceeded, the JVM will throw an OutOfMemroy exception.
Method area
The method area is also shared by all threads of the JVM. The same is created with JVM startup. The data stored in the method area is loaded from the bytecode by the classloader, which will exist consistently during the application run, unless the classloader loading them is destroyed or the JVM is stopped.
The method area stores the following data:
Class information (attribute name, method name, parent class name, excuse name, version, etc.)
Methods and constructed bytecodes
Runtime constant pool created when loading each class
The JVM specification does not force method areas to be implemented in the heap. Prior to java7, HotSpot implemented method zones using a region called PermGen. Permanent band is adjacent to the heap (memory management is the same as the heap), the default bit is 64MB
Starting from java8, HptSpot uses a separate local memory to implement the method area and naming the metadata area (Metaspace). The maximum available space in the metadata area is the available memory of the entire system.
If the method cannot apply for available memory, the JVM will also throw OutOfMemoryError.
Runtime constant pool
The runtime constant pool is part of the method area. Because of the importance of running a constant pool to metadata, it is described separately in the Java specification outside the method area. The runtime constant pool grows with the loaded classes and interfaces.
Constant pools are a bit of a syntax table in traditional languages. In other words, when a class, method, or property is called, the JVM searches for the real address of this data in memory through the runtime constant pool. Runtime constant pool also contains constants of string literals or primitive types
Stirng myString="This is a string litteral" static final int MY_CONSTANT = 2 ;
pc (program counter) register (per thread) The pc register (Per Thread)
Each thread has its own pc (program counter) register, which is created together with thread creation. Each thread can only execute one method at a point in time, called the current method of the thread. The pc register contains the address of the JVM currently executing the instruction (in the method area).
If the currently executed method is a local method, the value of the pc register is undefined
Virtual machine stack per thread-java-virtual-machine-stacks-per-thread">Virtual machine stack (per thread) java virtual machine stacks (per thread)
The virtual machine stack stores multiple frames, so before describing the stack, let's take a look at the frames first.
Frames
A frame is a data structure that contains multiple data representing the current method state that the thread is executing:
Operand Stack: As mentioned before, bytecode instructions use operand stack to pass parameters
Local variable array: This array contains all local variables within a scope of the currently executed method. This array can contain a primitive type, a reference, or a return address. The size of the local variable array is determined at compile time. jvm uses local variables to pass parameters when calling the method, and the local variable array of the called method is created through the operand stack of the calling method.
Runtime constant pool reference: References to the constant pool of the current method of the current class. The JVM uses constant pool references to pass signals to real memory references.
Stack (stack)
Each JVM thread has a private JVM stack, which is created at the same time as the thread. Java virtual machine stack stores frames. Each time a method is called, a frame is created and pushed into the virtual machine stack. When this method is executed, the frame will also be destroyed (regardless of whether the method is executed normally or an exception is thrown)
Only one frame is available during execution of a thread. This frame is called the current frame.
Operations on local variables and operand stacks are usually accompanied by references to the current frame.
Let's look at another example of addition
public int add(int a, int b){ return a + b;} public void functionA(){// some code without function call int result = add(2,3); //call to function B// some code without function call}Inside method A, frame A is the current frame, located at the top of the virtual machine stack. At the beginning of calling the add method, a new frame B is created and pushed into the virtual machine stack. Frame B becomes the new current frame.
The local variable array of frame B is filled with data in the operand stack of frame A. When the add method ends, frame B is destroyed and frame A is re-established as the current frame. The result of the add method is pushed into the operand stack of frame A, so that method A can obtain the result of add through the operand stack of frame A.
Summarize
The above is all about the data area analysis of Java virtual machine runtime. I hope it will be helpful to everyone.
If there are any shortcomings, please leave a message to point it out.