JAVA Virtual Machine
Class Loading Process

Loading
- Fetch binary stream: Get the binary stream defining the class by its fully qualified name.
- Convert to runtime data structure: Convert the byte stream static storage structure into the runtime data structure of the method area.
- Generate class: Generate a class object representing this class in memory as the access entry for various data of this class in the method area.
Verification
- File format verification: Byte stream enters method area for storage.
- Metadata verification
- Bytecode verification: Use type checking to complete data flow analysis and verification.
- Symbol reference verification:
Preparation
Formally allocate memory for class variables and set initial values for class variables, all allocated in the method area.
Resolution
The process where the virtual machine replaces symbol references with direct references.
Initialization
<clinit> <init>
The six situations triggering initialization are as follows:
- New creates class instance.
- Accessing a static variable of a class or interface, or assigning a value to the static variable.
- Calling a static method of a class.
- Reflection
class.forName("com.xxx.xxx"). - Initializing a subclass of a class.
- The class marked as the startup class when the Java virtual machine starts.
Dynamic Invocation
Static Allocation
Corresponds to method overloading: Relies on static types to locate the version of method execution, belongs to multiple dispatch.
Dynamic Allocation
Corresponds to method overriding, belongs to single dispatch.
Steps to confirm the method:
- Find the actual type of the object pointed to by the first element of the operand stack, i.e., the type after the new keyword.
- If a method matching the description and simple method name is found in the constant pool, and permission check passes, reference directly.
- Otherwise, perform the second step from bottom to top according to the inheritance relationship, re-finding other subclasses.
- If still not found, throw an exception.
Definitions of several related terms:
Quantity: Method receiver and method parameters. Single dispatch: Select target method based on one quantity. Multiple dispatch: Select target method based on multiple quantities.
JAVA language is a Static Multiple Dispatch Dynamic Single Dispatch language.
Lock
Spin: Spin refers to when a thread needs to acquire a lock, but the lock is already occupied by another thread, the thread will not be suspended, but will continue to consume CPU time, constantly trying to acquire the lock.
Biased Lock
Biased lock is introduced to minimize unnecessary lightweight lock execution paths in the absence of multi-thread competition, because the acquisition and release of lightweight locks rely on multiple CAS atomic instructions, while biased locks only rely on one CAS atomic instruction when replacing ThreadID. When only one thread competes for the lock, we do not need to block or spin, because only one thread is competing, we only need to check if the ThreadID in the biased lock is the current thread. If it is, execute the synchronous code; if not, try to modify the ThreadID using CAS. If successful, execute the synchronous code; if unsuccessful, upgrade the biased lock to a lightweight lock.
Lightweight Lock
The process of obtaining a lightweight lock is different from a biased lock. The thread competing for the lock first needs to copy the Mark Word in the object header to the lock record in the stack frame. After successful copying, use CAS operation to try to update the object's Mark Word to a pointer pointing to the current thread. If this update action is successful, the thread owns the lock of the object. If the update fails, it means multiple threads are competing. When a competing thread fails to occupy the lightweight lock multiple times (using spinning), the lightweight lock will inflate into a heavyweight lock. The heavyweight thread pointer points to the competing thread, and the competing thread will also be blocked, waiting for the lightweight thread to wake it up after releasing the lock.
Heavyweight Lock
The locking and unlocking process of a heavyweight lock is similar to a lightweight lock. The difference is: after competition failure, the thread blocks. After releasing the lock, the blocked thread is woken up. It does not use spin locks and does not consume CPU as much, so heavyweight locks are suitable for cases where the synchronous block execution time is long.
synchronized and volatile
- Volatile essentially tells the jvm that the value of the current variable in the register (working memory) is uncertain and needs to be read from the main memory; synchronize locks the current variable, only the current thread can access the variable, other threads are blocked.
- Volatile can only be used at the variable level; synchronize can be used on variables, methods, and classes.
- Volatile only implements visibility of variable modifications and cannot guarantee atomicity; while synchronized can guarantee both.
- Volatile will not cause thread blocking; synchronized will cause thread blocking.
- Volatile variables will not be optimized by the compiler; variables marked by synchronized can be optimized by the compiler.
synchronize and ReentrantLock
ReentrantLock extends synchronized. ReentrantLock can set the wait time for the lock, thus avoiding deadlocks. ReentrantLock can obtain information about various locks. ReentrantLock can flexibly implement multi-way notification.
The locking mechanisms of the two are different: ReentrantLock basically calls Unsafe's park method to lock. Synchronized operates on the mark word information in the object header to lock.
Parent Delegation Model
Class loader classification: Each class loader has an independent namespace.
Bootstrap Class Loader: Part of the virtual machine itself, used to load classes in the JAVA_HOME/lib/ directory.
Extension Class Loader: Responsible for loading all class libraries in the path specified by the java.ext.dirs system variable.
Application Class Loader: Responsible for loading specified class libraries on the user class path, the default class loader used.
If a class loader receives a class loading request, it will not load the class first, but delegate the request to the parent class loader to complete. This is true for every layer of loaders, so all loading requests will be passed to the top-level bootstrap class loader. Only when the parent class loader cannot complete the loading request, the child loader will try to load the class.
Order of Attribute Loading in Parent and Child Classes During Class Instantiation
- Parent class static variable
- Parent class static code block
- Child class static variable
- Child class static code block
- Parent class non-static variable
- Parent class constructor
- Child class non-static variable
- Child class constructor
- Static variable -> Static code block -> Non-static variable -> Constructor
Optimize GC
- Appropriately adjust the ratio of
-XX:SurvivorRatio. - Choose a garbage collector suitable for your business, web services are generally ParNew+CMS.
- Adjust the proportions of JVM old generation, young generation and permanent generation, test out a satisfactory value.
- Set
-XX:MaxTenuringThresholdto let the young generation enter the old generation in advance, reducing copying in the survivor area. - Adjust
-XX:CMSInitiatingOccupancyFraction=60to control minor gc frequency. -XX+UseCMSCompactAtFullCollectioneliminates cms fragmentation.
Garbage Collection and Algorithms
Reference Counting: When a place uses +1 to count value, -1 when invalid, 0 means object can no longer be referenced. Disadvantage: Circular references, some objects cannot be recycled.
Reachability Analysis Algorithm: Search downwards starting from a series of objects called GCROOTS (path is reference chain). When an object is not connected to any GC reference chain, these objects are unreachable. GCROOTS objects include:
- Objects referenced in the virtual machine stack.
- Objects referenced by static attributes or constants in the method area.
- Objects referenced by native methods.
Garbage Collection Algorithms:
- Mark-Sweep (causes fragmentation).
- Copying Algorithm (memory usage 50%).
- Mark-Compact (one more step than Mark-Sweep, moves surviving objects to one end, clears other objects).
- Generational Algorithm (Young generation: Copying algorithm; Old generation: Mark-Sweep or Mark-Compact).
Garbage Collectors: ParNew (Multi-thread, High Throughput)
CMS (Initial Mark, Concurrent Mark (long time), Remark, Concurrent Sweep (long time), Low Latency)

G1 (Divides the entire heap into small blocks, 1-32M, RememberSet points to the block's memory address) Adjust small -XX:InitiatingHeapOccupancyPercent=45% Increase Minor GC frequency, reduce Full GC frequency.

Memory Area
String Type
- All strings created by literals (such as
"hello") will be put into the string constant pool for reuse.String hello = "hello"; - But this creates a new object in the heap
String hello1 = new String("hello"); - So
helloandhello1are different references.
Other Types
IntegerCaches integers in the range-128to127.CharacterCaches common characters (usually\u0000to\u007F).Long,Short,Byteetc. also cache small range values-128to127.BooleanCachesBoolean.TRUEandBoolean.FALSE.
\u0000to\u007Fare usually used to represent basic control characters, numbers, letters and some common symbols
Zhou Zhiming "Understanding the JAVA Virtual Machine" Second Edition
- Attribution: Retain the original author's signature and code source information in the original and derivative code.
- Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
- Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
- NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
- ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.