Learning JVM from scratch (4) - the heap in the runtime data area

1 Overview

A JVM instance only has one heap memory. The heap is the core area of ​​Java memory management, and it is also the largest memory space managed by the JVM.It is created when the JVM starts, and its space size is determined. We can adjust the heap memory size according to the actual program situation

The "Java Virtual Machine Specification" stipulates that the heap can be in a physically discontinuous memory space, but logically it should be considered continuous. All threads share the Java heap, where a thread-private buffer (Thread Local Allocation Buffer, TLAB for short) can also be divided.

The description of the Java heap in the "Java Virtual Machine Specification" is: All object instances and arrays should be allocated on the heap at runtime. Arrays and objects may never be stored on the stack. Because the stack frame holds a reference, this reference points to the location of the object or array in the heap. The heap is the key area for GC (Garbage Collectioni, Garbage Collector) to perform garbage collection. After the method ends, the objects in the heap will not be removed immediately, but will only be removed during garbage collection.The heap is the key area for GC (Garbage Collection, Garbage Collector) to perform garbage collection

insert image description here

Official documentation: The Java® Virtual Machine
Specification

The "Java Virtual Machine Specification" clearly states: "Although all method areas are logically part of the heap, some simple implementations may not choose to perform garbage collection or compression."

The memory subdivision of the heap, here logically put the method area into the heap memory category: The
insert image description here
internal structure of the JDK1.7 heap space is shown in the figure:
insert image description here
The internal structure of the JDK1.8 heap space is shown in the figure:

insert image description here

2. Heap memory size setting and OOM

The Java heap area is used to store Java object instances, so the size of the heap has been set when the JVM starts, and you can set it through the options " " and -Xmx" ".-Xms

  • " -Xms" is used to indicate the starting memory of the heap area (young generation + old generation), -X flags the operating parameters of the JVM, and ms is memory start, which is equivalent to-XX:InitialHeapSize
  • " -Xmx" is used to indicate the maximum memory of the heap area, which is equivalent to-XX:MaxHeapSize

Once the memory size in the heap area exceeds -Xmxthe maximum memory specified by " ", an OutOfMemoryError exception will be thrown.During development, it is usually recommended to configure the two parameters -Xms and -Xmx to the same value. The purpose is to improve performance without having to re-partition the size of the heap area after Java garbage collection cleans up the heap area.

By default, initial memory size: physical computer memory size/64; maximum memory size: physical computer memory size/4.

View the initial memory size and maximum memory size of the heap through code

public class HeapSpaceInitial {
    
    
    public static void main(String[] args) {
    
    
        //返回JVM中堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回JVM试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
        System.out.println("-Xms:" + initialMemory + "M");
        System.out.println("-Xmx:" + maxMemory + "M");

        System.out.println("系统内存大小为:" + initialMemory * 64 / 1024 + "G");
        System.out.println("系统内存大小为:" + maxMemory * 4 / 1024 + "G");
    }
}

Set the initial memory size and the maximum memory size to 640M by yourself, and ++PrintGCDetailsprint GC information by parameter setting

insert image description here
Execute code output

-Xms:613M
-Xmx:613M
Heap
 PSYoungGen      total 190976K, used 16384K [0x00000000f2b00000, 0x0000000100000000, 0x0000000100000000)
  eden space 163840K, 10% used [0x00000000f2b00000,0x00000000f3b00250,0x00000000fcb00000)
  from space 27136K, 0% used [0x00000000fe580000,0x00000000fe580000,0x0000000100000000)
  to   space 27136K, 0% used [0x00000000fcb00000,0x00000000fcb00000,0x00000000fe580000)
 ParOldGen       total 437248K, used 0K [0x00000000d8000000, 0x00000000f2b00000, 0x00000000f2b00000)
  object space 437248K, 0% used [0x00000000d8000000,0x00000000d8000000,0x00000000f2b00000)
 Metaspace       used 3092K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 338K, capacity 388K, committed 512K, reserved 1048576K

