Let’s talk about Java Integer cache pool IntegerCache

directory

  1. Start with a classic case
  2. IntegerCache introduction
  3. Bytecode verification
  4. conclusion
  5. extension
  6. Expand the conclusion
  7. Written in the end

This article from the shallow to the deep, the difficulty of the whole is not high, may be some positions for some students are blind.

If you are in a hurry, you can jump to the conclusion to see the conclusion.

Bytecode verification and extension may be difficult for beginners to read, so you can record them and wait for some time to learn them in the future.

Introducing classic cases

Let’s start with a classic example

public class IntegerCacheExample { public static void main(String[] args) { Integer a = 127; Integer b = 127; System.out.printf("a==b: %s.\n", a == b); Integer c = 128; Integer d = 128; System.out.printf("c==d: %s.\n", c == d); }}Copy the code

Very simple two outputs, think about it for five seconds and then open and fold and see the answer.

Fold: Click to see the answer

a==b: true.


c==d: false.

I don’t know if the answer is the same as you think, but let’s talk about why.

Integer Specifies the cache pool IntegerCache

Integer cache pool? Is it something like a string cache pool?

The only similarity is that the pool does store some values.

IntegerCache, let’s go to java.lang.Integer source code to find out.

Jdk1.8.0-OpenJDK

IntegerCache is an internal class in line 780, so I’m going to look at the comment first.

Cache to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive) as required by JLS.
The cache is initialized on first usage. The size of the cache may be controlled by the -XX:AutoBoxCacheMax=<size> option. During VM initialization,
java.lang.Integer.IntegerCache.high property may be set and saved in the private system properties in the sun.misc.VM class.

private static class IntegerCache {
Copy the code

Google Translate explains:

Object identity semantics cached to support auto-boxed values between -128 and 127 (inclusive) as required by JLS. The cache is initialized the first time it is used. The size of the cache can be controlled by the -xx :AutoBoxCacheMax=<size> option. In the process of VM initialization, Java. Lang. Integer. IntegerCache. High attribute may be set up and stored in the sun. The misc. VM class in the system of private property.Copy the code

Key words:

  1. – 128 ~ 127
  2. The size can be adjusted by ** -xx :AutoBoxCacheMax**

The cache generation ranges from -128 to 127, and the parameters can be modified by configuring the JVM parameter AutoBoxCacheMax before starting.

So we know why a==b is true and c==d is false, because both a and B are taken from the cache pool and point to the same pointer, while C and D are not taken from the cache pool and point to their respective memory addresses. (If you don’t understand what a new object refers to a new memory address, you can refer to the blog memory post. If you can’t find it, you may not have finished writing it and have not published it yet.)

Let’s try to see if this is true

IntegerCache bytecode authentication– This section is difficult for beginners to read. It can be easily read. There is no need to study deeply

Look at the pointer address first. Run the following code

The system.identityHashCode (Object x) method can return the memory address of an Object, regardless of whether the Object’s class overrides the hashCode() method.

System.out.printf("a:%s.\n", System.identityHashCode(a));
System.out.printf("b:%s.\n", System.identityHashCode(b));
System.out.printf("c:%s.\n", System.identityHashCode(c));
System.out.printf("d:%s.\n", System.identityHashCode(d));
Copy the code

The output is as follows:

a:1118140819.
b:1118140819.
c:1975012498.
d:1808253012.
Copy the code

You can see that a and B point to the same address, and c and D point to different addresses. Let’s take a look at how the JVM does this from a bytecode perspective.

To reduce bytecode complexity, I commented out the comparison section of the code

public class IntegerCacheExample { public static void main(String[] args) { Integer a = 127; Integer b = 127; // System.out.printf("a==b: %s.\n", a == b); Integer c = 128; Integer d = 128; // System.out.printf("c==d: %s.\n", c == d); }}Copy the code

Generating bytecode and executing the command in command line mode in the directory of the main method produces a.class file with the same name

javac IntegerCacheExample.java
Copy the code

View bytecode

javap -c IntegerCacheExample.class
Copy the code

The bytecode is as follows:

Compiled from "IntegerCacheExample.java"
public class com.xh.basic.lang.integer.IntegerCacheExample {
  public com.xh.basic.lang.integer.IntegerCacheExample();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        127
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: bipush        127
       8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      11: astore_2
      12: sipush        128
      15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      18: astore_3
      19: sipush        128
      22: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: astore        4
      27: return
}
Copy the code

Don’t worry if you don’t understand. I’m not omnipotent. Let’s find some key positions to interpret.

Public static void main public static void main public static void main

We see it in lines 2, 8, 15, and 22 under Main

// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer
Copy the code

As you can guess, JAVA makes the following optimizations at runtime

Integer x = num; X = integer.valueof (num);Copy the code

We follow the source code into the integr. valueOf method

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
Copy the code

The source code tells us that when valueOf is executed, it checks whether I is in the range [low, high]. If it is in the range, it is taken from IntegerCache. If it is not in the range, new Integer(I) is performed, so we can conclude.

conclusion

When Integer x = num is initialized, if num is in the range from -128 to 127 (the default range), the value will be retrieved from IntegerCache and will point to the same pointer address. If num is outside the range, new Integer will point to a different pointer.

extension

We see that the IntegerCache comment says -xx :AutoBoxCacheMax, so we can try it out and see if it works and what happens when we change it.

We add -xx to the JVM run parameter :AutoBoxCacheMax=200

I used IDEA, and some IDEA did not have this VM options, so we selected the interface position one by one

Run –> Eidt Configurations –> Modify options –> Add VM options

Once added, you can see it in the interface location, and you can add the running parameters

Run the code after Apply to see the effect

AutoBoxCacheMax=200

sun.misc.VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”);

This method gets the value of AutoBoxCacheMax in the VM parameter

public class IntegerCacheExample { public static void main(String[] args) { System.out.printf("high:%s.\n", sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")); Integer a = 127; Integer b = 127; System.out.printf("a==b: %s.\n", a == b); Integer c = 128; Integer d = 128; System.out.printf("c==d: %s.\n", c == d); Integer e = 200; Integer f = 200; System.out.printf("e==f: %s.\n", e == f); Integer g = 201; Integer h = 201; System.out.printf("g==h: %s.\n", g == h); System.out.printf("a:%s.\n", System.identityHashCode(a)); System.out.printf("b:%s.\n", System.identityHashCode(b)); System.out.printf("c:%s.\n", System.identityHashCode(c)); System.out.printf("d:%s.\n", System.identityHashCode(d)); System.out.printf("e:%s.\n", System.identityHashCode(e)); System.out.printf("f:%s.\n", System.identityHashCode(f)); System.out.printf("g:%s.\n", System.identityHashCode(g)); System.out.printf("h:%s.\n", System.identityHashCode(h)); }}Copy the code

The output is as follows:

high:200.
a==b: true.
c==d: true.
e==f: true.
g==h: false.
a:1118140819.
b:1118140819.
c:1975012498.
d:1975012498.
e:1808253012.
f:1808253012.
g:589431969.
h:1252169911.
Copy the code

You can see that 200 is taken from IntegerCache and 201 is a new Integer for a different pointer. Configuration to take effect

AutoBoxCacheMax=126

If it’s less than 127, is it valid? Let’s test that out.

public class IntegerCacheExampleMinValue { public static void main(String[] args) { System.out.printf("high:%s.\n", sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")); Integer a = 127; Integer b = 127; System.out.printf("a==b: %s.\n", a == b); Integer c = 128; Integer d = 128; System.out.printf("c==d: %s.\n", c == d); Integer min126a = 126; Integer min126b = 126; System.out.printf("min126a==min126b: %s.\n", min126a == min126b); System.out.printf("a:%s.\n", System.identityHashCode(a)); System.out.printf("b:%s.\n", System.identityHashCode(b)); System.out.printf("c:%s.\n", System.identityHashCode(c)); System.out.printf("d:%s.\n", System.identityHashCode(d)); System.out.printf("min126a:%s.\n", System.identityHashCode(min126a)); System.out.printf("min126b:%s.\n", System.identityHashCode(min126b)); }}Copy the code

The output is as follows:

high:126.
a==b: true.
c==d: false.
min126a==min126b: true.
a:697960108.
b:697960108.
c:943010986.
d:1807837413.
min126a:2066940133.
min126b:2066940133.
Copy the code

Min126a ==min126b is ok, but why is a==b? The cache pool high is 126. So 127 should be new? Did the configuration not work? It’s still 127?

By printing the value of high, we found that the configuration did take effect, but it did not take effect because the source code made a judgment. Specify a method for assigning the maximum value of the cache pool in IntegerCache.

String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue ! = null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } }Copy the code

The key code

i = Math.max(i, 127);
Copy the code

As you can see, it takes the vm parameter AutoBoxCacheMax and 127 and takes the maximum value, that is, if we set the AutoBoxCacheMax parameter to less than 127, it won’t take effect.

If you don’t understand, please leave a message to me at the end of this article. I will reply one by one.

Expand the conclusion

-xx :AutoBoxCacheMax You can change the maximum range of the cache pool. The value must be greater than 127 to take effect. If the value is smaller than or equal to 127, the default value 127 is still used.

Written in the end

I am dahui, a program ape who is still working hard. I hope you and I can continue to learn and grow in the future.

And don’t lose your joy. Be a happy programmer.

I hope this article can bring you some benefits.