This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Thinking about technical knowledge requires thinking about, say, the JVM virtual machine.Copy the code

The JVM is what makes the Java language cross-platform. The JVM hides differences between operating systems. The composition of JVM virtual machine includes program counter PC, method call stack, native method call stack, heap area, method area (class, method and constant pool), direct memory and other basic components. Heap space and method areas are shared by all threads. The contents of the Jvm virtual machine can be thought of as the virtual address space of the process.

compile

Java code is compiled by Javac into bytecode like code. Bytecodes are also interesting. The IDE can decompile class files directly into Java source files, although they are slightly different from source files. We can use Javap tools to read bytecodes in a readable way. -verbose displays Prints stack size, number of locals and arguments for methods. When we talk about method stacks later, we’ll come back to this section and maybe go deeper.

Another point to note: both the compiler and the processor reorder instructions to improve program performance. Reordering is simply the order in which instructions are executed.

This compilation is also not in the ordinary sense of compilation, which is compiled to machine code, machine code corresponding to assembly language. C and Cpp compilation can be described as compilation, so Java execution is actually interpreted execution. Why use the term interpreted execution? Java is an interpreted language, equivalent to the JVM being an interpreter.

AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT = AOT JIT is the bytecode that precompiles part of the hotspot. The JVM also uses reference counting and sampling to determine the code at the top of the stack, which feels consistent with the idea of garbage collection.

Class file structure

A class file is a set of binary streams in base 8

Java object structure

Object headers include object hashes, object generational ages, Pointers to lock records, Pointers to heavyweight locks, biased thread ids, biased timestamps, and so on

The instance data

Alignment filling

Constant pool

The pool of constants in the class file can be thought of as constants and symbolic references.

Load and method areas

