Hello, lovely friends, Hello! I’m yunsheng, and I’m glad to share my knowledge with you here 🙂

The creation of objects in the JVM

When a virtual machine receives a new instruction, it first checks whether it has been loaded by the class loader. If not, the corresponding class loading process must be performed first. Class loading is the process of loading a class into the JVM’s runtime data area.

1) Check the load

First, check that the argument to the directive can locate a symbolic reference to a class in the constant pool (symbolic references: symbolic references describe the referenced target as a set of symbols), and check that the class has been loaded, parsed, and initialized.

2) Allocate memory

Next, the virtual machine allocates memory for the newborn objects. The task of allocating space for an object is equivalent to dividing a certain size of memory from the Java heap.

Pointer to the collision

If the Java heap memory is absolutely neat, all used memory aside and free memory on the other side, there is a middle pointer as a cut-off point indicator, the allocated memory is just put the pointer to the free space there move a and the object is equal to the size of the distance, this distribution is called a pointer “bumped”.

The free list

If memory in the Java heap is not neat, used memory and free memory cross each other, that is simply no way pointer collision, the virtual machine, you must maintain a list of records on which memory blocks are available, at the time of distribution from the list to find a large enough space division to the object instance, and update the list of records, This allocation is called a “free list”.

The choice of allocation method depends on whether the Java heap is clean, which in turn depends on whether the garbage collector used has collation capabilities. For Serial, ParNew and other garbage collectors with compacted collation, the system uses pointer collision, which is simple and efficient. If you are using a garbage collector like CMS without compression (collation), you can theoretically only use a more complex free list.

Concurrent security

Except how to divided space available, there is another issue to consider is the object creation in A virtual machine is very frequent behavior, even if just modify the position of A pointer is pointing to, in the case of concurrent is not thread-safe, is possible to allocate memory object A, pointer could change, Object B also uses the original pointer to allocate memory.

Mechanism of the CAS

There are two solutions to this problem. One is to synchronize the memory allocation — the virtual machine actually uses CAS and retry to ensure atomization of the update operation.

Allocate the buffer

The JVM allocates a small chunk of private memory in the Java heap, known as the Thread Local Allocation Buffer (TLAB). In this way, each thread has an independent Buffer. If it needs to allocate memory, it can allocate it to its own Buffer. In this way, there is no competition and the allocation efficiency can be greatly improved. Then apply for a piece from Eden area to continue to use.

The goal of TLAB is to allow each Java application thread to use its own dedicated allocation pointer to allocate memory for new objects, reducing synchronization overhead.

Note: TLAB only allows each thread to have a private allocation pointer, but the memory space underlying the object is still accessible to all threads, but not to other threads. When a TLAB is full, a new TLAB is applied.

Parameters:

-XX:+UseTLAB

Allows the use of thread-local allocation blocks (TLabs) in young generation Spaces. This option is enabled by default. To disable TLAB, specify -xx: -usetlab.

3) Memory space initialization

After memory allocation is complete, the virtual machine needs to initialize all allocated memory space to zero (int 0, Boolean false, etc.). This step 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 virtual machine does the necessary Settings on the object, such as which class instance the object is, how to find the metadata information about the class (Java classes are represented as class metadata within Java hotspot VM), the object’s hash code, the object’s GC generation age, and so on. This information is stored in the object header of the object.

5) Object initialization

After the above work is done, a new object has been created from the virtual machine’s point of view, but from the Java program’s point of view, object creation has just begun, with all fields still having zero values. So, in general, executing the new instruction will be followed by the initialization (constructor) of the object as the programmer wishes so that a usable object is fully generated.

Object memory layout

In the HotSpot virtual machine, the layout of objects stored in memory can be divided into three areas: object headers, Instance Data, and alignment Padding.

Object head

The object header contains two parts of information. The first part is used to store the runtime data of the object itself, such as HashCode, GC generation age, lock status flag, thread held lock, bias thread ID, bias timestamp, etc. The other part is a type pointer, which is a pointer to an object’s class metadata that the virtual machine uses to determine which class the object is an instance of. If the object is a Java array, there is also a piece of data in the object header that records the length of the array.

The instance data

Instance data refers to the data used by the program during execution.

Alignment filling

Alignment padding does not necessarily exist and has no special meaning. It simply acts as a placeholder. Because HotSpot VM’s automatic memory management system requires that the object’s size be an integer multiple of 8 bytes. When the rest of the object’s data is not aligned, it needs to be completed by alignment padding.

