Finally, I used JOL to directly break all your imagination about Java objects

The advantage of using an object-oriented programming language is that although there is no girlfriend, new objects can still be created. Java is an object-oriented programming language. We use java to create new objects every day, but it is estimated that few people know what the new objects look like, whether they are beautiful or ugly, and does it meet our requirements?

For ordinary Java programmers, they may never have considered the problem of objects in Java, and they can write good code if they don't understand these. But for a geek with a deliberate spirit, he will definitely think more, and more, what the objects in java are like.

Today, I will introduce you a tool JOL, which can satisfy all your imagination of java objects.

Finally, I used JOL to directly break all your imagination about Java objects

 

Introduction to JOL

The full name of JOL is Java Object Layout. It is a small tool for analyzing the layout of objects in the JVM. Including the occupancy of Object in memory, the reference of instance objects, and so on.

JOL can be used in code or run independently on the command line. I will not specifically introduce the command line here. Today I will mainly explain how to use JOL in the code.

To use JOL, you need to add maven dependency:

<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.10</version>

After adding the dependency, we can use it.

Use JOL to analyze VM information

First, let's look at how to use JOL to analyze JVM information. The code is very, very simple:

log.info("{}", VM.current().details());

Output result:

# 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]

In the above output, we can see: Objects are 8 bytes aligned, which means that the bytes allocated by all objects are integer multiples of 8.

Use JOL to analyze String

None of the above is the point, the point is how to use JOL to divide the information into class and instance.

In fact, the size of objects in java, except for arrays, should be fixed. Let's take a look at one of the most commonly used strings first:

log.info("{}",ClassLayout.parseClass(String.class).toPrintable());

In the above example, we use ClassLayout to parse a String class, first look at the output:

[main] INFO com.flydean.JolUsage - java.lang.String object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0    12           (object header)                           N/A
     12     4    byte[] String.value                              N/A
     16     4       int String.hash                               N/A
     20     1      byte String.coder                              N/A
     21     1   boolean String.hashIsZero                         N/A
     22     2           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total

First explain the meaning of each field, OFFSET is the offset, that is, the number of bytes occupied to this field position, SIZE is the size of the following type, TYPE is the type defined in Class, DESCRIPTION is the description of the type, and VALUE is TYPE The value in memory.

Analyzing the above output, we can conclude that there are 5 parts occupying space in the String class. The first part is the object header, which occupies 12 bytes, the second part is a byte array, which occupies 4 bytes, and the third part is The hash value represented by int occupies 4 bytes, the fourth part is the coder represented by byte, which occupies 1 byte, and the last one is hashIsZero represented by boolean, which occupies 1 byte, and a total of 22 bytes. However, the allocation of object memory in the JVM must be an integral multiple of 8 bytes, so 2 bytes must be completed. Finally, the total size of the String class is 24 bytes.

Someone may ask little F, if there are a lot of data in the string, is the size of the object still 24 bytes?

This question is very level, let's take a look at how to use JOL to parse the information of String objects:

log.info("{}",ClassLayout.parseInstance("www.flydean.com").toPrintable());

In the above example, we used parseInstance instead of parseClass to parse the String instance information.

Output result:

[main] INFO com.flydean.JolUsage - java.lang.String object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 c2 63 a2 (00000001 11000010 01100011 10100010) (-1570520575)
      4     4           (object header)                           0c 00 00 00 (00001100 00000000 00000000 00000000) (12)
      8     4           (object header)                           77 1a 06 00 (01110111 00011010 00000110 00000000) (399991)
     12     4    byte[] String.value                              [119, 119, 119, 46, 102, 108, 121, 100, 101, 97, 110, 46, 99, 111, 109]
     16     4       int String.hash                               0
     20     1      byte String.coder                              0
     21     1   boolean String.hashIsZero                         false
     22     2           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total

First look at the conclusion, like String Class, this String object really only occupies 24 bytes.

The results of instance analysis and Class analysis are similar, because it is an instance object, so the value of VALUE is added.

We know that after JDK9, the underlying storage of String has changed from Char[] to Byte[] to save String storage space. In the above output, we can see that the String.value value is indeed very long, but what is stored in the String is only the reference address of the Byte array, so 4 bytes are enough.

Analyze arrays using JOL

Although the size of String is constant, the size of its underlying array is variable. Let's give another example:

log.info("{}",ClassLayout.parseClass(byte[].class).toPrintable());

Output result:

[main] INFO com.flydean.JolUsage - [B object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0    16        (object header)                           N/A
     16     0   byte [B.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

The analysis result of the class shows that the Byte array occupies 16 bytes.

Look at the actual situation:

log.info("{}",ClassLayout.parseInstance("www.flydean.com".getBytes()).toPrintable());

Output result:

[main] INFO com.flydean.JolUsage - [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)                           22 13 07 00 (00100010 00010011 00000111 00000000) (463650)
     12     4        (object header)                           0f 00 00 00 (00001111 00000000 00000000 00000000) (15)
     16    15   byte [B.<elements>                             N/A
     31     1        (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 1 bytes external = 1 bytes total

You can see that the size of the array has really changed, this time it has become 32 bytes.

Auto-boxing using JOL analysis

We know that there is a basic type in Java and its corresponding Object type, such as long and Long. Let's analyze the memory difference between the two in the JVM:

log.info("{}",ClassLayout.parseClass(Long.class).toPrintable());

Output result:

[main] INFO com.flydean.JolUsage - 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

You can see that a Long object occupies 24 bytes, but the value of the long stored in it only occupies 8 bytes.

Look at an example:

log.info("{}",ClassLayout.parseInstance(1234567890111112L).toPrintable());

Output result:

[main] INFO com.flydean.JolUsage - java.lang.Long object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           9a 15 00 00 (10011010 00010101 00000000 00000000) (5530)
     12     4        (alignment/padding gap)                  
     16     8   long Long.value                                1234567890111112
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

Use JOL to analyze citation relationships

Above we used JOL to analyze the space usage inside the class, so if there are external references, can we analyze it?

HashMap hashMap= new HashMap();
hashMap.put("flydean","www.flydean.com");
log.info("{}", GraphLayout.parseInstance(hashMap).toPrintable());

Above we use a different layout: GraphLayout, which can be used to analyze external references.

Output result:

[main] INFO com.flydean.JolUsage - java.util.HashMap@57d5872cd object externals:
          ADDRESS       SIZE TYPE                      PATH                           VALUE
        7875f9028         48 java.util.HashMap                                        (object)
        7875f9058         24 java.lang.String          .table[14].key                 (object)
        7875f9070         24 [B                        .table[14].key.value           [102, 108, 121, 100, 101, 97, 110]
        7875f9088         24 java.lang.String          .table[14].value               (object)
        7875f90a0         32 [B                        .table[14].value.value         [119, 119, 119, 46, 102, 108, 121, 100, 101, 97, 110, 46, 99, 111, 109]
        7875f90c0         80 [Ljava.util.HashMap$Node; .table                         [null, null, null, null, null, null, null, null, null, null, null, null, null, null, (object), null]
        7875f9110         32 java.util.HashMap$Node    .table[14]                     (object)

From the result, we can see that HashMap itself occupies 48 bytes, and it references the key and value that occupy 24 bytes.

to sum up

Using JOL can analyze java classes and objects, which is very helpful for our understanding and implementation of JVM and java source code.

Author: flydean
link: https: //juejin.im/post/5edae70551882543194ed3e1

Guess you like

Origin blog.csdn.net/qq_45401061/article/details/108631205