JVM memory model

Memory model diagram

Analyze the memory model diagram

  • Class-loading subsystem: Java bytecode files are loaded and initialized by the class-loading subsystem. See this article for class-loaders

  • Stack: A stack is what I prefer to call a thread stack. Each thread has its own stack space, and the stack memory is freed when the thread terminates. The stack stores the running state of the current thread in frames, called stack frames.

    • Ordinary method stack frame: stack frame consists of local variables, operand stack and dynamic link.

      • Here is an example of the relationship between local variables and operand stacks:
      // Parse the code using javap -c
      public static int incr(a) {
              int a = 1;
              int b = 2;
              int c = (a + b) * 3;
              return c;
      }
      / / the JVM instructions
      Code:
       0: iconst_1 // push int 1 onto the operand stack ()
       1: istore_0 // Store a value of type int into the local variable 0
       2: iconst_2 // push int constant 2 onto the operand stack
       3: istore_1 // Store a value of type int into the local variable 1
       4: iload_0  // Load int from local variable 0
       5: iload_1  // Load int from local variable 1
       6: iadd	 // Perform an addition of type int
       7: iconst_3 // push int constant 3 onto the stack
       8: imul	 // Perform the multiplication of type int
       9: istore_2 // Store a value of type int into local variable 2
       10: iload_2 // Load int from local variable 2
       11: ireturn // Returns data of type int from the method
      Copy the code

      The JVM pushes the value of the variable into the operand stack, stores the variable into local variables, and finally loads the value.

      • Dynamic linking: Symbolic and direct references are parsed and linked at run time, called dynamic linking.

    • Main stack frame: The local variables in the main stack frame are different from those in the normal stack. The local variables in the main stack frame are pointer to the heap.

  • Heap: The heap area is divided into the Cenozoic era and the old era. The Cenozoic era is divided into Eden area and Survivor area, and Survivor area is divided into from area and to area.

JVM memory parameter Settings

Ex. :

java -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar microservice-eureka-server.jar
Copy the code

-Xss: stack size of each thread

-Xms: the initial heap size, 1/64 of the default physical memory

-Xmx: the maximum heap size, 1/4 of the default physical memory

-xMN: Cenozoic size

-xx :NewSize: Sets the initial size of the new generation

-xx :NewRatio: Default 2 Indicates that the new generation occupies half of the old generation and 1/3 of the total heap memory.

-xx :SurvivorRatio: Default 8 indicates that a survivor zone occupies 1/8 Eden memory, that is, 1/10 memory of the new generation.

There are two JVM arguments for MetaspaceSize: -xx :MetaspaceSize=N and -xx :MaxMetaspaceSize=N

-xx: MaxMetaspaceSize: sets the maximum metasize space. The default value is -1, that is, the local memory size is not limited.

-xx: MetaspaceSize: specifies the initial threshold for Fullgc to be triggered in the metasspace (the MetaspaceSize has no fixed initial size), in bytes. The default value is about 21M. When this threshold is reached, full GC will be triggered for type uninstallation. If you free up a lot of space, lower the value appropriately; If very little space is freed, increase this value appropriately, up to -xx: MaxMetaspaceSize (if set). This is not the same as the ** -xx :PermSize** argument in earlier JDK versions, where -xx :PermSize represents the initial capacity of the permanent generation.

Since resizing the metaclass requires Full GC, which is a very expensive operation, if an application starts up with a lot of Full GC, it is usually due to a permanent generation or metaclass resizing. In this case, It is generally recommended that MetaspaceSize and MaxMetaspaceSize be set to the same value in the JVM parameters and larger than the initial value. For a machine with 8 GIGABytes of physical memory, I would normally set both values to 256M.

Object creation

The flow chart

1. Class loading check

When the virtual machine gets a new instruction, it first checks whether the instruction’s parameter can locate the symbolic reference of a class in the constant pool, and checks whether the symbolic reference class has been loaded, parsed, initialized, if not, the class load will be performed first.

2. Allocate memory

When the class load check passes, the JVM allocates memory for the new objects, and the required memory size is fully determined after the class load is complete.

There are two problems with allocating memory:

  1. How to partition memory
  2. In the case of concurrency, it is possible that object A is allocating memory, and object B uses the original pointer to allocate memory at the same time before the pointer can be modified.

Memory partition method:

  • Pointer collision (default) If the heap is perfectly neat, use used memory on one side and unused memory on the other, with a pointer in the middle as a boundary pointer, and divide memory by moving objects the same size toward free memory.

  • The free list

    If the memory is not organized, the virtual machine must maintain a list of available and unavailable memory, and find a large enough space from the list to allocate memory for objects, which is an obvious problem with memory fragmentation.

