This article is a summary after learning “Understanding the Java Virtual Machine in Depth”, the main content is from the book, but also some of the author’s understanding. One is to sort out knowledge points, sum up, the other is to share communication, if there are mistakes also hope to point out. This article is based on the JDK1.7 specification.

The article is roughly divided into the following sections: JVM memory regions, JVM memory overflow, JVM garbage collection, JVM performance tuning, and class loading.

First, the JVM memory area


During the running of a Java VM, the memory space is divided into several areas. According to the Java Virtual Machine Specification (Java SE 7 edition), the memory area managed by a Java VM is divided into the following parts: method area, heap memory, virtual machine stack, local method stack, and program counter.

1. Method area

The method area is mainly used to store data such as class information loaded by the VIRTUAL machine, constants, static variables, and compiled code by the compiler. Before jdk1.7 or earlier, a method area was a “logical part” of the heap (a contiguous chunk of heap space), but to distinguish it from the heap, it was also called “non-heap” or “permanent generation” (HotSpot’s implementation of the method area).

In the jdk1.7 HotSpot, static variables, string constant pools, etc., which were originally in the method area, have been moved to the heap. In JDK1.8, the method area no longer exists, and the class information, compiled code data, and so on stored in the original method area have been moved to MetaSpace, which is not in heap memory, but directly in local memory (Native Emory). According to the online information combined with my own understanding of jdK1.3-1.6, JDK1.7, JDK1.8 method changes in the map as follows (if there is any unreasonable place hope readers point out) :



The reasons for going permanent are:

(1) String exists in permanent generation, which is prone to performance problems and memory overflow.

(2) It is difficult to determine the size of class and method information, so it is difficult to specify the size of permanent generation. If the size is too small, it is easy to overflow the permanent generation, while if the size is too large, it is easy to overflow the old generation.

(3) Permanent generation will bring unnecessary complexity to GC, and the collection efficiency is low.

2. Heap memory

Heap memory, which holds objects and arrays, is the largest area of memory managed by the JVM. Heap memory and method areas are shared by all threads and created at virtual machine startup. From the perspective of garbage collection, the heap can be divided into YoungGeneration and OldGeneration, and the new generation can also be divided into Eden, From Survivor, and To Survivor, because the current collectors basically adopt generational collection algorithm.

3. Program counter

The program counter is a very small piece of memory space, can be seen as the number of rows in the current thread execute bytecode indicator, each thread has a separate program counter, so the program counter is private to the thread a piece of space, in addition, the program counter the Java virtual machine is the only memory area will not occur.

4. Virtual machine stack

The virtual stack is also a piece of memory that is private to each thread. It describes the memory model of a method, as shown below:



Each thread is assigned a virtual machine stack, and each stack has several stack frames. Each stack frame stores local variables, operand stacks, dynamic links, return addresses, and so on. A stack frame corresponds to a method in Java code. When a thread executes a method, it means that the corresponding stack frame of this method has entered the virtual machine stack and is at the top of the stack. From the call to the end of execution, each Java method corresponds to a stack frame from the stack to the stack.

5. Local method stack

The difference between the Native Method stack and the virtual machine stack is that the virtual machine stack executes Java methods, while the Native Method stack executes Native methods. Otherwise, it is basically the same. In HotSpot, the Native Method stack and the virtual machine stack are merged into one.

6. Meta-space

In JDK1.8, the permanent generation (method area) does not exist. Instead, it is replaced by a space called a “meta-space”, which is similar to the JVM specification for the permanent generation. However, the meta-space does not exist in the virtual machine. However, you can specify the size of the meta-space by using -xx :MetaspaceSize and -xx :MaxMetaspaceSize.

JVM memory overflow


1, the heap memory overflow

Heap memory mainly holds objects, arrays, etc. As long as these objects are constantly created and there is a reachable path between the GC Roots and the objects to avoid garbage collection, OutOfMemoryError will be generated when the size of these objects exceeds the maximum heap capacity. The following is an example of heap memory exceptions:

/** * Set maximum heap minimum heap: -xMS20m -XMx20m * At runtime, instances of the OOMObject class are constantly created in the heap, and there are reachable paths between GC Roots(oomObjectList in code) and objects (each OOMObject) before the while execution ends, The garbage collector is unable to reclaim them and eventually runs out of memory. */ public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> oomObjectList = new ArrayList<>(); while (true) { oomObjectList.add(new OOMObject()); }}}Copy the code

After the operation will quote is unusual, can be seen in the stack information. Java lang. OutOfMemoryError: Java heap space information, out of memory on the heap memory space abnormal.

The newly generated objects are initially allocated to the Cenozoic generation. When the Cenozoic generation is Full, a Minor GC is performed. If there is insufficient space after the Minor GC, the objects and the qualified objects of the Cenozoic generation are put into the old age. Then OutOfMemoryError is thrown if there is not enough space to hold the new object. Common causes: Too much data is loaded in the memory, for example, too much data is fetched from the database. The collection references too many objects and is not emptied after use. An infinite loop or loop that produces too many duplicate objects in code; Heap memory allocation is not reasonable; Network connection and database problems.

2. Virtual machine stack/local method stack overflow

(1) StackOverflowError: A StackOverflowError is raised when the stack requested by a thread is larger than the maximum depth allowed by the virtual machine. The most common scenario is an infinite recursive call to a method, as follows:

/** * Set the stack size for each thread: -xss256k * At run time, doSomething() method is constantly called, the main thread is constantly creating stack frames to merge into the stack, resulting in the stack depth more and more, eventually resulting in stack overflow. */ public class StackSOF { private int stackLength=1; public void doSomething(){ stackLength++; doSomething(); } public static void main(String[] args) { StackSOF stackSOF=new StackSOF(); try { stackSOF.doSomething(); Println (" stack depth: "+stackSOF. StackLength);}catch (Throwable e){ throw e; }}}Copy the code

Throws: after the above code execution Exception in the thread “thread – 0” Java. Lang. StackOverflowError abnormalities.

(2) OutOfMemoryError: An OutOfMemoryError is thrown if the VM cannot obtain sufficient memory space when extending the stack. For example, if the memory of a machine is 4G, the system and other applications take up 2G, the available physical memory of the VIRTUAL machine is 2G, the maximum heap memory is 1G, and the maximum method area memory is 512M. If we set the size of each thread stack to 1M, the maximum number of threads to be created in the virtual machine is 512. If we set the size of each thread stack to 1M, the maximum number of threads to be created in the virtual machine is 512.



The following is an example of an OutOfMemoryError that can be generated on the stack:

/** * Set stack size for each thread: -xss2m * run, constantly create new threads (and each thread continues to execute), each thread against a stack, and eventually no more space to allocate for new threads, OutOfMemoryError */ public class StackOOM {private static int threadNum = 0; public void doSomething() { try { Thread.sleep(100000000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { final StackOOM stackOOM = new StackOOM(); try { while (true) { threadNum++; Thread thread = new Thread(new Runnable() { @Override public void run() { stackOOM.doSomething(); }}); thread.start(); }} catch (Throwable e) {system.out.println (" current active thread count: "+ threadNum); throw e; }}}Copy the code

Abnormal transactions after the above code to run, can be seen in the stack information. Java lang. OutOfMemoryError: unable to create new native thread information, unable to create new threads, that is produced when extended stack memory exceptions.

To solve this problem, you can properly increase the stack depth (increase the stack space size), that is, set the value of -xss to a larger value. However, in general, code problems are more likely. A OutOfMemoryError will be reported when a thread is created on the VM. This problem can be solved by reducing the stack depth appropriately, that is, setting the value of -xss to a smaller value. If each thread occupies a smaller space, the total space must be able to accommodate more threads. However, the operating system has a limit on the number of threads in a process. The rule of thumb is about 3000 to 5000. Before jdK1.5 -Xss default is 256K, after JDK1.5 default is 1M, this option is quite hard on the system, should be set according to actual conditions, use caution.

3. Overflow of method area

The method area is used to store virtual machine loaded class information, constants, static variables, and compiled code. The reason for the overflow of the method area is that there is not enough memory to store this data.

Since string constant pools existed in the method area prior to JDK1.6, a pre-JDK1.6 virtual machine can simulate OutOfMemoryError exceptions in the method area by constantly generating inconsistent strings (while ensuring that there is a reachable path between GC Roots and the string). But the method area also stores information about loaded classes, so A JDk1.7-based virtual machine can simulate method area overflow by constantly creating a large number of classes on the move.

/** * Set method area maximum and minimum space: -xx :PermSize=10m -xx :MaxPermSize=10m * Cglib: JavaMethodAreaOOM */ Public class JavaMethodAreaOOM {public static void main(final String[] args){try {while (true){ Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(JavaMethodAreaOOM.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o,objects); }}); enhancer.create(); } }catch (Throwable t){ t.printStackTrace(); }}}Copy the code

After the above code to run the “Java. Lang. OutOfMemoryError: PermGen space” exception, that is the method area appeared out of memory error.

4. Local direct memory overflow

DirectMemory is not part of the virtual machine’s run-time data area, nor is it defined in the Java VIRTUAL Machine specification. However, when Java uses NIO-related operations (such as ByteBuffer’s allocteDirect method, which requests nATIVE-direct memory), Memory overflow exceptions may also occur.

JVM garbage collection


Garbage collection, is through the garbage collector to clean up useless objects in memory. Garbage collection involves the following contents: 1, determine whether the object is dead; 2. Select the garbage collection algorithm; 3. Choose the time of garbage collection; 4. Select the appropriate garbage collector to clean up garbage (dead objects).

1. Check whether the object is dead

To judge whether objects are dead is to find out which objects are dead and will not be used in the future, just like waste paper, drink bottles and hundred-dollar bills on the ground. Before sweeping the floor, we should judge that waste paper and drink bottles on the ground are garbage, and hundred-dollar bills are not garbage. There are reference counting algorithms and reachability analysis algorithms to determine whether an object is dead.

(1) Reference counting algorithm

Add a reference counter to each object, incrementing the counter each time a reference is made to it; Whenever a place no longer refers to it, the counter value is reduced by 1, so that as long as the value of the counter is not zero, it is referred to somewhere else and it is not a useless object. In the figure below, object 2 has one reference and its reference counter value is 1. Object 1 has two local references and its reference counter value is 2.

This method looks very simple, but now many of the mainstream of the virtual machine is no use this algorithm to manage memory, the reason is that when some object reference each other between, unable to determine whether the object is dead, as the chart, the object 1 and object 2 have never been outside the heap variable references, but by the other party reference each other, though they didn’t use at this time, However, the reference counter still has a value of 1, so there is no way to determine that they are dead objects and the garbage collector cannot collect them.

(2) Accessibility analysis algorithm

Before understanding the reachabilities analysis algorithm, we should first understand a concept — GC Roots, the starting point of garbage collection, which can be used as GC Roots for objects referenced in the local variable table in the virtual machine stack, objects referenced by static attributes in the method area, objects referenced by constants in the method area, and objects referenced by JNI (Native method) in the local method stack.

When an object is not connected to GC Roots by any reference chain (GC Roots are unreachable to this object), the object is unavailable and dead. There are reachable paths between OBJECT1, Object2, Object3, Object4 and GC Roots. These objects will not be recycled, but there are no reachable paths between Object5, Object6, object7 and GC Roots. These objects are condemned to death.



Object5, Object6, Object7 are not doomed to death. There is room for redemption. When there is no reference chain between objects and GC Roots after the reachabability analysis, objects will be marked once, and then objects will be executed (cleared) if finalize() method does not overwrite objects or has been called by virtual machine. If the object overwrites the Finalize () method and has not been called yet, the content of finalize() method will be executed, so you can save yourself by re-associating with the object in the GC Roots reference chain, but it is generally not recommended to do so. Zhou zhiming also suggested that you should forget this method completely

(3) Method area recovery

All of the above is the judgment of objects in the heap memory, the method area is mainly recycled discarded constants and useless classes. You can determine if there is a place to reference the constant. If there is no reference, the constant is discarded. To determine whether a class is discarded, the following conditions must be met:

  • All instances of the class have been reclaimed (there are no instances of the class in the heap)

  • The ClassLoader that loaded the class has been reclaimed

  • The java.lang.Class object corresponding to this Class is not referenced anywhere (there are no methods to access this Class through reflection)

2. Common garbage collection algorithms

There are three common garbage collection algorithms: mark-sweep algorithm, copy algorithm, and mark-collation algorithm.

(1) Mark-clearing algorithm: it is divided into two stages: marking and clearing. Firstly, all objects to be recycled are marked, and all marked objects are uniformly recycled after the marking is completed, as shown in the figure below.

Disadvantages: low efficiency in both marking and cleaning processes; A large number of discrete memory fragments are generated after the flag is cleared.

(2) Replication algorithm: the memory is divided into two pieces of equal size, and only one piece is stored each time. When this piece is used up, all the surviving objects are copied to the other piece, and all the used memory space is cleaned up, and the cycle is repeated, as shown in the figure below.

Disadvantages: the actual available memory space is reduced to half of the original, more suitable.