Why specify the initial memory size and maximum memory size of the heap as 640M, but the output is 613M?
This is because the young generation in the heap space is divided into From area and To area, that is, s0 and s1 areas. From the GC log, the memory size of the heap is calculated as: Young generation (Eden area + From area/To area) + Old generation (163840K+27136K+437248K=613.5M).

3. Young Generation and Old Generation

Java objects stored in the JVM can be divided into two categories, namely:

  • Short-lived transient objects that are created and destroyed very quickly
  • The life cycle of the object is very long, and in some extreme cases, it can be kept consistent with the life cycle of the JVM.

If the Java heap area is further subdivided, it can be divided into the young generation (YongGen) and the old generation (OldGen), and the young generation can be divided into Eden space, Survivor0 space and Survivor1 space (sometimes also called from area, to area).
insert image description here

Configure the proportion of the new generation and the old generation in the heap structure:

  • By default -XX:NewRatio=2, it means that the young generation occupies 1, the old generation occupies 2, and the young generation occupies 1/3 of the entire heap.
  • It can be modified -XX:NewRatio=4,to indicate that the young generation occupies 1, the old generation occupies 4, and the young generation occupies 1/5 of the entire heap. butThis parameter is generally not adjusted during development
    insert image description here

In the Hotspot virtual machine, the ratio of the Eden space in the new generation to the other two Survivor spaces is 8:1:1. Developers can -XX:SurvivorRatioadjust this space ratio through the option " ". For example -XX:SurvuvirRatio=8(default parameter value).

Almost all Java objects are newly created in the Eden area, and most of the Java objects are destroyed in the new generation. IBM's special research shows that 80% of the objects in the new generation are "live and die". You can use the option " -Xmn" to set the maximum memory size of the young generation. This parameter generally uses the default value, and generally does not set it. If the parameter is used -XX:NewRatioto set the ratio of the new generation to the old generation at the same time -Xmn, the maximum memory size of the new generation set by the parameter will be used.
insert image description here

4. Object allocation process

Allocating memory for new objects is a very rigorous and complex task. JVM designers not only need to consider how and where to allocate memory, but also need to consider GC because the memory allocation algorithm is closely related to the memory recovery algorithm. Whether memory fragmentation will be generated in the memory space after memory reclamation is performed.

  1. New objects are placed in the Eden area first, and the size of this area is limited
  2. When the space in Eden is full, the program needs to create objects again, and the garbage collector of the JVM will perform garbage collection (Minor GC) on the Eden area to destroy the objects in the Eden area that are no longer referenced by other objects. Then load the new object and put it in the Eden area.
  3. Then move the remaining objects in the Eden area to the survivor 0 area
  4. If garbage collection is triggered again, the objects that survived last time and were placed in Survivor 0 will be placed in Survivor 1 if the GC is not recycled this time.
  5. If it goes through garbage collection again, it will be put back into Survivor 0 at this time
  6. Generally, after 15 garbage collections, if it is still alive, it will enter the retirement area. -MM:MaxTenuringThreshold=<N>It can be set by setting parameters:
  7. The objects in the retirement area are quite leisurely. When the memory in the retirement area is insufficient, GC:Major GC is triggered again to clean up the memory in the retirement area.
  8. If the Elderly District executes Major GC and finds that objects cannot be saved, an OOM exception will occur.

About Garbage Collection: Frequently collected in the new area, rarely collected in the old area, and almost never collected in the permanent area/meta space.

Flowchart for applying heap space for new objects:
insert image description here
summary

  • Summary for the survivors s0 and s1 areas: there is an exchange after copying, whoever is empty and who is to
  • About garbage collection: frequently collected in the new area, rarely collected in the old area, and almost never collected in the permanent generation and meta space

5. Minor GC、Major GC与Full GC