Object access location

Objects are created to use objects, and our Java program needs reference data on the stack to manipulate specific objects on the heap. At present, there are two main access methods: handle and direct pointer.

handle

If handle access is used, the Java heap will allocate a chunk of memory as a handle pool. Reference stores the handle address of the object, and the handle contains the specific address information of the instance data and the type data respectively. The biggest benefit of using handles for access is that reference stores a stable handle address, which only changes the instance data pointer in the handle when the object is moved (a very common behavior in garbage collection). Reference itself does not need to be modified.

Direct Pointers

If direct pointer access is used, the direct stored in Reference is the address of the object.

The two object access methods have their own advantages. The biggest advantage of using direct pointer access is that it is faster and saves the time cost of a pointer location. Since object access is very frequent in Java, this kind of overhead is also a very considerable execution cost.

For Sun HotSpot, it uses direct pointer access for object access.

Determine object survival

Almost all object instances are stored in the heap, and the garbage collector needs to determine which of these objects are “alive” and which are “dead” before collecting them. (Dead represents objects that can no longer be used in any way.)

Reference counting method

Adds a reference counter to the object, incrementing it every time a reference is made to it, and decrement it when the reference is invalid.

Python is used, but mainstream VMS do not use it, because objects reference each other. In this case, additional mechanisms need to be introduced to deal with this, which affects efficiency.

Code validation

public class Isalive {
    public Object instance =null;
    // Take up memory for GC analysis
    private byte[] bigSize = new byte[10*1024*1024];