(3) Mark-collation algorithm: mark available objects first, then all marked objects move to a segment, and finally clear the memory beyond the boundary of available objects, as shown in the following figure.

(4) Generational collection algorithm: the heap memory is divided into the new generation and the old generation, and the new generation is divided into Eden region, From Survivor and To Survivor. In general, objects in the new generation are basically born and die, and only a small number of objects survive each time. Therefore, using the replication algorithm, only those small number of surviving objects can be copied to complete garbage collection. In the old age, the survival rate of the object is higher, so the algorithm of mark-clearing and mark-sorting is used for recycling.

Garbage collection in these areas may be as follows:

  • In most cases, new objects are allocated in Eden. When there is no space in Eden for allocation, a Minor GC is performed to clean up the useless objects in Eden. After clearing, the surviving objects in Eden and From Survivor enter To Survivor if the available space is smaller than that of To Survivor, otherwise enter the old age directly); The age of the Eden and From Survivor objects that are still alive and can enter To Survivor is increased by 1 year (the virtual machine defines an age counter for each object, and the age of each Minor GC is increased by 1). When the age of the Survivor object reaches a certain age (15 by default), it enters the old age. You can set the age value by running -xx :MaxTenuringThreshold.

  • After Minor GC, Eden will not be able to allocate space for the new object (which must be large), and the new object will go straight to the old age.

  • For objects that occupy more than half of the To Survivor space and are of the same age, those whose age is greater than or equal To this age will directly enter the old age. For example, if the Survivor space is 10M, several objects whose age is 4 occupy a total space of more than 5M, then all objects whose age is greater than or equal To 4 will directly enter the old age. There is no need to wait until MaxTenuringThreshold specifies an age.

  • Before the Minor GC, will determine whether the old s largest continuous space available is greater than the new generation total space, all objects if greater than, that Minor GC is safe, otherwise it will determine whether to allow guarantees failure, if allowed, whether old s largest continuous space available is greater than all previous promotion to the old s average size of the object, If greater, Minor GC is performed, otherwise Full GC is performed.

  • When calling System.gc() directly from Java code, the JVM is recommended to perform Full GC, but Full GC is usually triggered and is not recommended. Try to let the VIRTUAL machine manage gc policies.

  • The permanent generation (method area) is used to store class information. In JDK1.6 and earlier, the permanent generation also stores constants, static variables, etc. When the space of the permanent generation is insufficient, the Full GC is also triggered. If the permanent generation cannot store new data through the Full GC, the permanent generation will throw an out-of-memory exception.

  • Large objects (objects that require a lot of contiguous memory), such as long arrays, go straight to the old age, and if there is not enough contiguous space for the old age, Full GC is performed.

3. Choose the time of garbage collection

All kinds of data, objects, threads, memory, etc. are changing all the time while the program is running. Should garbage collection be done immediately after the command is issued? Definitely not. Here are two concepts: Safepoint and Safe Region. Safe point: From the perspective of threads, safe point can be understood as some special points during code execution. When a thread reaches the safe point, it indicates that the virtual machine is safe. If necessary, the user thread can be suspended at this point. If you need to suspend current user threads while garbage collection is going on, but the user threads are not at the safe point at the time, you should wait for those threads to execute to the safe point before suspending them. For example, the mother is sweeping the floor and the son is eating watermelon (the skin will be thrown to the ground). When the mother sweeps to the son, the son says: “Mom, wait a minute, let me finish this piece and then sweep.” After the son finishes eating the watermelon and throws the rind on the ground, it is a safe spot for the mother to continue sweeping the floor (the garbage collector can continue collecting garbage). In theory, a safety point can be placed at the boundary of each bytecode in the interpreter. In practice, the safety point is basically selected based on whether it has the characteristics that make the program run for a long time. Safe zone: A safe zone is relative to a running thread. For a thread in a state such as sleep or Blocked, the collector does not wait for CPU time to be allocated. As long as the thread is in a safe zone, it is safe. A security zone is a safe point in a code snippet where references do not change and can be seen as an extended, stretched safety point. The above examples show that the mother is sweeping the floor, the son is eating watermelon (melon rind will be thrown to the ground), the mother sweep to the son, the son said: “Mom, you continue to sweep the floor, I have to eat 10 minutes!” While the son is eating the melon, the mother can continue to sweep the floor (garbage collector can continue to collect garbage).

4. Common garbage collectors

There are several common garbage collectors today

Cenozoic collectors: Serial, ParNew, Parallel Insane

Serial Old, CMS, Parallel Old collector

Heap memory garbage collector: G1

There are wires between each garbage collector to indicate that they can be used together.

(1) Serial collector

Serial is a single-threaded collector for the new generation that uses a replication algorithm for garbage collection. Serial does garbage collection not only with one thread, but all user threads must Stop The World while it collects. For example, when my mother is cleaning at home, she will not let her son throw paper on the ground while cleaning, otherwise, she will make garbage and clean up the garbage again.

Serial collector and Serial Old collector are used to collect garbage. When the user threads have reached a safe point, all threads are suspended. Serial collector is a single-thread garbage collector that uses a replication algorithm to collect garbage.

Application scenario: Client mode (desktop application). Single-core server. Serial can be selected as a Cenozoic collector with -xx :+UserSerialGC.

2) ParNew collector

ParNew is a multithreaded version of Serial, otherwise no different from Serial. ParNew does not perform better than the Serial collector on a single-core CPU. By default, the number of garbage collection threads enabled is the same as the number of cpus. You can set the number of garbage collection threads by using -xx :ParallelGCThreads.

The following is a schematic diagram of the combination of ParNew collector and Serial Old collector for garbage collection. When the user threads are executed to a safe point, all threads are suspended. ParNew collector uses multiple threads, using the replication algorithm for garbage collection, after collection, the user threads continue to execute.



Application scenario: Multi-core server; Used with the CMS collector. When using -xx :+UserConcMarkSweepGC to select CMS as the old collector, the new collector defaults to ParNew. You can also use -xx :+UseParNewGC to specify ParNew as the new collector.

The Avenge

The Parallel Insane is a next-generation multithreaded collector. Unlike ParNew, the Parallel Insane aims to minimize the downtime of user threads during garbage collection, and the Parallel Insane aims to achieve a controlled throughput. Throughput is the ratio of the amount of time the CPU takes to execute user threads to the total amount of time the CPU takes to execute user threads. For example, if the virtual machine runs for 100 minutes and garbage collection takes 1 minute, the throughput is 99%. For example, in the following two scenarios, the garbage collector collects every 100 seconds and pauses for 10 seconds, and the garbage collector collects every 50 seconds and pauses for 7 seconds. Although the latter pauses for a shorter time, the overall throughput is lower and the overall CPU utilization is lower.