When the JVM performs GC, it does not recycle the above three memory areas together every time. Most of the time, the recycle refers to the new generation.

For the implementation of Hotspot VM , the GC in it is divided into two types according to the recovery area: one is partial collection ( Partial GC ), and the other is full heap collection ( FullGC ).

  • Partial collection: Garbage collection that does not completely collect the entire Java heap. It is further divided into:
    Minor GC / Young GC : only the garbage collection of the new generation
    ○ Old age collection ( Major GC / Old GC ): only the garbage collection of the old generation. Currently, only the CMS garbage collector has the behavior of separately collecting the old generation.Note that in many cases, Major GC will be confused with Full GC, and it is necessary to specifically distinguish whether it is old generation recycling or full heap recycling.
    ○ Mixed collection ( Mixed GC ): Collect the garbage collection of the entire new generation and part of the old generation. (Currently, only the G1 garbage collector has this behavior)
  • Whole heap collection ( Full GC ): Garbage collection that collects the entire java heap and method area.

5.1 Minor GC

Young generation GC (Minor GC) trigger mechanism: When the young generation space is insufficient, Minor GC will be triggered, and each Minor GC will clean up the memory of the young generation. The young generation here refers to the Eden area being full, and Survivor is full. GC.

Because most Java objects have the characteristics of eternity, Minor GC is very frequent, and the recovery speed is generally faster. Minor GC will trigger STW (STOP WORK), suspend the threads of other users, and wait for the garbage collection to end before the user threads resume running.
insert image description here

5.2 Major GC

Old generation Major GC trigger mechanism:
Major GC refers to the GC that occurs in the old generation. When objects disappear from the old generation, we say that "Major GC" or "Full GC" has occurred. Major GC appears, often accompanied by at least one Minor GC (but not absolute, in the collection strategy of the Parallel Scavenge collector, the strategy selection process of Major GC will be directly carried out). That is, when there is not enough space in the old generation, it will try to trigger Minor GC first, and if there is not enough space later, it will trigger Major GC.

The speed of Major GC is generally more than 10 times slower than Minor GC, and the STW time is longer. If the memory is not enough after Major GC, OOM will be reported. The speed of Major GC is generally more than 10 times slower than Minor GC.

5.3 Full GC

Full GC should be avoided as much as possible in development or tuning. The trigger mechanism of the full heap collection Full GC, there are five situations that trigger the execution of the Full GC:

  1. When calling System.gc(), the system suggests to execute Full GC, but not necessarily
  2. Insufficient space in the old generation
  3. Insufficient space in method area
  4. After Minor GC, the average size of the old generation is larger than the available memory of the old generation
  5. When copying from Eden area, Survivor0 (From) area to Survivor1 (To) area, if the object size is larger than the available memory in To Space, the object is transferred to the old generation, and the available memory in the old generation is smaller than the object size.

6. Heap space generation idea

Why do you need to divide the Java heap into generations? Doesn't it work properly regardless of generation?
After research, different objects have different life cycles. 70%-99% of objects are temporary objects. The heap space is divided into generations, divided into the new generation and the old generation:

  • New generation: It consists of Eden and two Survivors of the same size (also known as from/to, so/s1), and to is always empty.
  • Old generation: stores objects that survive multiple GCs in the new generation.

insert image description here
insert image description here

In fact, it is completely possible to not divide into generations. The only reason for generation is to optimize GC performance . If there is no generation, then all the objects are together, just like shutting all the people in a school in a classroom. During GC, it is necessary to find which objects are useless, so that all areas of the heap will be scanned. And many objects are born and die, if it is divided into generations, put the newly created object in a certain place, and when the GC, first recycle the area that stores the "lived and died" objects, so that it will be freed. Come out with a lot of space.

7. Memory allocation strategy