Solutions to concurrency problems

  • CAS: The CENTRAL Authentication Service (CAS) service is configured for the VM to synchronize the allocated space to ensure atomicity of update operations.
  • -xx :+/ -usetlab specifies whether the VIRTUAL machine uses TLAB (enabled by default). -xxtlabSize specifies the size of TLAB.

3. The initialization

After memory allocation is complete, the virtual machine initializes all allocated memory space to ‘zero’ (excluding object headers), which can be done before TLAB allocation. This ensures that the instance fields of the object can be used in JAVA code without initial values, and the program can access the ‘zero’ values corresponding to the data types of these fields.

4. Set the object header

After initializing the zero value, the virtual machine sets the Object such as which class the Object is an instance of, how to find the metadata information about the class, the Object’s hash code, and the GC generation age of the Object, which is stored in the Object Header of the Object.

In the HotSpot VIRTUAL machine, the layout of objects stored in memory is divided into three areas: object headers, instance data, and aligned padding. The object header contains two parts of information. The first part is used to store the object’s own runtime data, such as hash code, GC generation age, lock status flag, thread held lock, bias thread ID, bias timestamp, etc. The other part is a type pointer, a pointer to the object’s class metadata that the virtual machine uses to determine which class instance the object is.

5. Execution method

The linguistic equivalent is assigning values to properties (values assigned in code) and executing constructors

Object size and pointer compression

What is pointer compression?

JVM configuration parameter: XX:+/ -usecompressedoops Enables/disables pointer compression

Why use pointer compression?

  • Using 32-bit Pointers in HtpSpot on 64-bit operating platforms increases memory usage by about 1.5 times. Using large Pointers to move data between main memory and the cache takes up a lot of bandwidth and puts a lot of pressure on the GC.

  • 32-bit addresses support up to 4 gigabytes of memory (2 ^ 32) in the JVM, and Mark Word occupies 4 bytes. This takes up eight bytes on a 64-bit system, is encoded into four bytes of storage using a pointer compression algorithm, and decoded into eight bytes of storage before processing by the processor, allowing the JVM to support a larger memory configuration using only 32-bit addresses.

  • Pointer compression does not need to be enabled for less than 4 gigabytes of memory, but for more than 32 GIGABytes, the compression pointer will fail and 64-bit (8 bytes) will be forced to address Java objects, so it is better not to have more than 32 heap memory.

Object memory allocation

Object stack allocation

Object escape analysis

Through the analysis of the JVM, we know that objects are allocated on the heap, while there is no reference object need to rely on the GC to garbage collection, if the garbage more often sign up for GC to take larger pressure, impact performance, in order to reduce the number of temporary objects in the heap allocation, the JVM by escape analysis to determine the object will not be external access.

	// Analyze object dynamic scope
	// Object escape
    public User getUser(a){
        User user = new User();
        user.setId(1);
        return user;
    }
    // The object does not escape
    public void setUser(a){
        User user = new User();
        user.setId(1);
    }
Copy the code

If there is no escape, the object can be allocated memory on the stack, so that the memory occupied by the object can be destroyed as the stack frame goes off the stack, reducing the pressure on the GC.

Run -xx :+DoEscapeAnalysis to enable escape analysis. In JDK7, escape analysis is enabled by default. To disable escape analysis, run -xx: -doescapeAnalysis.

Scalar replacement

When escape analysis determines that the object cannot be accessed externally and that the object can be further decomposed, the JVM does not create the object. Instead, the JVM splits the object’s member variables into a number of member variable locks that are used by this method. These substitute member variables allocate space on stack frames or registers. This will not cause stack memory to be unable to allocate enough memory to objects because there is not a large contiguity of space, which is enabled by default after JDK7.

Scalars and aggregates

In short, scalars are non-separable quantities, such as basic data types such as Int Long and reference types, etc. Aggregate quantities are separable quantities, and Java objects are further decomposed quantities

The object is allocated in Eden

In most cases, objects are allocated in the Eden region of the new generation, and when the Eden region does not have enough space to allocate, the JVM issues a minor GC.

The default ratio of Eden to Survivor is 8:1:1

After a Minor GC is triggered, 99% of the objects will be garbage collected, and the remaining objects will be moved to Survivor zones. The next time a Minor GC is triggered, garbage objects in Eden and Survivor zones will be collected. The remaining surviving objects are moved to another survivor zone at once.

