This is the tenth article in the Learn More about the JVM series

Before we get to the Java garbage collection mechanism, it’s worth taking a look at the memory allocation and creation process for Java objects.

JDK8 memory area

During the execution of Java programs, the Java VIRTUAL machine divides the memory it manages into different data areas. As shown below:

The memory allocated by Java objects is primarily related to the heap, so I’ll cover only heap memory here.

The heap is the largest area of memory managed by the JVM, and its sole purpose is to hold instances of objects. All object instances and arrays are allocated memory on the heap. It is also the main administrative area for the garbage collector. The Java heap can be in a physically discontinuous space, as long as it is logically continuous. An area shared by threads. OutOfMemoryError is thrown if there is no memory in the heap to complete the instance allocation and the heap can no longer be extended.

To support garbage collection, the heap is divided into three parts:

  1. Young generation: often divided into Eden zone and From Survivor To Survivor zone (Eden space, From Survivor space, To Survivor space (space allocation ratio is 8:1:1)
  2. The old s
  3. Permanent generation (JDK 8 removed permanent generation)

(1) The heap is shared by all threads in the JVM, so the allocation of object memory on it needs to be locked, which also leads to the relatively high overhead of new objects.

(2) In order to improve the efficiency of object memory Allocation, Sun Hotspot JVM will allocate an independent space TLAB (Thread Local Allocation Buffer) for the created threads. The size of TLAB is calculated by JVM according to the running situation. There is no lock required to allocate objects on TLAB, so the JVM tries to allocate memory on TLAB when allocating objects on thread. In this case, the PERFORMANCE of allocating object memory in JVM is almost as efficient as that of C, but if the object is too large, it still uses heap space allocation directly.

(3) TLAB only applies to Eden Space of the new generation, so when writing Java programs, it is usually more efficient to allocate multiple small objects than large objects.

(4) All newly created objects will be stored in the new Generation of Yong Generation. If data from the Young Generation survives one or more GCS, it will be transferred to the OldGeneration. New objects are always created in Eden Space.

It is worth noting that TLAB is thread exclusive, but only in the “allocate” action is thread exclusive, in the read, garbage collection and other actions are thread shared. And there is no difference in use.

In other words, although each thread allocates a TLAB to the heap during initialization, it does not mean that the TLAB area is completely inaccessible to other threads. Other threads can read the TLAB area, but they cannot allocate memory in the area.

Java object creation

In Java programs, we have a variety of ways to create objects.

The easiest way to do this is to use the new keyword.

User user = new User();
Copy the code

In addition, you can use reflection to create objects:

User user = User.class.newInstance();
Copy the code

Or use newInstance of the Constructor class:

Constructor<User> constructor = User.class.getConstructor();
User user = constructor.newInstance();
Copy the code

The Clone method, deserialization, and Unsafe classes are also available. The Object.clone method and deserialization initialize instance fields of newly created objects by copying existing data directly. The broadening. AllocateInstance method does not initialize the instance field, whereas the new statement and reflection mechanism initialize the instance field by calling the constructor. For those who want to explore these three methods, I recommend reading five ways to create objects in Java

The new keyword

In the case of the new statement, the compiled bytecode will contain the new instruction to request memory and the Invokespecial instruction to invoke the constructor.

public class SubUser { public static void main(String[] args) { SubUser subUser = new SubUser(); }} / / parsing bytecode file public com. MSDN. Java hotspot. Object. SubUser (); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: new #2 // class com/msdn/java/hotspot/object/SubUser 3: dup 4: invokespecial #3 // Method "<init>":()V 7: astore_1 8: returnCopy the code

Without mentioning constructors, we can’t help but mention Java’s many constraints on constructors. First, if a class does not define any constructors, the Java compiler automatically adds one without arguments.

The Object class is the parent of all Java classes. Ordinary Java classes inherit the Object class by default, even if they are not declared. As shown in the case above, SubUser does not explicitly declare a constructor, and the Java compiler automatically adds a call to the superclass constructor. However, if the parent class does not have a no-argument constructor, then the constructor of the subclass needs to explicitly call the constructor of the parent class with arguments.

Explicit calls can be made either directly using the “super” keyword to call the parent constructor, or using the “this” keyword to call another constructor in the same class, as shown in the example code below. Both direct and indirect explicit calls are required as the first statement of the constructor to initialize the inherited superclass field first. (This can be circumvented by bytecode injection, as we discussed earlier when we talked about overloading.)

public class Cutomer {

  private String name;
  private int age;

  public Cutomer(String name, int age) {
    this.name = name;
    this.age = age; }}public class SubUser extends Cutomer {

  private long id;

  public SubUser(String name, int age) {
    super(name, age);
  }

  public SubUser(String name, int age, long id) {
// super(name, age);
    this(name, age);
    this.id = id; }}Copy the code

In summary, when we call a constructor, it calls the parent constructor first, up to the Object class. The callers of these constructors are the same object, that is, the object created by the new directive.

Let’s look at the bytecode contents of the first constructor in the SubUser class:

0: aload_0 1: aload_1 2: iload_2 3: invokespecial #1 // Method com/msdn/java/hotspot/object/Cutomer."<init>":(Ljava/lang/String; I)V 6: returnCopy the code

Compared to the default constructor without Customer, there are two more instructions, aload_1 and ILoad_2, corresponding to the name and age fields in Customer, that is, the subclass allocates memory covering all the instance fields in the parent class. That is, although a subclass does not have access to its parent class’s private instance fields, or its instance fields hide its parent class’s instance fields of the same name, the instance of the subclass still allocates memory for those instance fields.

Java object creation process

To illustrate the creation of an object:

From the diagram, we can see that the object creation steps are as follows:

  1. Class load checking: When a virtual machine is presented with a new instruction, it first checks to see if the instruction’s arguments locate the symbolic reference to the class in the constant pool, and whether the symbolic reference represents a class that has been loaded, parsed, and initialized. If not, the corresponding class loading process must be performed first.

  2. Memory allocation: After the class load check passes, the virtual machine next allocates memory for the new objects. The size of memory required by an object is determined after the class is loaded, and the task of allocating space for an object is equivalent to dividing a certain size of memory from the Java heap.

  3. Initialize zero: After memory allocation is complete, the VM needs to initialize the allocated memory space to zero (excluding the object header). This step ensures that the instance fields of the object can be used in Java code without assigning initial values, and the program can access the zero values corresponding to the data types of these fields.

  4. Setting the object header: After initializing the zero value, the virtual machine performs the necessary Settings on 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, and the object’s GC generation age. This information is stored in the object header. In addition, the object header can be set differently depending on the running status of the VM, for example, whether biased locking is enabled.

  5. Execute the init method: After all the work above is done, from the virtual machine’s point of view, a new object has been created, but from the Java program’s point of view, object creation has just begun, methods have not been executed, and all fields are still zero. So, in general, a new instruction is followed by a method that initializes the object as the programmer wants it to be, and then a usable object is fully generated.

  6. The Reference in the Java virtual machine stack points to the object we just created.

So how does memory allocation work?

Memory allocation mode

There are two allocation methods: “pointer collision” and “free list”, which are determined by the cleanliness of the Java heap, which in turn is determined by the use of the garbage collector with collation capabilities.

The choice between the two approaches depends on whether the Java heap memory is tidy. Whether the Java heap memory is clean depends on whether the GC collector’s algorithm is “mark-clean” or “mark-tidy” (also known as “mark-compress”). Notably, the copy-algorithm memory is clean as well.

ParallelGC is the default garbage collector for JDK8, as shown in the following command:

% java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC java version Java(TM) SE Runtime Environment (build 1.8.0_301-b09) Java HotSpot(TM) 64-bit Server VM (build 25.301-b09, mixed mode)Copy the code

UseParallelGC is the Parallel Avenge + Parallel Old.

% java -XX:+PrintGCDetails -version
java version "1.8.0 comes with _301"Java(TM) SE Runtime Environment (Build 1.8.0_301-b09) Java HotSpot(TM) 64-bit Server VM (build 25.301-b09, mixed mode) Heap PSYoungGen total 76288K, used 2621K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000) eden space 65536K, 4%, informs [x000000076ab00000 0, 0 x000000076ad8f748, 0 x000000076eb00000) from space 10752 k, 0%, informs [x0000000770000000 x000000076f580000 0, 0 x000000076f580000, 0) to space 10752 k, 0%, informs [x000000076eb00000 0, 0 x000000076eb00000, 0 x000000076f580000) ParOldGen total 175104 k, used 0K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000) object space 175104K, 0%, informs [x00000006c0000000 0, 0 x00000006c0000000, 0 x00000006cab00000) Metaspace informs the 2247 k, capacity 4480 k, committed 4480K, reserved 1056768K class space used 243K, capacity 384K, committed 384K, reserved 1048576KCopy the code