If the object is born in the Eden area and still survives after the first MinorGC, and can be accommodated in the Survivor area, it will be moved to the Survivor space, and the age of the object will be set to 1. Every time the object experiences a MinorGC in the Survivor area, The age is increased by 1 year, and when its age increases to a certain level (the default is 15 years old, each JVM and each garbage collector is different), it will be promoted to the old age. The age threshold for objects to be promoted to the old age can be -XX:MaxTenuringThresholdset through options.

The principles of object allocation for different age groups are as follows:

  • Priority allocation to Eden
  • Large objects that cannot be accommodated in Eden are directly allocated to the old generation (try to avoid too many large objects in the program)
  • Long-lived objects are allocated to the old generation
  • Dynamic object age judgment: If the sum of the size of all objects of the same age in the Survivor area is greater than half of the Survivor space, objects whose age is greater than or equal to this age can directly enter the old age without waiting for the age required in MaxTenuringThreshold.
  • Space Allocation Guarantee:-XX:HandlePromotionFailure

8. Allocate memory for objects: TLAB

What is TLAB ( Thread Local Allocation Buffer )?
From the perspective of memory model rather than garbage collection, the Eden area continues to be divided, and the JVM allocates a private buffer area TLAB for each thread, which is included in the Eden space. When multiple threads allocate memory at the same time, using TLAB can avoid a series of non-thread-safe problems, and can also improve the throughput of memory allocation. This memory allocation method is called a fast allocation strategy.
insert image description here
Why is there TLAB ( Thread Local Allocation Buffer )?
The heap area is a thread-shared area, and any thread can access the shared data in the heap area. Since the creation of object instances is very frequent in the JVM, it is not thread-safe to divide the memory space from the heap area in a concurrent environment. To avoid multiple threads operating the same address, mechanisms such as locking need to be used, which in turn affects the allocation speed.

Although not all object instances can successfully allocate memory in TLAB, JVM does use TLAB as the first choice for memory allocation. In the program, the developer can -XX:UseTLABset whether to open the TLAB space through the option " ".

By default, the memory in TLAB space is very small, occupying only 1% of the entire Eden space. Of course, we can -XX:TLABWasterTargetPercentset the percentage of Eden space occupied by TLAB space through the option " ".Once the object fails to allocate memory in the TLAB space, the JVM will try to use the locking mechanism to ensure the atomicity of data operations, thereby directly allocating memory in the Eden space
insert image description here

9. Parameter setting of heap space

Official website address

parameter effect
-XX:PrintFlagsInitial See default initial values ​​for all parameters
-XX:PrintFlagsFinal View the final values ​​of all parameters
-Xms Initial heap space memory (1/64 of physical memory by default)
-Xmx Maximum heap space memory (1/4 of physical memory by default)
-Xmn Set the size of the new generation (initial value and maximum value)
-XX: NewRatio Configure the proportion of the new generation and the old generation in the heap structure
-XX:SurvivorRatio Set the ratio of Eden and s0/s1 space in the new generation
-XX:MaxTenuringThreshold Set the maximum age of the new generation garbage
-XX:+PrintGCDetails Output detailed GC processing logs
-XX:+PrintGC/-verbose:gc Print GC brief information
-XX:HandlePromotionFailure Whether to set space allocation guarantee

Regarding distribution guarantees:

Before Minor GC occurs, the virtual machine checks whether the maximum available continuous space in the old generation is greater than the total space of all objects in the new generation.

● If it is larger, the Minor GC is safe.
● If it is smaller, the virtual machine checks -XX:HandlePromotionFailurewhether the setting value allows guarantee failure.

If HandlePromotionFailure=trueso, it will continue to check whether the maximum available continuous space in the old generation is greater than the average size of objects promoted to the old generation. If it is greater, try to perform a Minor GC, but this Minor GC is still risky; if it is less than, perform a Full GC instead.

If HandlePromotionFailure=falseso, perform a Full GC instead.