Collect frequency Each pause time throughput
Collect every 100 seconds 10 seconds 91%
Collect every 50 seconds 7 seconds 88%

You can set the maximum amount of time for the collector to complete memory collection by -xx :MaxGCPauseMillis, and you can control throughput precisely by -xx :GCTimeRatio.

Parallel collector and Parallel Old collector combined garbage collection schematic diagram, in the new generation, when the user thread is executed to the safe point, all threads suspended execution, ParNew collector to multithreading, using the replication algorithm garbage collection work, after the collection, the user thread continues to execute; In the Old days, when all user threads had reached a safe point, all threads would suspend execution. Parallel Old collector uses multiple threads and a tag collation algorithm for garbage collection.



Application scenario: Focuses on throughput, uses CPU efficiently, requires efficient computing and does not require much interaction. To be insane, use -xx :+UseParallelGC to select the Parallel Scavenge avenge. The jdK7, JDK8 and Jdk7 avenge avenge.

(4) Serial Old collector

The Serial Old collector, an older version of Serial, is also a single-threaded collector with a mark-collation algorithm.

The Serial collector and Serial Old collector combined for garbage collection are shown below:



Application scenario: Client mode (desktop application). Single-core server; Avenge with the Parallel Scavenge; As a backup to the CMS collector.

(5) CMS(Concurrent Mark Sweep) collector

The CMS collector is a collector that aims for the shortest collection pause time, known as the “shortest user thread pause time.” The garbage collection process is divided into four steps:

Initial marking: it is faster to mark objects that GC Roots can be directly associated with. Concurrent marking: IT takes longer to mark all garbage objects with GC Roots Tracing. Fix the mark record of the changed object caused by the continuous operation of the user program in the concurrent marking stage, which takes a short time. ④ Concurrent clearing: it takes a long time to clear the garbage object with the mark-clearing algorithm

The most time-consuming concurrent tagging and concurrent cleanup work with the user thread, so in general, the CMS collector garbage collection can be viewed as being performed concurrently with the user thread.



The CMS collector also has some disadvantages:

  • Sensitive to CPU resources: The default number of garbage collection threads allocated is (number of cpus +3) /4. As the number of cpus decreases, the more CPU resources consumed, the lower throughput

  • Unable to handle floating garbage: During the concurrent cleanup phase, the CMS collector is unable to remove this part of garbage in the current collection because the user thread is still running and new garbage is constantly being generated. Because the user threads are also executing concurrently during the garbage collection phase, the CMS collector cannot wait until the ages are filled, as other collectors do, and needs to reserve space for the user threads to run. When CMS is running and the reserved memory cannot meet the needs of user threads, a “Concurrent Mode Failure” will occur, and a backup plan will be initiated, temporarily using Serial Old to restart the Old garbage collection.

  • Because the CMS is based on the tag – clear algorithm, so after garbage collection can produce space debris, can pass – XX: UserCMSCompactAtFullCollection open defragmentation open (the default), before CMS for Full GC, to memory fragments. Still can use – XX: CMSFullGCsBeforeCompaction set execute many times without compression (without defragmentation) after a Full GC, followed by a Full GC with compression (defragmentation). Application scenario: Server response speed and minimum system pause time are required. You can use -xx :+UserConMarkSweepGC to select the CMS as the old-age collector.

(6) Parallel Old collector

The Parallel Old collector is an older version of the Parallel Avenge, a multi-threaded collector that uses a mark-collation algorithm. Can be used with the Parallel Exploiter to exploit the computing power of multi-core cpus.



Application scenarios: Use with the Parallel Exploiter; Focus on throughput. By default, jdk7 and JDK8 use this collector as the old-age collector. Use -xx :+UseParallelOldGC to specify the use of Paralle Old collector.

(7) G1 collector

The G1 collector was officially referenced commercially in jdk1.7 and is now the default collector in jdk9. G1 collects garbage from the entire heap. It divides the whole heap into multiple independent regions of equal size. The concept of new generation and old age is retained in G1 collector. They are all part of regions, as shown in the following figure:



Each square is a region, and each region may be Eden, Survivor, or old age, or the number of each region may not be the same. JVM startup automatically sets the size of each region (1M~32M, must be a power of 2), can be set to a maximum of 2048 regions (that is, the maximum supported heap memory is 32M*2048=64G), if set to -XMx8g-xMS8g, each region size is 8G /2048=4M.

In order to avoid scanning the whole heap when GC Roots Tracing, there is a Remembered Set in each Region to record the reference relationship between the reference type data in this Region and the reference data in other regions in real time. There is also a Remembered Set for the new generation and old generation to record references to other regions in real time), and reference these references directly when marking to see if objects should be cleared without scanning the whole heap.

The G1 collector can “model predictable pause times” by maintaining a list of the value of each Region’s collection (the amount of space acquired after collection and the empirical value of the time it took to collect) to ensure that the G1 collector achieves maximum collection efficiency in a limited amount of time.

As shown in the following figure, the G1 collector has an initial mark, concurrent mark, final mark, and filter collection process, which is similar to the previous steps of the CMS collector:



(1) Initial mark: mark objects directly associated with GC Roots. This stage is faster and requires the user thread to be stopped and executed by a single thread

(2) Concurrent marking: Starting from GC Root, reachable objects in the heap are analyzed to find viable objects. This phase is time-consuming, but can be performed concurrently with the user thread

(3) Final mark: correct the mark record that changes during the concurrent mark stage caused by the execution of user program

④ Screening and recycling: In the screening collection phase, the collection value and cost of each Region are sorted, and the collection plan is specified according to the expected GC pause time of users (the Region containing the most Garbage is collected in the least time, which is the origin of Garbage First — the block with the most Garbage is cleaned in the First time). In order to improve the collection efficiency, Instead of executing concurrently with the user thread, the user thread is paused.

Application scenario: GC pause time should be controllable as much as possible. Applications with large memory usage. The G1 collector can be used with -xx :+UseG1GC; jdK9 uses the G1 collector by default.

Fourth, JVM performance tuning


1. JVM tuning goal: Higher throughput or lower latency with a smaller footprint.

Applications that are tested or run before they go live sometimes have large and small JVM problems, such as high CPU load, request latency, reduced TPS, and even memory leaks (garbage collections are used for longer and more frequently each time, Each garbage collection cleans up less and less garbage data), and memory overruns lead to system crashes, so the JVM needs to be tuned to make the program run properly with a higher user experience and efficiency.