When the program is run, the class file classes are loaded into the constant pool by the Classloader(which loads the bytecode into the constant pool and verifies, prepares, parses, and initializes (by initialization, we mean assigning values to class variables, i.e. static variables, and executing static code blocks).

So the strict lifecycle is documenting, validating, preparing, parsing, and initializing. (Does loadClass do so much?)

So there’s a constant pool in the method area.

The timing of class loading

When does a class load? The JVM does not enforce when it needs to be initialized, but it does enforce when it needs to be initialized, summarized in five points:

  1. When the VM starts, the user needs to specify a main class (including the main method) to execute. The virtual machine initializes the main class first
  2. When initializing a class, if the parent class has not already been initialized, the parent class must be initialized first. (So the Object class must be the first to be initialized in the virtual machine.)
  3. The four bytecode instructions, New getstatic putstatic Invokestatic, trigger initialization if the class has not already been initialized.
  4. Reflect’s package makes a reflection call to a class that has not already been initialized.
  5. REF_getStatic (REF_getStatic, REF_getStatic, REF_getStatic, REF_getStatic);
  6. Final fields are special and are put into the calling class’s constant pool at compile time (constant propagation optimization), so referring directly to static constant fields of the class does not cause class initialization.

Loading actually does three things:

  1. Gets the binary byte stream that defines a class by its fully qualified name. The binary stream of bytecode can also be computed at run time. Proxy uses ProxyGenerator to generate binary stream of $Proxy for a specific interface.
  2. Convert the static storage structure represented by this byte stream to the method area runtime data structure (meaning virtual memory, as well as some specific method area data structures)
  3. Generate an in-memory Class object representing the Class as an access point to the Class’s various data structures in the method area.

loading

The loading stage of non-array classes can be completed by using the bootstrap class loader provided by the system, or by using user-defined class loaders. Developers can define their own class loaders to control the way of obtaining byte streams.

The array class is special, right? The array class itself is not created by the class loader. It is created directly by the Java virtual machine (JVM), which creates an array primarily by considering how much memory to allocate. The element type of an array class is ultimately created by the classloader, so if the element class of the data type is a reference type, the array will be marked in the class namespace of the classloader that loaded the component type. Non-reference type, which is associated with the boot class loader.

After the loading phase is complete, the binary byte streams are stored in the virtual machine’s method area in a format implemented by the virtual machine. It then instantiates an in-memory object of Class Class (either in the method area or in the heap), which acts as an external interface for the program to access the data of these types in the method area.

validation

Verify that the bytecode complies with the rules and is formatted correctly.

To prepare

Allocates and initializes memory for class variables, that is, static variables, and the memory used by these variables is allocated in the method area.

Initialization refers to the initial value of the VM, not the self-set initial value.

parsing

The process of replacing symbolic references in a constant pool with direct references.

A constant pool is a repository of resources in a.class file. The constant pool contains literal and symbolic references.

Classes are parsed as they are loaded by the loader or wait until a symbolic reference is about to be used it is custom by the virtual machine.

The parse action is mainly for class or interface, field, class method, interface method, method type, method handle, and call point qualifier 7 class symbol references. For the first four, we can talk about them in detail. In other words, it resolves a symbolic reference that has never been resolved into a direct reference to a class or interface C, i.e. loads the class as a direct reference. Parsing is still all in the methods area.

Initialize the

In preparation, class variables have been initialized with the initial values of the system. In the initialization phase, variables and other resources are initialized according to the code logic. The initialization phase is the process of executing the class constructor Clinit.

Clinit is the result of the compiler’s automatic collection of class variable assignments combined with statements in a static statement block.

Clinit’s execution logic is a bit confusing, however. Only variables defined before the static block can be accessed, and variables defined after it can’t be accessed by the preceding static block, but can be assigned.

If you can assign, why can’t you access it?

Since this variable has an initial value in its preparation phase, it can also be assigned. But because the static variable is defined and initialized after the static block, the last assignment is not finished, and the value read here is not the final value, so it cannot be read. If it does, there may be an ambiguity that a variable that has not been initialized for the entire JVM life cycle is used first, which is against the rule.

classloader

The class-loading operation of “get the binary stream of a class by its fully qualified name” is implemented externally to the virtual machine. This leaves it up to the programmer to decide how to get the required classes. The code module that does this is called the class loader.

Classes and classloaders are bound.

The recommended use of VMS is parental inheritance.

There is JNDI, which is implemented by Rt.jar, which is loaded by bootloader, but does not recognize third-party code. There’s contextClassLoader for threads, and OSGI, which seems like a modular hot deployment technology. What if the base class wants to call the code of the user class? This is the second massive destruction, the JNDI scenario,

Classloaders use parental delegation, which is not only a black box mechanism within the JVM, but also open to developers. We can define our own Classloaders to do useful things like hot loading, hot deployment, loading encrypted bytecode, and so on.

Springboot-devtools defines the loading mechanism itself and defines a Restart classloader. When we rely on springboot-devtools as a third-party library in springboot poM files.

When a SpringBoot program is started, if devTools is used, a background thread is used to observe whether the bytecode file has changed. If it has changed, an event is sent, and a listener starts the logic for reloading the bytecode. Use a custom Restart ClassLoader to load the changed bytecode, generate new classes and objects (this debugging seems to be mainly about main), and then call Main to Restart the program. RestartClassLoader: RestartClassLoader: RestartClassLoader: RestartClassLoader: RestartClassLoader: RestartClassLoader

Springboot starts with LaunchedURLClassLoader as the main class loader, but it also starts with AppClassLoader when using IDE.

Methods area

When a JVM loads resources through a class loader, these resources include classes, interfaces, enumerations, and annotations. The JVM must store this information in the method area:

  • The full valid name for this type
  • The full valid name of the parent class of this type
  • An ordered list of direct interfaces of this type
  • Modifiers of this type, such as public Final Abstract, etc

Understand the annotation

Annotations are the trickier ones, introduced in JDK 5. Annotated Retentation policies include Source Compile Runtime, etc.

The only annotation that still needs to be loaded when the JVM loads bytecode is the Retentation policy of the Runtime. It is not uncommon to need a runtime annotation handler, such as in a section.

An annotation, after the class is loaded, is a class whose properties are static final fields.

The Retentation policy is annotated by the source type to be viewed by the source code; no error will be reported if none is present. But it might be clearer to mark it, such as Override. It can also provide information to the compiler.

Class policy is used to generate documentation or code at load time. It is not reserved for later loading.

Annotations are not part of the code and have no direct impact on the program’s running logic. That is, it does not affect the program run stack.

There are also meta annotations: @repeatable is used to indicate that you can repeat

Inherited Is used to indicate that a child can inherit from its parent

For example, how do you understand the annotations on the annotations? It’s just an annotation on top of the class. Annotations that are not meta-annotations also apply to the target object (method, class).

Can it be inherited? Annotations have no concept of inheritance. Classes can inherit some annotations from their parent class if the @inherited annotation is used. For example, Slf4j cannot inherit, but SpringBootTest can be Inherited.

It turns out that Repository annotations, like Aspect annotations, have special cases.

The @repository annotation marks any class to indicate that the class is used to perform database-related operations (i.e. dao objects). If an exception is thrown in a database operation on an @Repository annotated class, it will be handled. Instead, it will throw a translated spring-specific database exception. It is convenient for us to troubleshoot anomalies.

A PersistenceExceptionTranslationAdvisor annotations, it is spring in the initialization bean 3 pool of things to do, at this stage will Reposity beans will give agent.

The Runtime Runtime

Java interprets the bytecode runtime and compiles it to machine code. This process can also be optimized in a number of ways, and not optimizing can have a significant impact on performance. What does JVM 8 default to? I think JIT is enabled by default. The Java compilation process is divided into a front-end compiler and a back-end compiler.

run

Java programs start running through the main entry, and the program counter stores the address of the next instruction unit, the first bytecode in main.

The virtual machine calls each frame corresponding to the stack method, also known as stack frame. After the top frame completes, the result is returned to the bottom frame.

Each stack frame also has a fixed structure. Stack frame includes several parts such as local variable table and workspace. Each thread, the local variable table, is also dynamically generated as the method runs, working in collaboration with the workspace stack. This dynamic process is driven by the PC register, which points to the next instruction to run.

The workspace is used to store the current temporary results. Temporary operands, such as a+b, are pushed first by A, then by B, then the add instruction is evaluated, then by a and B, and then the result is pushed.

If not in a static method, then the first slot in the local variable table refers to this, which is the address of the object in 32 bits.

  • How do I know if a 64-bit VIRTUAL machine has pointer compression?

Print directly using GraphLayout

OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object  header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 4a ca 22 00 (01001010 11001010 00100010 00000000) (2280010) 12 4 (loss due to the next object alignment)Copy the code

The compression pointer is turned on by default (-xx :+UseCompressedOops), and – is turned off.

What is the difference in addressing between a 32-bit JVM and a 64-bit JVM? 64-bit free memory can be greater than 4G, but 32-bit memory can’t. But because 64-bit compacts the pointer, 64-bit can use 32-bit addressing, greatly reducing the size of the object.

Is the heap directly modified if the reference object of the local variable table is changed? That’s what it should be. But because the CPU has a cache, this process is not necessarily reliable. If synchronized or some other locking method is used, this will definitely be written back to memory. If not, it is not very reliable, and you need to determine whether volatile is needed to help obtain visibility of variables when concurrent reads and writes occur.

Hence the introduction of Java’s memory model JMM, which is the hardware level CPU cache and bus protocol.

When you talk about volatile, you talk about visibility and preventing instruction reordering.

Frame holds the local variable table, operand stack, dynamic link (that is, the method reference to the run-time constant pool (which method it is), and method return type returnAddress (the address of a PC register), which also occupies a slot. The parameter is in the local variable table, after this.

The size of the local variable table is determined when the bytecode is compiled and will not change later.

Describing a method that refers to another method is determined by referring to symbolic links in the constant pool (which becomes a direct reference during parsing). There are invoke directives involved, as well as invoke of virtual methods.