After JDK6 Update24, the HandlePromotionFailure parameter will no longer affect the space allocation guarantee policy of the virtual machine. Observe the source code changes in openJDK. Although the HandlePromotionFailure parameter is defined in the source code, it will no longer be used in the code. The rule after JDK6 Update 24 becomesAs long as the continuous space of the old generation is greater than the total size of the new generation objectsorThe average size of previous promotions will be Minor GC, otherwise FullGC will be performed.

10. Escape analysis and code optimization

In "In-depth Understanding of Java Virtual Machine", there is such a description about Java heap memory:

With the development of JIT compilation period andescape analysis techniqueGradually mature,stack allocationScalar Replacement Optimization TechniqueWill lead to some subtle changes, all objects are allocated on the heap and gradually become less "absolute".

In the Java virtual machine, objects are allocated memory in the Java heap, which is a common knowledge. However, there is a special case, that isIf after escape analysis (Escape Analysis) it is found that an object does not escape the method, then it may be optimized to be allocated on the stack.. This eliminates the need to allocate memory on the heap and garbage collection. This is also the most common off-heap storage technique.

In addition, the aforementioned TaoBaoVM based on the deep customization of OpenJDK, in which the innovative GCIH (GC invisible heap) technology implements off-heap, moves Java objects with a long life cycle from the heap to outside the heap, and GC cannot manage the internals of GCIH Java objects, in order to achieve the purpose of reducing the frequency of GC recovery and improving the efficiency of GC recovery.

10.1 Overview of Escape Analysis

How to allocate objects on the heap to the stack requires the use of escape analysis. This is a cross-function global data flow analysis algorithm that can effectively reduce the synchronization load and memory heap allocation pressure in Java programs. Through escape analysis, the Java Hotspot compiler can analyze the scope of use of a new object reference and decide whether to allocate this object on the heap.

The basic behavior of escape analysis is to analyze the dynamic scope of objects:

  • When an object is defined in a method, and the object is only used inside the method, it is considered that no escape has occurred
  • When an object is defined in a method and it is referenced by an external method, it is considered to have escaped. For example, as a call parameter passed to other places.

Objects that have not escaped can be allocated on the stack, and the stack space is removed as the method execution ends. Code example where no escaping occurs:

public void my_method() {
    
    
    V v = new V();
    // use v
    // ....
    v = null;
}

Code example where the escape occurs:

public static StringBuffer createStringBuffer(String s1, String s2) {
    
    
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
}

More code samples:

public class EscapeAnalysis {
    
    

    public EscapeAnalysis obj;


    /**
     * 方法返回EscapeAnalysis,发生逃逸
     *
     * @return
     */
    public EscapeAnalysis getInstance() {
    
    
        return obj == null ? new EscapeAnalysis() : obj;
    }

    /**
     * 为成员属性赋值,发生逃逸
     */
    public void setObj() {
    
    
        this.obj = new EscapeAnalysis();

    }

    /**
     * 对象的作用域仅在当前方法中有效,没有发生逃逸
     */
    public void useEscapeAnalysis() {
    
    
        EscapeAnalysis escapeAnalysis = new EscapeAnalysis();
    }

    /**
     * 引用成员变量的值,发生逃逸
     */
    public void useEscapeAnalysis2() {
    
    
        EscapeAnalysis escapeAnalysis = getInstance();
    }
}

After the JDK 6u23 version, escape analysis has been enabled by default in HotSpot. If an earlier version is used, developers can -XX:+DoEscapeAnalysisexplicitly enable escape analysis through options, and -XX:+PrintEscapeAnalysisview the filtering results of escape analysis through options.

Conclusion: Allocation on the stack can effectively reduce the synchronization load and the pressure of heap memory space allocation in Java programs. Therefore, if local variables can be used in development, do not use them defined outside the method.

10.2 Code Optimization

You can refer to the blog: https://www.cnblogs.com/lisingshen/p/11586160.html