Here are a few important indicators:

  • Memory footprint: The amount of memory required by a program to run properly.

  • Latency: The amount of time a program pauses due to garbage collection.

  • Throughput: The ratio of the elapsed time of a user program to the total elapsed time of a user program and garbage collection.

, of course, and the principle of the CAP, and satisfy a program small footprint, low latency, high throughput is impossible, the application of different goals, to consider when tuning direction is different also, before tuning, must be combined with the actual scene, have clear the optimization goal, find the performance bottleneck, the bottleneck of targeted optimization, the final test, Use various monitoring tools to verify that the tuned results meet the objectives.

2. JVM tuning tools

(1) Data that tuning can rely on and refer to include system run logs, stack error messages, GC logs, thread snapshots, heap dump snapshots, etc.

(1) System run log: system run log is a log printed out in the program code, describing the code level of the system running track (method of execution, entry, return value, etc.), general system problems, system run log is the first to view the log.

Error message: (2) the stack when the system is abnormal, can according to the preliminary stack information positioning problem, such as according to the “Java. Lang. OutOfMemoryError: Java heap space” to judge is a heap of memory; According to the “Java. Lang. StackOverflowError” judging is stack overflow; According to the “Java. Lang. OutOfMemoryError: PermGen space” to judge is overflow method, etc.

③ GC log: -xx :+PrintGCDetails and -xloggc :/data/ JVM/gC.log can be used to record the gc details while the program is running, or directly configure the “-verbose: GC” parameter to print the GC log to the console. The gc logs can be used to analyze the gc frequency, timing, and so on for each memory region to identify problems and optimize them accordingly. For example, the following GC log:

The 2018-08-02 T14: it happened. 560-0800:10.171: [GC [PSYoungGen: [Times: Times: 328k -> 328k [Times: 328K] 328K -> 328k [Times: 328k] 328K -> 328k [Times: 328k] 328K -> 328k [Times: 328k] 328K -> 328k [Times: 328k] User =0.02 sys=0.03, real= 0.01secs] 2018-08-02T14:39:11.574-0800:10.185: [PSYoungGen: 4091K->0K(30208K)] [ParOldGen: 46698K->50669K(68608K)] 50790K->50669K(98816K) [PSPermGen: [Times: user= 0.03sys =0.00, real= 0.03secs] 2018-08-0t14:39:14.045-0800:2018-08-0t14:39:14.045-0800:2018-08-0t14:39:14.045] [Times: user= 0.03sys =0.00, real= 0.03secs] 2018-08-0t14:39:14.045-0800: 12.656: [GC [PSYoungGen: 14097K->4064K] 64766K->64536K, 0.011769secs] [Times: User =0.02 sys=0.01, real=0.01 secs] 2018-08-02T14:39:14.057-0800:12.668: [PSYoungGen: 4064K->0K(30208K)] [ParOldGen: 60471K->401K(68608K)] 64536K->401K(98816K) [PSPermGen: [Times: user= 0.03sys =0.00, real= 0.03secs] [Times: user= 0.03sys =0.00, real= 0.03secs]Copy the code

[2018-08-02T14:39:11.560-0800] [2018-08-02T14:39:11.560-0800] [2018-08-02T14:39:11.560-0800] [2018-08-02T14:39:11.560-0800] [2018-08-02T14:39:11.560-0800] The “-xx :+PrintGCDateStamps” parameter is configured to print this timestamp following the GC log. “10.171” is the number of seconds that elapsed between JVM startup and gc. [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] [Full GC] Depending on the type of garbage collector, if system.gc () is called directly, [Full GC(System) is displayed. The following “[PSYoungGen”, “[ParOldGen” denotes the area in which GC occurs, and the specific name is specified by the garbage collector, as in the case of the [PSYoungGen “denotes the Parallel Scavenge collector. “[ParOldGen” represents the Serial Old collector, in addition, the Serial collector displays “[DefNew”, the ParNew collector displays “[ParNew”, and so on. The following “30128K->4091K(30208K)” indicates that after the GC, the memory usage in this area decreases from 30128K to 4091K, and the total memory size is 30208K. After this garbage collection, the memory usage of the entire heap memory is reduced from 51092K to 50790K, and the total heap memory space is 98816K. Gc takes 0.0140970 seconds.

④ Thread snapshot: As the name implies, you can see the status of the thread at a certain time according to the thread snapshot. When the system may have request timeout, deadlock loop, deadlock, etc., you can further determine the problem according to the thread snapshot. By performing a virtual machine’s own “jstack pid” command, you can dump the threads in the process of the current snapshot information, more detailed analysis and use of the Internet has a lot of cases, this article wrote here already very long but more narrative, posted a blog for your reference: www.cnblogs.com/kongzhongqi…

⑤ Heap dump snapshot: The program can be started with “-xx :+HeapDumpOnOutOfMemory” and “-xx :HeapDumpPath=/data/ JVM /dumpfile.hprof”. When the program runs out of memory, Dump the current memory snapshot as a file (you can also use the jmap command to dump the memory snapshot at any time when the program is running), and then analyze the memory usage.

(2) JVM tuning tools

JPS (JVM Process Status) allows you to view all processes started by the VM, the full name of the executing main class, and the JVM startup parameters. Execute JPS -l to see the JPSTest class below has a PID of 31354, plus the -v parameter to see the JVM startup parameters.

3265 
32914 sun.tools.jps.Jps
31353 org.jetbrains.jps.cmdline.Launcher
31354 com.danny.test.code.jvm.JPSTest
380 Copy the code

Jstat -gc PID 500 10: The Java heap status is displayed every 500 milliseconds for 10 times

S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
11264.0 11264.0 11202.7  0.0   11776.0   1154.3   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   4037.0   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   6604.5   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   9487.2   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0  0.0    0.0   11776.0   258.1    68608.0    58983.4     -      -      -      -        15    0.082   8      0.059    0.141
11264.0 11264.0  0.0    0.0   11776.0   3076.8   68608.0    58983.4     -      -      -      -        15    0.082   8      0.059    0.141
11264.0 11264.0  0.0    0.0   11776.0    0.0     68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0    0.0     68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0   258.1    68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0   3012.8   68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149Copy the code

Jstat can also monitor extents’ memory size, class loading information, and so on from other perspectives, as detailed by Google jstat usage.

Jmap-histo PID prints the number of instances and Memory usage of each class in the current heap, as follows: class name is the name of each class (B is byte type, C is char type, Instances are the number of instances of this class: instances are the number of instances of this class: instances are the number of instances of this class: instances are the number of instances of this class: instances are the number of instances of this class.

num     #instances         #bytes  class name
----------------------------------------------
   1:          2291       29274080  [B
   2:         15252        1961040  <methodKlass>
   3:         15252        1871400  <constMethodKlass>
   4:         18038         721520  java.util.TreeMap$Entry
   5:          6182         530088  [C
   6:         11391         273384  java.lang.Long
   7:          5576         267648  java.util.TreeMap
   8:            50         155872  [I
   9:          6124         146976  java.lang.String
  10:          3330         133200  java.util.LinkedHashMap$Entry
  11:          5544         133056  javax.management.openmbean.CompositeDataSupportCopy the code

Jmap-dump dumps heap memory snapshots to a specified file. For example, jmap -dump:format=b,file=/data/ JVM/Dumpfile_jmap. hprof 3361 dumps a snapshot of the current heap memory to a file dumpfile_jmap.hprof, which can then be analyzed.

④ Use JConsole and JVisualVM to analyze the memory information (memory changes in each area such as Eden, Survivor, Old, etc.). If the JVM of the remote server is viewed, the program needs to add the following parameters:

"- Dcom. Sun. Management jmxremote = true" "- Djava. Rmi. Server. The hostname = 12.34.56.78" "-Dcom.sun.management.jmxremote.port=18181" "-Dcom.sun.management.jmxremote.authenticate=false" "-Dcom.sun.management.jmxremote.ssl=false"Copy the code

Here is the JConsole interface with the overview option to observe the heap memory usage, threads, classloads, and CPU usage. The memory option allows you to view the amount of memory used in each region of the heap with a detailed description (memory size, GC status, and so on) in the lower left corner; The thread option allows you to view the threads currently loaded by the JVM, see stack information for each thread, and detect deadlocks; The VM profile describes detailed VM parameters. (Jconsole function demonstration)

The jVisualVM interface, shown below, is a little more featurethan JConsole, although most of the features require plug-ins to be installed. The overview is similar to the VM profile of JConsole, which describes the detailed parameters of the JVM and program startup parameters. Monitoring shows much the same overview interface as JConsole (CPU, heap/method area, class loading, threading); Threads are similar to jConsole’s threading interface; The sampler can display the ranking of the current memory occupying classes and the number of instances; Visual GC provides a richer picture of the current memory footprint and historical information for each region (figure below). (Jvisualvm function demonstration)

⑤ Analyze heap dump snapshots

If the -xx :+HeapDumpOnOutOfMemory parameter is configured, you can dump a snapshot of the current memory when the program runs out of memory. You can also use the jmap command to dump a snapshot of the current memory status. Memory snapshots in dump are usually binary files with a. Hprof suffix. Memory snapshots can be analyzed directly using the JHAT (JVM Heap Analysis Tool) command, which is essentially embedded with a tiny server that can be analyzed through a browser. For example, jhat-port 9810 -j -xmx4g /data/ JVM /dumpfile_jmap.hprof is used to start jHAT embedded server with port 9810:

Reading from /Users/dannyhoo/data/jvm/dumpfile_jmap.hprof...
Dump file created Fri Aug 03 15:48:27 CST 2018
Snapshot read, resolving...
Resolving 276472 objects...
Chasing references, expect 55 dots.......................................................
Eliminating duplicate references.......................................................
Snapshot resolved.
Started HTTP server on port 9810
Server is ready.Copy the code

On the console you can see that the server is started, accesshttp://127.0.0.1:9810/You can see the snapshot of the results of each class analysis (interface slightly low), the following is I randomly selected a class of information, there is the parent class of this class, load the class loader and the size of the space occupied by the class, and the following each instance of this class (References) and its memory address and size, Click in to display the instance’s member variables and other information:

Jvisualvm can also analyze the memory snapshot, in the JVisualVM menu “file” – “load”, select heap memory snapshot, snapshot information is displayed in the graphical interface, as follows, mainly can view the space occupied by each class, the number of instances and instance details:

There are also a number of third-party tools for analyzing memory snapshots, such as Eclipse MAT, which is more specialized than JVisualVM in terms of looking at the space and amount of each class and its corresponding instance, looking up call chains between objects, looking at links between an instance and GC Root, and so on. Can be installed in the eclipse mat plug-in, also independent version can be downloaded (www.eclipse.org/mat/downloa)… I installed it on MAC and ran it. The following is a screenshot on Windows (MAT function demo) :

(3) EXPERIENCE in JVM tuning

JVM configuration, can first use the default configuration (some basic initial parameters can ensure general application run more stable), according to the system running status in the test (concurrent conditions, a time of the session, etc.), in combination with gc logs, memory monitoring, use reasonable adjustments, such as the garbage collector Too little memory can cause frequent Full GC, and too much memory can take too long.

What is the best JVM configuration for new generation and old generation? The answer is not necessarily, tuning is the process of finding the answer, under the condition of a certain physical memory, the larger the new generation setting, the smaller the old generation, the higher the Full GC frequency, but the shorter the Full GC time; On the contrary, the smaller the new generation setting is, the larger the old generation is, and the lower the Full GC frequency is, but the longer the Full GC takes each time. The suggestions are as follows:

  • -xms and -xmx are set to equal, and the default heap size is the size specified by -xms. When the default free heap is less than 40%, the JVM will expand the heap to the size specified by -xmx. When free heap memory is greater than 70%, the JVM reduces the heap to the size specified by -xms. Dynamic adjustments are made if memory requirements are not met after Full GC, which is a more resource-intensive phase.
  • Make the minicars as large as possible to keep objects alive for a longer period of time in the minicars, and each Minor GC should collect as many garbage objects as possible to prevent or delay the chances that the objects will age to reduce the frequency of Full GC occurrences in the application.
  • If the CMS collector is used in the old era, the new generation can be less large, because the parallel collection speed of CMS is also very fast, and the time-consuming concurrent marking and concurrent cleaning phase of the collection process can be performed concurrently with the user thread.
  • For setting the size of the method area, before 1.6, it is necessary to consider the constant and static variables dynamically added during the system running, and 1.7, as long as it can almost hold the class information dynamically loaded at startup and later stage.

In addition to the JVM configuration, there may be problems with the code implementation.

  • Avoid creating objects and arrays that are too large: Objects or arrays that are too large for the new generation will go straight to the old generation, and short-lived large objects will go to Full GC early.

  • Avoid loading a large amount of data at the same time, such as loading a large amount of data from a database or reading a large number of records from Excel at one time. Instead, load the data in batches and clear references as soon as possible.

  • When there are references to objects in the collection, the references in the collection should be cleared as soon as possible after the use of these objects, and these useless objects should be recycled as soon as possible to avoid entering the old age.

  • You can use soft references or weak references in appropriate scenarios (such as caching). For example, assign an instance to an ObjectA using a SoftReference. SoftReference ObjectA =new SoftReference(); Before a memory overflow occurs, objectA will be included in the collection scope for a second collection, and if there is not enough memory in the collection, an exception will be thrown. Avoid creating an infinite loop. After an infinite loop is created, a large number of instances may be created repeatedly in the loop body, causing the memory space to be used up quickly.

  • Try to avoid waiting for external resources (database, network, device resources, etc.) for a long time, reduce the life cycle of the object, avoid entering the old age, if the result cannot be returned in time, you can appropriately use asynchronous processing.

(4) JVM troubleshooting record cases

The JVM service problems screening blog.csdn.net/jacin1/arti…

A memorable Full screen frequently caogen81.iteye.com/blog/151334 GC process…

Online FullGC frequent screening blog.csdn.net/wilsonpeng3…

Online application troubleshooting JVM 】 【 www.cnblogs.com/Dhouse/p/78…

Once the JVM FullGC problem iamzhongyong.iteye.com/blog/183026 screening process…

The JVM memory leak caused by excessive CPU problem blog.csdn.net/nielinqi520 screening cases…

A screening of Java memory leak case blog.csdn.net/aasgis6u/ar…

(5) Common JVM parameters reference:

parameter instructions The instance
-Xms Initial heap size, 1/64 of the default physical memory -Xms512M
-Xmx Maximum heap size, 1/4 of the default physical memory -Xms2G
-Xmn The new generation memory size is officially recommended as 3/8 of the entire heap -Xmn512M
-Xss Thread stack size, 1M by default for JDK1.5 and later, 256K before -Xss512k
-XX:NewRatio=n Set the ratio of the new generation to the old generation. For example, is 3, indicating that the ratio of the young generation to the old generation is 1:3, and the young generation accounts for 1/4 of the sum of the young generation and the old generation -XX:NewRatio=3
-XX:SurvivorRatio=n Ratio of Eden zone to two Survivor zones in the young generation. Notice that there are two Survivor zones. For example, 8 indicates Eden: Survivor=8:1:1, and a Survivor zone accounts for 1/8 of the entire young generation -XX:SurvivorRatio=8
-XX:PermSize=n The default value of the permanent generation is 1/64 of the physical memory -XX:PermSize=128M
-XX:MaxPermSize=n Maximum number of permanent generations. The default value is 1/4 of the physical memory -XX:MaxPermSize=256M
-verbose:class Print class loading information on the console
-verbose:gc Prints the garbage collection log on the console
-XX:+PrintGC Print GC logs, simple content
-XX:+PrintGCDetails Print GC logs with detailed content
-XX:+PrintGCDateStamps Add a timestamp to the GC log
-Xloggc:filename Specify the GC log path -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC The young generation sets the Serial collector Serial
-XX:+UseParallelGC The younger generation sets the Parallel collector Parallel Insane
-XX:ParallelGCThreads=n Set the number of cpus to use when collecting the Parallel Avenge. Collect the number of threads in parallel. -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n Set the maximum time to recycle the Insane (ms). -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n Set the Parallel Recycle time as a percentage of the application run time. Formula for 1 / (1 + n) -XX:GCTimeRatio=19
-XX:+UseParallelOldGC Set the old generation collector to ParallelOld
-XX:+UseConcMarkSweepGC Set up the old concurrent collector CMS
-XX:+CMSIncrementalMode Set the CMS collector to incremental mode for single-CPU scenarios.

Class loading


Java code is written to be compiled by the compiler into a class file (the process of converting native machine code into bytecode), a class file is a set of binary streams based on 8-bit bytes, These binary streams represent the magic number (used to identify whether it is a Class file that can be received by the virtual machine), version number, field table, access identifier, and so on. After the code is compiled into a class file, the class file needs to be loaded into the VIRTUAL machine through the class loader to run and use it.

1. Class loading steps

From being loaded into memory to being used, a class needs to go through loading, connecting, initializing, using, and unloading. Connecting can be subdivided into verification, preparation, and parsing.

(1) Loading

During the load phase, the virtual machine does three main things: (1) get the binary stream that defines a class by its fully qualified name (e.g. Com.danny.framework.t); ② Transform the static storage structure represented by this byte stream into the runtime storage structure of the method area; (3) Generate a java.lang.Class object representing the Class in memory as an external interface for the program to access the Class in the method area.

(2) Verification

The purpose of verification is to ensure that the byte stream of the class file meets the requirements of virtual machines and does not harm virtual machine security.

  • File format verification: mainly verifies the format of binary byte streams in class files, such as whether magic number starts with 0xCAFEBABY and whether the version number is correct.

  • Metadata validation: Performs semantic analysis of the information described by bytecode to ensure that it conforms to Java language specifications, such as verifying whether the class has a parent class (except java.lang.object), if the class is not abstract, whether it implements methods not implemented in the parent class or interface, and so on.

  • Bytecode verification: Bytecode verification is more advanced, through data flow and control flow analysis, to ensure that the program is legitimate and logical.

  • Symbolic reference validation: For example, can a class be found by its fully qualified name, can a field/method be found in the class by its field name/method name, and if symbolic reference validation fails, Will throw “Java. Lang. NoSuchFieldError” and “Java. Lang. NoSuchMethodError” such as abnormal.

(3) Preparation

Formally allocate memory for [class variables] and set the class variables [initial value]. The memory used by these variables is allocated in the method area. Note that the object that allocates memory is a “class variable”, not an instance variable, and that it is allocated an “initial value”. Generally, the initial value of numeric types is 0, char starts with a value of ‘\u0000’ (a string representing Nul in the constant pool), Boolean starts with a value of false, and reference types start with a value of null. Public static final int value=123; In the preparation phase, value is initialized to 123;

(4) Analysis

Parsing is the process of replacing symbolic references with direct references in a constant pool.

Symbolic references are a set of symbols that describe the referenced target. Symbolic references are independent of the memory layout implemented by the virtual machine, and the referenced target may not have been loaded into memory. Such as com. Danny. Framework. LoggerFactory class references the com. Danny. Framework. The Logger, but at compile time is don’t know the Logger class memory address, So can only use first com. Danny. Framework. The Logger (assumption is that this, in fact is represented by the similar to CONSTANT_Class_info constants) to represent the Logger class address, this is the reference symbols.

A direct reference can be a pointer directly to the target, a relative offset, or a handle that can be indirectly located to the target. A direct reference is related to the memory layout implemented by the virtual machine. If a direct reference is present, the target of the reference must exist in memory.

The class is already loaded into the method area’s memory at the time of parsing, so symbolic references are converted to direct references, that is, references that directly find the actual memory address of the class.

(5) Initialization

In the preparation phase, class variables have been assigned initial values. In the initialization phase, class variables and other resources are initialized according to the programmer’s customized subjective plan. It can also be understood from another perspective: initialization phase is the process of executing the class constructor () method. Class constructor () and instance constructor () methods are added to the syntax tree when Java generates bytecode if there is a static code block or assignment to a static variable in a class. Class constructor — () method and instance constructor — () method, which can be viewed with javap command), () is mainly used to construct classes, such as initializing class variables (static variables), executing static code blocks (statis{}), etc., this method is executed only once; () method is mainly used to construct an example, in the process of constructing instance, will be executed first (), when all the members of an object variable will be set to the default value (the default value for each data type and class loading stage described), then we will perform instance constructor (will first perform the superclass constructor, to perform the static block of code, Finally, the constructor is executed.

Here’s some code to understand:

Public class Parent {static {system.out.println (" parent-static code block execution "); } public Parent() {system.out.println (" parent-constructor execution "); } {system.out.println (" parent-non-static code block execution "); } } public class Child extends Parent{ private static int staticValue = 123; private int noStaticValue=456; Static {system.out.println (" child-static code block execution "); } public Child() {system.out.println (" child-constructor execution "); } {system.out.println (" child-non-static code block execution "); } public static void main(String[] args) { Child child = new Child(); }}Copy the code

You can guess what the result is before looking at the following results:

Parent-static code blocks execute child-static code blocks execute parent-non-static code blocks execute parent-constructor methods execute child-non-static code blocks execute child-constructor methodsCopy the code

The above example shows the loading sequence of static code blocks, constructors, and non-static code blocks from loading to instantiation of a class. You can’t see when static and non-static variables are initialized. Static variables and static code blocks are initialized during class initialization (()) and non-static variables and non-static code blocks are initialized during instance initialization (()).

Class loaders

(1) The role of the class loader

  • Load class: The first step in the loading phase of class loading is done through the classloader, whose main job is to “get the binary stream describing a class by its fully qualified name.” Here, the binary stream loaded by the classloader does not have to be retrieved from the class file. It can also be read from other formats such as zip files, from the network or a database, dynamically generated at run time, generated by other files (such as class files generated by JSP), and so on. From a programmer’s point of view, the classloader dynamically loads a class file into the virtual machine and generates a java.lang. class instance, each representing a Java class, from which information about the class can be obtained, and an object of the class can be generated through the newInstance() method.

  • Determining the uniqueness of a class: In addition to loading a class, the class loader has an important role to play. For each class, the uniqueness of the class in the Java virtual machine must be jointly established by the loader and the class itself. That is, two identical classes are “equal” only if they are loaded by the same loader. In this case, equality refers to the returns from equals(), isAssignableFrom(), isInstance(), and other methods of the Class object representing the Class. It also includes the judgment result of the instanceof keyword on the object ownership relationship.

(2) Classification of class loaders

From a developer’s point of view, class loaders fall into the following categories: Start the Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader, and custom User ClassLoader. The boot ClassLoader is part of the JVM, and the other class loaders are implemented in Java and ultimately inherit from java.lang.classloader.

Bootstrap ClassLoader is compiled by C/C++. The Bootstrap ClassLoader definition is native “Private Native Class findBootstrapClass(String name); . The bootloader is mainly responsible for loading the JAVA_HOME\lib directory or some classes in the directory specified by the -xbootclasspath parameter. You can check which classes are loaded by using “System.getProperty(” sun.boot.class.path”).

(2) the Extension class loader (Extension this) by the sun. The misc. The Launcher. ExtClassLoader implementation, All libraries that are responsible for loading the JAVA_HOME\lib\ext directory or in the path specified by the java.ext.dirs System variable can be checked by “System.getProperty(” java.ext.dirs”) “.

(3) the Application class loader (this) by the sun. The misc. The Launcher. AppClassLoader implementation, responsible for loading the user class path (we usually specify the classpath) on the class, if there is no custom class loader in the program, The application class loader is the default class loader for your program.

(4) Custom class loader (User ClassLoader), JVM class loader can only load specified directory classes (JAR and class), if we want to get class files from other places or even on the network, we need to customize the class loader to achieve. Custom classloaders are implemented primarily by inheriting ClassLoader or its subclasses, but either way, the parent of the custom ClassLoader is the application ClassLoader, because no matter which parent ClassLoader is called, Create object must be finally invokes the Java. Lang. This. GetSystemClassLoader () as a parent, GetSystemClassLoader () method is the return value of the sun. The misc. The Launcher. AppClassLoader application class loader.

(3) ClassLoader and parent delegate model

Let’s look at the loadClass() method, the core logic in the java.lang.classloader:

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// Check whether Class C = has been loaded findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! C = parent.loadClass(name, false); } else {// If the parent loader is empty, use the bootloader to load the class c = findBootstrapClassOrNull(name); }} Catch (ClassNotFoundException e) {} if (c == null) {// Long t1 = system.nanotime (); c = findClass(name); sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}Copy the code

So what this code basically means is that when a class loader loads a class, if there’s a parent loader, it tries to get the parent loader to load it, and if there’s a parent loader, it keeps throwing up, leaving the class loader to start the class, The class loader will throw a ClassNotFoundException if it fails to load the class, and then throw down the classloading task as shown in the following figure:

Through above class loading process, leads to the concept of a more important – parents delegation model, the diagram below shows the level of the relationship, parents delegation model requirements in addition to the start of the top class loader, the rest of the class loader should have a parent class loader, but the father and son relationship is not inherited, but relations as shown in the above code combination.

The way the parent delegate model works is, if a class loader receives a request for a class load, it doesn’t load the class first, it delegates the request to its parent loader at the level above it, every level, so eventually the request goes to the initiator class loader, and then from the initiator class loader it tries to load the class, If it can’t be loaded (the class to be loaded is outside the loading range of the current class loader), let its subclasses try to load, every layer.

So what are the benefits of the parental delegation model? The biggest benefit is that it gives classes in Java the same “priority” as classloaders. For each class, the uniqueness of the Java virtual machine must be established by both the loader and the class itself, such as the java.lang.Object class (stored in JAVA_HOME\lib\rt.jar). If a user writes a java.lang.Object class and it is loaded by a custom class loader, are there two classes in the program? So the parental delegation model is critical to keeping Java running smoothly.

Note: the above is a summary of the “Deep Understanding of the Java Virtual Machine”, part of the content is excerpted from the book, but also referred to countless online materials, in this thank you for your selfless sharing of the big god. [Reproduced please indicate the source — Hu Yuyang “JVM Learning Notes”]