The JVM default parameter -xx :+UseAdaptiveSizePolicy(enabled by default) will cause the 8:1:1 ratio to automatically change. If you do not want this ratio to change, you can set the parameter -xx: -useadaptivesizePolicy

Big object goes straight to the old age

Large objects are objects that require a large amount of contiguous memory space such as strings and arrays.

JVM parameters – XX: PretenureSizeThreshold can set the size of a large object, if the object is more than set size goes straight to the old age, not the young generation, this parameter in Serial and ParNew only effective under two collector.

Why do you do that?

To avoid inefficient replication operations when allocating memory for large objects.

Long-lived objects will enter the old age

The JVM gives each object an object age counter. Each time an object in the new generation survives a minor gc and is accepted by Survivor, the object age increases by 1. When it reaches a certain age (the default is 15, The CMS collector defaults to 6) if the object is still alive then it goes straight to the old age.

The age threshold for the object to be promoted to the old age can be set by using -xx :MaxTenuringThreshold.

Object dynamic age judgment

If the total size of a group of objects is greater than 50% of the total capacity of a Survivor zone (-xx :TargetSurvivorRatio can be specified), then the group of objects that is greater than or equal to the maximum age of the group can enter the old age directly. The rule is to hope that objects that are likely to survive for a long time will enter the old age as soon as possible.

The dynamic age determination mechanism for objects generally occurs after the Minor GC.

Old chronospatial allocation guarantee mechanism

Before each minor GC occurs in the young generation, the JVM calculates the remaining free space of an old generation, and if the free space is less than all the objects in the young generation, it checks to see if the “-xx: -handlePromotionFailure” parameter is enabled (JDK1.8 defaults to enabled). If this parameter is present, it checks to see if the remaining size of the old age is greater than the average size of the previous minor GC entering the old age.

Full GC is triggered if this parameter is not set, and OOM occurs if there is not enough space for new objects after collection.

Full GC will also be triggered if the size of objects that need to be moved to the old age after the Minor GC is larger than the remaining space of the old age, and OOM will occur if there is still not enough space after the collection.

Object memory reclamation algorithm

There are instances of almost all objects in the heap, and the first step in collecting money from the heap is to determine which objects are dead and need to be collected.

Reference counting method

Add a reference counter to the object. Whenever it is referenced, the counter is incremented by 1. When the reference is implemented, the counter is decreased by 1.

This method is easy to implement and efficient, but it is not chosen to manage memory in mainstream virtual machines at present, because it is difficult to solve the problem of object reference cycle between each other.

For example, if you have two objects A/B, A refers to B, and B refers to A, the reference counter for both objects will never be zero.

Accessibility analysis algorithm

Start with “GC roots” objects as seven points, and search down from these nodes for referenced objects. All objects found are non-garbage and the rest are garbage.

GC Roots: thread stack local variables, static variables, local method stack variables, and so on.

Common reference types

Strong reference, soft reference, weak reference, virtual reference

  • Strong references: Plain variable references

    Tax tax = new Tax();
    Copy the code
  • SoftReference: an object is wrapped with a SoftReference SoftReference. Normally, the object will not be reclaimed. After GC is complete, no space is available for storing new objects, so these SoftReference objects are reclaimed. Soft references can be used to implement memory sensitive tell caches.

    SoftReference<Tax> tax = new SoftReference<Tax>(new Tax());
    Copy the code
  • WeakReference: wrap the object with WeakReference soft reference type object. WeakReference and no reference are almost the same, GC will directly recycle them, generally not used

    WeakReference<Tax> tax = new WeakReference<Tax>(new Tax());
    Copy the code
  • Virtual reference: The weakest type of reference relationship, rarely used

The Finalize () method finally determines whether an object is alive or not

  • Mark and filter for the first time:
    • The filtering condition is whether it is necessary for this object to execute finalize() method, even if there is no reference connected with GC Roots after reachability analysis.
    • When an object does not override the Finalize () method, the object will be recycled directly
  • Second mark
    • If you override the Finalize method, you can do something before the object is collected, for example, to save yourself. If you want to save yourself from Finalize, you just need to re-associate with any object in the GC Roots reference chain.
    • An object’s Finalize () method can only be executed once, that is, you can only save yourself once by finalize().

How do you tell if a class is useless

The method area mainly recycles useless classes

A class must satisfy all three conditions to be considered useless

  • All instances of the class are reclaimed
  • The ClassLoader that loaded the class has been reclaimed
  • The Class object of this Class is not referenced anywhere, and the methods of this Class cannot be accessed anywhere by reflection.