Using escape analysis, the compiler can optimize the code as follows:

  1. Allocated on the stack . To convert heap allocation to stack allocation, if an object is allocated in a subroutine, the object may be a candidate for stack allocation instead of heap allocation if the pointer to the object never escapes.
  2. Synchronization is omitted . If an object is found to be accessible only from one thread, operations on that object may not be synchronized.
  3. Detach object or scalar replacement . Some objects may be accessed without existing as a continuous memory structure, so part (or all) of the object may not be stored in memory, but stored in CPU registers.

10.2.1 Allocation on the stack

According to the results of the escape analysis during compilation, the JIT compiler finds that if an object does not escape the method, it may be optimized to be allocated on the stack. After the allocation is completed, it continues to execute in the call stack, and finally the thread ends, the stack space is reclaimed, and the local variable object is also reclaimed. This eliminates the need for garbage collection. Common stack allocation scenarios include: assigning values ​​to member variables, method return values, and passing instance references.

10.2.2 Synchronization elision

The cost of thread synchronization is quite high, and the consequence of synchronization is to reduce concurrency and performance. When dynamically compiling a synchronization block, the JIT compiler can use escape analysis toDetermine whether the lock object used by the synchronization block can be accessed by one thread without being released to other threads. If not, then the JIT compiler will cancel the synchronization of this part of the code when compiling the synchronization block. This can greatly improve concurrency and performance. This process of canceling synchronization is called synchronization elision, also called lock clearing.

Example:

public void f() {
    
    
    Object hellis = new Object();
    synchronized(hellis) {
    
    
        System.out.println(hellis);
    }
}

The hellis object is locked in the code, but the life cycle of the hellis object is only in the f() method, and will not be accessed by other threads, so it will be optimized in the JIT compilation stage, and optimized into:

public void f() {
    
    
    Object hellis = new Object();
	System.out.println(hellis);
}

10.2.3 Scalar substitution

A scalar (Scalar) refers to a piece of data that cannot be decomposed into smaller data. The original data type in Java is a scalar. In contrast, those data that can be decomposed are called aggregates, and objects in Java are aggregates because they can be decomposed into other aggregates and scalars.

In the JIT stage, if after escape analysis, it is found that an object will not be accessed by the outside world, then after JIT optimization, the object will be disassembled into several member variables contained in it to replace. This process is scalar replacement.

insert image description here
After scalar replacement, the above code will become:
insert image description here
It can be seen that after the escape analysis of the aggregate Point, it is found that it has not escaped, and it is replaced with two scalars. The advantage of scalar replacement is that it can greatly reduce the heap memory usage. Because once there is no need to create objects, then there is no need to allocate heap memory. Scalar substitution provides a good basis for on-stack allocation.

Scalar replacement parameter setting: parameter -XX:+EliminateAllocations, enable scalar replacement, allowing objects to be scattered and allocated on the stack.

10.3 Summary of Escape Analysis

The paper on escape analysis was published in 1999, but it was not realized until JDK1.6, and this technology is not very mature until now.

The root cause of this isThere is no guarantee that the performance consumption of escape analysis will be higher than his consumption. Although after escape analysis, scalar replacement, stack allocation, and lock elimination can be done. However, escape analysis itself also requires a series of complex analysis, which is actually a relatively time-consuming process.

An extreme example is that after escape analysis, it is found that there is no object that does not escape. Then the process of escape analysis is wasted.

Although this technology is not very mature, it is also a very important means in the optimization technology of the real-time compiler.

Note that there are some opinions that through escape analysis, the JVM will allocate objects on the stack that will not escape. This is theoretically feasible, but it depends on the choice of the JVM designer. As far as I know, this is not done in the Oracle Hotspot JVM. This has been stated in the documents related to escape analysis, so it can be clearly stated that all object instances are created on the heap.

At present, many books are still based on the previous version of JDK7. JDK has undergone great changes. The cache and static variables of intern strings were once allocated on the permanent generation, and the permanent generation has been replaced by the metadata area. However, the intern string cache and static variables are not transferred to the metadata area, but are directly allocated on the heap, so this also conforms to the conclusion of the previous point: object instances are all allocated on the heap.