    public static void main(String[] args) {
        Isalive objectA = new Isalive(); // objectA local variable table GCRoots
        Isalive objectB = new Isalive(); // objectB local variable table
        // Reference each other
        objectA.instance = objectB; / / strong reference
        objectB.instance = objectA;
        // Disconnect reachable
        objectA =null;
        objectB =null;
        // Enforce garbage collectionSystem.gc(); [GC (system.gc ()) 23429K->808K(188416K),0.0292046 secs]
[Full GC (System.gc())  808K->739K(188416K), 0.1055627 secs]
Copy the code

You can see in the code that only objects that keep references to each other are still recycled, indicating that reference counting is not used in the JVM.

Accessibility analysis (Critical)

The basic idea of this algorithm is to search down from these nodes through a series of objects called “GC Roots” as the starting point, and the search path is called the Reference Chain. When an object is not connected to GC Roots by any reference chain, the object is proved to be unavailable. Objects as GC Roots include the following (emphasis on the first 4) :

  • Objects referenced in the virtual machine stack (the local variable table in the stack frame); Parameters, local variables, temporary variables, and so on used in the stack of called methods in each field.
  • The object referenced by the class static attribute in the method area; A Java class reference type static variable.
  • The object referenced by the constant in the method area; Example: a reference in a string constant pool.
  • Objects referenced by JNI (commonly referred to as Native methods) in the Native method stack.
  • Internal references to the JVM (class objects, exception objects NullPointException, OutofMemoryError, system classloader).
  • All objects held by synchronized keys.
  • Jmxbeans within the JVM, callbacks registered in JVMTI, native code caches, and so on.
  • “Ephemeral” objects in JVM implementations, objects referenced across generations.

All the above recycling is an object. The recycling conditions of a class are very strict. The following conditions must be met. (Just yes, not necessarily, because there are parameters that can be controlled)

  • All instances of the class have been reclaimed, that is, 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, and the methods of this Class cannot be accessed anywhere through reflection.
  • Parameter control: -xnoclassGC (Disabling garbage collection for classes can save some GC time and thus shorten the interruption time during application running)

Deprecated constants and static variables are recycled in much the same way as Class.

All kinds of reference

Strong reference

Object obj = new Object() is a strong reference. In any case, the garbage collector will never reclaim the referenced object as long as there is a strong reference association (reachable to the root).

Soft reference SoftReference

Objects associated with soft references that are useful but not necessary are recycled just before the system runs out of memory (or is thrown out if there is still not enough space after the recycle).

Code validation

/** * VM options : -Xms10m -Xmx10m -XX:+PrintGC */
public class TestSoftRef {

	public static class User{
		public int id = 0;
		public String name = "";
		public User(int id, String name) {
			super(a);this.id = id;
			this.name = name;
		}
		@Override
		public String toString(a) {
			return "User [id=" + id + ", name=" + name + "]"; }}public static void main(String[] args) {
		User u = new User(1."King"); // New is a strong reference
		SoftReference<User> userSoft = new SoftReference<User>(u); / / soft references
		u = null; // Eliminate strong references and ensure that this instance has only soft references from userSoft
		System.out.println(userSoft.get()); // See if the object is still there
		System.gc(); // Do a GC garbage collection, never in business code
		System.out.println("After gc");
		System.out.println(userSoft.get());
		// Populate the heap with data, resulting in OOM
		List<byte[]> list = new LinkedList<>();
		try {
			for(int i = 0; i < 100; i++) {
				// System.out.println("*************" + userSoft.get());
				list.add(new byte[1024 * 1024 * 1]); // The object of 1M is 100m}}catch (Throwable e) {
			// Print soft reference objects when an OOM exception is thrown
			System.out.println("Exception*************"+ userSoft.get()); }} User [id=1, name=King]
[GC (System.gc())  1870K->704K(9728K), 0.0462310 secs]
[Full GC (System.gc())  704K->696K(9728K), 0.0090122 secs] After GC User [id]=1, name=King]
[GC (Allocation Failure) -- 7946K->7946K(9728K), 0.0006002 secs]
[Full GC (Ergonomics)  7946K->7864K(9728K), 0.0150448 secs]
[GC (Allocation Failure) -- 7864K->7864K(9728K), 0.0046188 secs]
[Full GC (Allocation Failure)  7864K->7847K(9728K)Exception, 0.0229536 secs] * * * * * * * * * * * * *null
Copy the code

For example, a program is used to process user-supplied images. If all images are read into memory, the images can be opened quickly, but the memory space is huge, and some less used images waste memory space and need to be manually removed from memory. If every time an image is opened, it is read into memory from the disk file and displayed again, although the memory footprint is small, some frequently used images need to access the disk every time they are opened, which is costly. At this point you can build the cache with soft references.

A weak reference WeakReference

Objects associated with weak references that are useful (to a lesser degree than soft references) but not necessary will only survive until the next garbage collection, when GC occurs and will be collected regardless of memory availability.

Code validation

public class TestWeakRef {
	
	public static class User{
		public int id = 0;
		public String name = "";
		public User(int id, String name) {
			super(a);this.id = id;
			this.name = name;
		}
		@Override
		public String toString(a) {
			return "User [id=" + id + ", name=" + name + "]"; }}public static void main(String[] args) {
		User u = new User(1."King");
		WeakReference<User> userWeak = new WeakReference<User>(u);
		u = null; // Kill strong references and ensure that this instance has only userWeak weak references
		System.out.println(userWeak.get());
		System.gc(); // Do a GC garbage collection, never in business code.
		System.out.println("After gc"); System.out.println(userWeak.get()); }} Run result: User [id=1, name=King]
After gc
null
Copy the code

Note: SoftReference SoftReference and WeakReference can be used to create less important data caches when memory resources are tight. When the system runs out of memory, the contents of the cache can be freed. Practical application: WeakHashMap, ThreadLocal

Phantom reference PhantomReference

Ghost references, the weakest, can be recycled at any time. A notification is received during garbage collection to monitor whether the garbage collector is working properly.

Object allocation policy

Object allocation rules

Objects are allocated in Eden area first

In most cases, objects are allocated in the Eden region of the new generation. When the Eden area does not have enough space to allocate, the virtual machine will initiate a Minor GC.

Big object goes straight to the old age

Large objects are Java objects that require a large amount of contiguous memory. The most typical large objects are long strings or arrays with large numbers of elements. The reason to avoid large objects in the Java virtual machine is that when allocating space, it tends to trigger garbage collection in advance when there is plenty of space in memory to get enough contiguous space to place them. When copying objects, large objects imply high memory copy overhead.

HotSpot virtual machine provides – XX: PretenureSizeThreshold parameters, specified is greater than the set value of object directly in old age distribution, the aim is to avoid back and forth between Eden area and two Survivor area to copy, produce a lot of memory copy operation.

The purpose of this method is as follows: 1. Avoid massive memory replication. 2. Avoid garbage collection in advance when there is enough memory to allocate. The PretenureSizeThreshold parameter is only valid for Serial and ParNew collectors. -XX:PretenureSizeThreshold=4m

Long-lived objects enter the old age

Most collectors in the HotSpot VIRTUAL machine use generational collection to manage heap memory, so memory collection must be able to decide which live objects should be placed in the new generation and which should be placed in the old generation. To do this, the virtual machine defines an object Age counter for each object, which is stored in the object header.

If the object survives after Eden is born and passes the first Minor GC and can be accommodated by Survivor, it is moved to Survivor space and the object age is set to 1. Each time the object survives a Minor GC in Survivor, Age increases by 1, and when it reaches a certain age (concurrent garbage collector defaults to 15) and CMS is 6, it is promoted to the old age.

Set this parameter to -xx :MaxTenuringThreshold.

Dynamic object age determination

In order to better adapt to the memory conditions of different programs, the virtual machine does not always require that the object age must reach MaxTenuringThreshold to advance to the old age. If the sum of all object sizes of the same age in the Survivor space is greater than half of the Survivor space, Objects older than or equal to this age can go directly to the old age without waiting until the age specified in MaxTenuringThreshold.

Space allocation guarantee

Before Minor GC occurs, the virtual machine checks to see if the maximum available contiguous space of the old generation is greater than the total space of all objects of the new generation. If this condition is true, then Minor GC is guaranteed to be safe. If this is not true, the virtual machine checks the HandlePromotionFailure setting to see if the guarantee failure is allowed. If so, it continues to check whether the maximum available contiguous space of the old age is greater than the average size of the objects promoted to the old age. If so, a Minor GC is attempted, although this Minor GC is risky, and a Full GC is attempted if the guarantee fails. If less than, or if the HandlePromotionFailure setting does not allow risk, then do a Full GC instead.

Optimization techniques for virtual machines

Escape analysis

Principle: Analyze object dynamic scope. when an object is defined in a method, it may be referenced by external methods.

For example, a call argument passed to another method is called method escape.

For example, assigning a value to a variable accessed in another thread is called thread escape.

From never escape to method escape to thread escape, this is called the different escape degrees of objects from low to high.

Having an object allocate memory on the stack can improve the efficiency of the JVM if it is certain that an object will not escape the thread.

Code validation

/** * -xx :+DoEscapeAnalysis -xx :+PrintGC * Escape analysis is enabled by default */
public class EscapeAnalysisTest {
    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 50000000; i++) { // 50 million times --50 million objects
            allocate();
        }
        System.out.println((System.currentTimeMillis() - start) + " ms");
        Thread.sleep(600000);
    }

    static void allocate(a) { // Escape analysis (no escape method)
        // This myObject reference does not go out and is not used in any other way
        MyObject myObject = new MyObject(2020.2020.6);
    }

    static class MyObject {
        int a;
        double b;

        MyObject(int a, double b) {
            this.a = a;
            this.b = b; }}}Copy the code

Myboject is a non-escapable object that the JVM can allocate on the stack during the call.

Then observe the difference by turning DoEscapeAnalysis on and off. The JVM turns escape analysis on by default.

To enable escape analysis, VM options: -xx :+DoEscapeAnalysis -xx :+PrintGC9[GC (Allocation Failure) 49152K->816K(188416K),0.0167867 secs]
[GC (Allocation Failure)  49968K->824K(237568K), 0.0031163 secs]
[GC (Allocation Failure)  99128K->776K(237568K), 0.0327010 secs]
[GC (Allocation Failure)  99080K->840K(335872K), 0.0025301 secs]
[GC (Allocation Failure)  197448K->816K(335872K), 0.0015809 secs]
[GC (Allocation Failure)  197424K->816K(525312K), 0.0013120 secs]
[GC (Allocation Failure)  394032K->780K(525312K), 0.0278659 secs]
568Ms turned off escape analysis, and the JVM was doing garbage collection frequently, which caused the performance difference.Copy the code

If escape parsed objects can be allocated on the stack, then the life of the object follows the thread and there is no need for garbage collection. If you call this method frequently, you can get a big performance boost. After using escape analysis, objects satisfying escape are allocated on the stack.

Without escape analysis enabled, objects are allocated on the heap, and garbage collection is frequently triggered (which affects system performance), causing code to run slowly.

Local Thread Allocation buffer (TLAB)

For details, see the description of allocating buffers above.

Hi, lovely little friend, thank you for your patience to see here, I hope you harvest full, happy double!

I am Yunsheng, writing is not easy, thank you for your thumbs up, comments and favorites. See you next time!