Parallel collector is similar to ParNew collector, and the new generation of ParNew collector uses the copy algorithm, the old age of mark-collation algorithm. So the Parallel collector’s heap memory is regular, so the default memory allocation in JDK8 is pointer collision.

One of the most important parts of Java object creation is setting the object header. How are these fields distributed in memory? Today we will look at the memory layout of objects.

Memory layout

In the JVM, objects are laid out in memory in three areas: object headers, instance data, and aligned padding.

Object head

The object header contains two pieces of information: a marker field, a type pointer, and, if the object is an array, a piece of data to record the length of the array.

Tag fields include HashCode, GC generation age, lock status flag, thread held lock, bias lock ID, bias timestamp, etc. The length of this part of data in 32-bit and 64-bit VMS is 32bit and 64bit respectively, which is officially called “Mark Word”. Mark Word is designed as an unfixed data structure to hold as much data as possible in a limited space.

For example, under 32-bit JVMS, in addition to the Mark Word default storage structures listed above, there are also the following structures that may change:

We can source in the JVM (hotspot/share/oops/markOop HPP) seen in the definition of object in the head store content

 public:
  // Constants
  enum { age_bits                 = 4,
         lock_bits                = 2,
         biased_lock_bits         = 1,
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
Copy the code

An example of the tag field structure in this file is as follows:

//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
Copy the code

The parameter meanings are as follows:

  • Hash: Hash code of an object
  • Age: indicates the generation age of the object
  • Biased_lock: bias lock identifier bit
  • Lock: identifier of the lock status
  • JavaThread* : ID of the thread holding the biased lock
  • Epoch: Biased timestamp

A type pointer points to the object’s class metadata, which the virtual machine can use to determine which class the object is an instance of. The bit length of this pointer is one word of the JVM, that is, 32 bits for a 32-bit JVM and 64 bits for a 64-bit JVM.

In a 64-bit Java virtual machine, the tag field of the object header takes up 64 bits, and the type pointer takes up 64 bits. That is, each Java object costs 16 bytes in memory.

In order to minimize the memory usage of objects, 64-bit Java virtual machines introduce the concept of compressed Pointers (corresponding to the virtual machine option -xx :+UseCompressedOops, enabled by default, 32-bit HotSpot VM does not support the UseCompressedOops parameter). Compacts 64-bit Java object Pointers in the heap into 32-bit Pointers.

For example, if the compression pointer is not enabled on an Integer object, check its memory allocation.

//-XX:-UseCompressedOops

Integer i = Integer.valueOf(5);
System.out.println(ClassLayout.parseInstance(i).toPrintable());

/ / output
java.lang.Integer object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           a0 72 7d 0d (10100000 01110010 01111101 00001101) (226325152)
     12     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
     16     4    int Integer.value                             5
     20     4        (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Copy the code

Let me explain the meaning of each field

  • OFFSETIs the offset, which is the number of bytes it takes to get to this field,
  • SIZEIs the size of the following type,
  • TYPEIs a type defined in Class,
  • DESCRIPTIONIs a description of the type. Object Header refers to the object header
  • VALUEisTYPEValue in memory.

The first part is the object header (16 bytes), and the second part is the int (4 bytes), which represents the specific value (20 bytes). However, the memory allocation of the object in the JVM must be an Integer multiple of 8 bytes. So you have to complete 4 bytes (the alignment padding described below), and then the total size of the Integer class is 24 bytes.

With the compression pointer enabled, the output is:

//-XX:+UseCompressedOops

java.lang.Integer object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           bf 22 00 f8 (10111111 00100010 00000000 11111000) (-134208833)
     12     4    int Integer.value                             5
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Copy the code

As you can see, the type pointer in the object header is also compressed to 32 bits, reducing the size of the object header from 16 to 12 bytes. Of course, compressed Pointers can operate not only on the type pointer of an object header, but also on fields of reference types, as well as on arrays of reference types.

By default, the 32-bit compressed pointer in the Java VIRTUAL machine can address up to 2 ^ 35 bytes, or 32GB of address space (more than 32GB will turn off the compressed pointer).

When dereferencing a compressed pointer, that is, when the reference is stored in a 64-bit register, we need to move it three bits to the left, plus a fixed offset, to get a pseudo-64-bit pointer that can address 32GB of address space.

The instance data

The order in which instance data is stored is affected by the virtual machine allocation policy and the order in which fields are defined in the source code.

Alignment filling

In the JVM (whether 32-bit or 64-bit), objects are already aligned at 8-byte boundaries. This alignment scheme is optimal for most processors.

Because HotSpot’s automatic memory management requires that the starting address of the object must be a multiples of 8 bytes, i.e. the size of the object must be a multiples of 8 bytes, and the data of the object’s header must be exactly a multiples of 8 bytes, so when the instance data is less than a multiples of 8 bytes, it needs to be completed by alignment padding.

Memory alignment exists not only between objects, but also between fields within objects. For example, the Java virtual machine requires long fields, double fields, and reference fields in the uncompressed pointer state to be multiples of 8.

Alignment padding also exists for fields in the object, as shown in the following example:

//-XX:-UseCompressedOops
class Stud{
  private long num;
  private int age;
}

Stud stud = new Stud();
System.out.println(ClassLayout.parseInstance(stud).toPrintable());
Copy the code

Output:

com.msdn.java.hotspot.object.Stud object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object  header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 48 dc 4d 05 (01001000 11011100 01001101 00000101) (88988744) 12 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 16 8 long Stud.num 0 24 4 int Stud.age 0 28 4 (loss due to the next object alignment) Instance size: 32 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalCopy the code

You can see that fields of type int are filled with alignment.

One of the reasons for memory alignment of ** fields is so that fields only appear in cached rows on the same CPU. ** If the fields are not aligned, it is possible to have fields that span cached rows. That is, reading the field may require replacing two cache rows, and the storage of the field may pollute both cache rows at the same time. Both cases are detrimental to the efficiency of the program. For example, in a 32-bit VM, the high 32 bits and the low 32 bits are not in the same cache line, resulting in read and write inconsistency.

Int fields are arranged in the same way as String fields. This brings us to another feature: field rearrangement.

Field rearrangement

Field rearrangement, as the name suggests, is the order in which the Java virtual machine reassigns fields to achieve memory alignment. There are three ways to arrange the Java virtual machine (for the Java virtual machine option -xx :FieldsAllocationStyle, the default is 1), but they all follow the following two rules.

First, if a field occupies C bytes, the offset of that field needs to be aligned to NC. Here offset refers to the difference between the field address and the object’s start address.

The Long class, for example, has only one instance field of type Long. In a 64-bit virtual machine that uses a compressed pointer, even though the object header is 12 bytes, the offset of the long field can only be 16, and the four bytes left empty in the middle are wasted.

// Compressed pointer is enabled by default
System.out.println(ClassLayout.parseClass(Long.class).toPrintable());

/ / output
java.lang.Long object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    12        (object header)                           N/A
     12     4        (alignment/padding gap)                  
     16     8   long Long.value                                N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
Copy the code

According to rule 1, the long instance memory offset (that is, the starting address) must be 8N, such as 8, 16, 24, and so on; Since the header size of an object of type Long is 12 bytes (say 0, 11), according to rule 1, Long instances can only be stored from address 16 (16, 24).

Second, the offset of the field inherited by a subclass must be the same as the offset of the field corresponding to the parent class.

In the implementation, the Java virtual machine also aligns the starting position of the subclass field. For 64-bit virtual machines that use compressed Pointers, the first field of the subclass needs to be aligned to 4N. For 64-bit virtual machines with compressed Pointers turned off, the first field of the subclass needs to be aligned to 8N.

Here is a case for analysis:

class A {

  long l;
  int i;
}

class B extends A {

  long l;
  int i;
}

B b= new B();
System.out.println(ClassLayout.parseInstance(b).toPrintable());
Copy the code

The offsets of each field of class B with and without compression pointer enabled are printed below.

 //-XX:+UseCompressedOops
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           81 c1 00 f8 (10000001 11000001 00000000 11111000) (-134168191)
     12     4    int A.i                                       0
     16     8   long A.l                                       0
     24     8   long B.l                                       0
     32     4    int B.i                                       0
     36     4        (loss due to the next object alignment)
Instance size: 40 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Copy the code

According to rule 1, the memory offset (that is, the starting address) of the long field must be 8N, such as 8, 16, 24, etc., so the int field of class A is placed before the long field, and the long field of class B is placed before the int field. Finally, since the overall size of the object needs to be aligned to 8N, the object is filled with 4 bytes of blank space at the end.

//-XX:-UseCompressedOops
com.msdn.java.hotspot.object.B object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           18 12 f6 16 (00011000 00010010 11110110 00010110) (385225240)
     12     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
     16     8   long A.l                                       0
     24     4    int A.i                                       0
     28     4        (alignment/padding gap)                  
     32     8   long B.l                                       0
     40     4    int B.i                                       0
     44     4        (loss due to the next object alignment)
Instance size: 48 bytes
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total
Copy the code

According to rule 2, the long field of class A precedes the int field, and the long field of class B precedes the int field, so there is A blank space after the int field.

extension

Byte order

Byte order refers to the memory storage order of data that occupies more than one byte. There are two types of byte order: small endian and big endian.

There are two types of byte order: big-endian and little-endian.

  • Large segment: High-byte data is stored at low addresses, and low-byte data is stored at high addresses.
  • Small segment: The high-byte data is stored at the high-byte address, and the low-byte data is stored at the low-byte address.

For example, a hexadecimal number 0x12345678 contains four bytes, namely 0x12, 0x34, 0x56, and 0x78. Therefore, 0x12 is the high value and 0x78 is the low value.

You can think of memory as a large array. The index of the array is the memory address, and the elements stored in the index are the real values.

The first order: the low address stores the high data, called the big-endian mode

The second order: the low address stores the low value data, called the little endian mode

It can be seen that the big-end mode is more consistent with human reading habits; The small-endian mode is more consistent with computer processing, because computers start from the low end.

How does Java see which byte order belongs to?

JOL stands for Java Object Layout. Object layout is a widget that analyzes Object layouts in the JVM. This includes the usage of Object in memory, the reference of instance objects, and so on.

1. Introduce dependencies first

      <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.14</version>
      </dependency>
Copy the code

2. View JVM information using JOL

Public class ObjectHeadTest {public static void main(String[] args) {// Check the byte order System.out.println(ByteOrder.nativeOrder()); / / print the current information System. The JVM out. The println (" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "); System.out.println(VM.current().details()); }}Copy the code

Output:

LITTLE_ENDIAN
======================================
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Copy the code

As you can see, Java uses small-endian mode for byte ordering in memory.

reference

Java big endian byte order _ Understand byte order

JVM Pointers to CompressedOops

JAVA object header analysis and Synchronized locking

JVM Details (3) – Runtime data area