11. Heap summary

  1. The young generation is the area where objects are born, grow, and die. An object is created and applied here, and finally it is collected by the garbage collector and ends its life.

  2. Objects with a long life cycle placed in the old generation are usually Java objects filtered and copied from the Survivor area. Of course, there are special cases. We know that ordinary objects will be allocated on TLAB; if the object is large, JVM will try to allocate it directly in other locations in Eden; if the object is too large, it is completely impossible to find a long enough continuous free space in the new generation Space, JVM will be directly allocated to the old generation.

  3. When GC only occurs in the young generation, the act of reclaiming young generation objects is called MinorGc. When GC occurs in the old age, it is called MajorGc or FullGC. Generally, the frequency of MinorGC is much higher than that of MajorGC, that is, the frequency of garbage collection in the old generation will be much lower than that in the young generation.

12. Object instantiation

12.1 How to create objects

  • new: the most common way, the static method of Xxx, the static method of XxxBuilder/XxxFactory
  • Class's newInstance method: the way of reflection, only the constructor with empty parameters can be called, and the permission must be public
  • Constructor's newInstance(XXX): Reflection method, you can call the constructor with empty parameters and parameters, and the permission is not required
  • Use clone(): do not call any constructors, require the current class to implement the Cloneable interface, implement clone()
  • Use serialization: get a binary stream of an object from a file, from the network
  • Third-party library Objenesis

12.2 Steps to create an object

The above is to look at the object creation process from the perspective of bytecode, and now analyze it from the perspective of execution steps:
insert image description here

  1. Determine whether the class corresponding to the object is loaded, linked, initialized

When the virtual machine encounters a new instruction, it first checks whether the parameter of this instruction can locate a symbolic reference of a class in the constant pool of Metaspace, and checks whether the class represented by the symbolic reference has been loaded, parsed, and initialized (that is, to determine whether the class meta information exists). If not, then in parent delegation mode, use the current classloader withClassLoader+package name+class nameFind the corresponding class file for the key.

  • If the file is not found, throws ClassNotFoundException,
  • If found, the class is loaded and the corresponding Class class object is generated.
  1. Allocate space for the object
    First calculate the size of the space occupied by the object, and then allocate a piece of memory in the heap for the new object. If the instance member variable is a reference variable, only the space for the reference variable is allocated, which is 4 bytes in size

If the memory is regular : the virtual machine will use the == pointer collision method (Bump The Point) == to allocate memory for the object.

  • Pointer collision method (Bump The Point) : All used memory is on one side, free memory is on the other side, and a pointer is placed in the middle as an indicator of the demarcation point. Allocating memory is just moving the pointer to the free side for a period The distance between objects of equal size. If the garbage collector chooses Serial, ParNew is based on the compression algorithm, and the virtual machine adopts this allocation method. Generally, when using a collector with a Compact (finishing) process, pointer collisions are used.

If the memory is irregular : The virtual machine needs to maintain a == free list (Free List) == to allocate memory for objects.

  • Free List method (Free List) : The used memory and unused memory are interleaved, then the virtual machine will use the free list to allocate memory for the object. It means that the virtual machine maintains a list, records which memory blocks are available, and finds a large enough space from the list to allocate to the object instance when reassigning, and updates the content on the list.

Which allocation method to choose is determined by whether the Java heap is regular, and whether the Java heap is regular is determined by whether the garbage collector used has a compacting function.

  1. Dealing with concurrency security issues
    When allocating memory space, another issue is to ensure the thread safety of new objects in time: creating objects is a very frequent operation, and the virtual machine needs to solve the concurrency issue. The virtual machine uses two methods to solve the concurrency problem:
  • Adopt **CAS (Compare And Swap)** failure retry, area lock; guarantee the atomicity of pointer update operation
  • **TLAB (Thread Local Allocation Buffer)** divides the action of memory allocation into different spaces according to threads, that is, each thread pre-allocates a small piece of memory in the Java heap to become a local thread allocation buffer. Whether the virtual machine uses TLAB: -XX:+UseTLABset by setting parameters
  1. Initialize the allocated space
    After the memory allocation is complete, the virtual machine initializes the allocated memory space to zero (excluding the object header). This step ensures that the instance fields of the object can be used directly in the Java code without assigning initial values, and the program can access the zero values ​​corresponding to the data types of these fields.

  2. Set the object header of the object
    Store data such as the class of the object (that is, the metadata information of the class), the HashCode of the object, the GC information of the object, and the lock information in the object header of the object. Exactly how this process is set up depends on the JVM implementation.

  3. Execute the init method to initialize
    From the perspective of the Java program, the initialization officially begins. Initialize member variables, execute the instantiation code block, call the constructor of the class, and assign the first address of the object in the heap to the reference variable. Therefore, generally speaking, the new instruction will be followed by the execution method to initialize the object according to the programmer's wishes, so that a truly usable object is completely created.

Operations that assign values ​​to object properties

  • default initialization of properties
  • explicit initialization
  • code block initialization
  • initialization in the constructor

The process of object instantiation

  1. Load class meta information
  2. allocate memory for the object
  3. Dealing with concurrency issues
  4. Default initialization of properties (zero value initialization)
  5. Set object header information
  6. Display initialization of properties, initialization in code blocks, initialization in constructors

13. Object memory layout

insert image description here

insert image description here

Code example:

public class Account {
    
    
}

public class Customer {
    
    
    int id = 1001;
    String name;
    Account acct;

    {
    
    
        name = "匿名客户";
    }

    public Customer() {
    
    
    }
}

public class CustomerTest {
    
    
    public static void main(String[] args) {
    
    
        Customer cust = new Customer();
    }
}

The memory layout of the object corresponding to the code is shown in the following figure:
insert image description here

13.1 Object header (Header)

The object header contains two parts, namely runtime metadata (Mark Word) and type pointer. If it is an array, the length of the array also needs to be recorded.

Runtime metadata:

  • Hash value (HashCode)
  • GC generational age
  • lock status flag
  • locks held by threads
  • Bias thread ID
  • Bias timestamp

Type pointer: Points to the class metadata InstanceKlass to determine the type of the object.

13.2 Instance data

It is the effective information actually stored by the object, including various types of fields defined in the program code (including fields inherited from the parent class and owned by itself)

  • Fields of the same width are always allocated together
  • Variables defined in the parent class will appear before the subclass
  • If the CompactFields parameter is true (default is true): the narrow variable of the subclass may be inserted into the gap of the parent class variable

13.3 Alignment padding

It is not necessary and has no special meaning, it only serves as a placeholder

14. Object access positioning

The purpose of creating an object is to use it. The JVM locates the object instance address through the object reference in the stack frame, and then accesses its internal object instance.There are two main ways to access objects: handle access and direct pointer
insert image description here

14.1 Handle access

insert image description here
The advantage of using handle access: the stable handle address is stored in the reference, and when the object is moved (it is common to move objects during garbage collection), only the object instance data pointer in the handle will be changed, and the reference itself does not need to be modified.

14.2 Direct Pointers

The Hotspot virtual machine uses direct pointers to access and locate objects. The direct pointer is a reference in the local variable table, directly pointing to the instance in the heap, and there is a type pointer in the object instance, pointing to the object type data in the method area

insert image description here

The notes are summarized from the video tutorial: Shang Silicon Valley Song Hongkang JVM full set of tutorials (detailed java virtual machine)
reference:
1. "In-depth understanding of Java virtual machine" 2nd edition

Guess you like

Origin blog.csdn.net/huangjhai/article/details/120479423