The article was first published in the public number [watch code to go to work], it is recommended to pay attention to the public number, read the latest article in time.
Must read: mp.weixin.qq.com/s?__biz=MzI…
Hi, I’m Tin, and this is my 19th original articleWe’ve all been thereInteger A =128, Integer B =128, but a==b is not valid
Confusion, today combined with source code and Java unpacking said one of the reasons, first on a directory:
An Integer with equal values ==
Two, automatic disassembly/packing
2.1 Automatic Packing
2.2 Automatic unpacking
Other basic types of caches
Four, conclusion
An Integer with equal values ==
There are two variables, A and B, which are assigned the same value respectively, but in different cases, the comparison of the == sign gives completely different results.
package com.tin.example.lang; /** * title: IntegerTest * <p> * description: Public class IntegerTest {public static void IntegerTest {public static void IntegerTest main(String[] args) throws InterruptedException { Integer a = 127; Integer b = 127; System.out.println("res:" + (a == b)); a = 128; b = 128; System.out.println("res:" + (a == b)); a = -128; b = -128; System.out.println("res:" + (a == b)); a = -129; b = -129; System.out.println("res:" + (a == b)); }}Copy the code
@ Example code 1
When we run the sample code above, the output looks like this:
res:true
res:false
res:true
res:false
Copy the code
Now, when we look at this, we should all be wondering, why is it equal in some cases and not equal in others?
As we all know, == compares object address references, and unequal means that objects are located at different memory addresses.
Try to guess: equal because pointing to the same object, not equal because pointing to different objects.
So, why do some values point to the same object and some values don’t?
To know the real reason, a look at the source code, there is no secret under the source code.
Java.lang.Integer#valueOf(int)
/** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #Integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * This method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code Integer} Instance representing {@code I}. * @since 1.5 */ 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 API makes it clear that this method is always cached- 128 ~ 127
, and it is possible to cache values outside this range.Let’s understand the real principle behind this statement:
Always cache values between -128 and 127
Open the Java directly. Lang. Integer. IntegerCache source.
java.lang.Integer.IntegerCache
Is an inner class of Integer, whose sole purpose is to implement local caching, and has a static block inside:The meaning is that when the JVM starts, the- 128 ~ 127
The integer between is stored in the cache.
You can also see that all objects in the cache are the ones from new Integer().
Cache [I + (-integercache.low)] is the valueOf the same int in the cache.
In the example above, a=127 and b=127 can be read from the cache, and the same object is read.
Int values beyond this range are not cached. Instantiated by new Integer(I), the result of the == comparison is false.
It is also possible to cache values beyond -128 to 127
We can customize the upper limit of the IntegerCache cache value range. For example, I can change the upper limit to 256, which supports the cache between -128 and 256 integers.
Add the following code to the startup parameters:
-Djava.lang.Integer.IntegerCache.high=256
Copy the code
Let’s run it again@ Example code 1
, the result looks like this:So obviously Integer a =128 is the same thing as Integer B =128,= =
The result of the comparison is true.
You can also use another VM parameter setting to achieve the same effect, as follows:
-XX:AutoBoxCacheMax=256
Copy the code
Support for this setting is also reflected in the source code:If the value is less than 127, the default value is 127, that is, the upper limit is 127.
Two, automatic disassembly/packing
What is automatic packing and unpacking?
❝
Boxing is Java’s automatic conversion of a primitive type to the corresponding wrapper type, such as converting an int variable to an Integer object. This process is called boxing, whereas the process of converting a wrapper type to a primitive type is called unboxing, such as converting an Integer object to an int value. Since the boxing and unboxing is done automatically by Java internal mechanisms, it is called automatic boxing and unboxing.
The basic types byte, short, char, int, long, float, double, and Boolean correspond to encapsulated classes byte, short, Character, Integer, long, float, double, Boolean, they all have an automatic unboxing process.
❞
2.1 Automatic Packing
The Integer a = 127 statement does not use java.lang.Integer#valueOf(int).
The reason is simple: Integer a = 127 is not the same as new Integer(127), but the same as integer.valueof (127), which refers to Java’s automatic boxing principle.
To answer the question of when Java does boxing, we need to open the assembly instruction code for class.
Run the following command:
javac IntegerTest.javajavap -c IntegerTest
Copy the code
The following code is obtained:
Compiled from "IntegerTest.java" public class com.tin.example.lang.IntegerTest { public com.tin.example.lang.IntegerTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]) throws java.lang.InterruptedException; 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: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 15: aload_1 16: aload_2 17: if_acmpne 24 20: iconst_1 21: goto 25 24: iconst_0 25: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Z)Ljava/lang/String; 30: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 33: sipush 128 36: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 39: astore_1 40: sipush 128 43: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 46: astore_2 47: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 50: aload_1 51: aload_2 52: if_acmpne 59 55: iconst_1 56: goto 60 59: iconst_0 60: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Z)Ljava/lang/String; 65: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 68: bipush -128 70: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 73: astore_1 74: bipush -128 76: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 79: astore_2 80: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 83: aload_1 84: aload_2 85: if_acmpne 92 88: iconst_1 89: goto 93 92: iconst_0 93: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Z)Ljava/lang/String; 98: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 101: sipush -129 104: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 107: astore_1 108: sipush -129 111: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 114: astore_2 115: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 118: aload_1 119: aload_2 120: if_acmpne 127 123: iconst_1 124: goto 128 127: iconst_0 128: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Z)Ljava/lang/String; 133: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 136: return }Copy the code
The two key command statements are described as follows:
- Bipush 127: Indicates that the JVM pushes constant values between -128 and 127.
- Invokestatic # 2: Invokestatic is the bytecode instruction for the method invocation, and #2 indicates the index number of the symbol reference in the constant pool. By retrieving the constant table, you can see that the final representation is a string literal. For example Java/lang/Integer. The valueOf: (I) Ljava/lang/Integer, this is the method of symbol references.
❝
To make it easier to understand bytecodes, javap decomcompiled bytecodes annotate symbolic references that indicate the final representation value of the symbolic reference, such as Java /lang/Object.””:()V.
Symbolic References are a string that unambiguously identifies an entity (such as a method/field) and is translated at run time as a Direct Reference. In the case of a method, this is the entry address of the method.
So, the essence of a method call is to determine a direct reference to a method based on its symbolic reference (the entry address)
❞
This is decided at Java compilation timeInteger a = 127
Is equivalent toInteger a = Integer.valueOf(127)
For this reason, we analyzed the source code of integer.valueof above.
2.2 Automatic unpacking
Automatic unpacking, just the opposite of packing.
Let’s write the following demo code:
Integer b = 500; int c = 500; System.out.println("res:" + (b == c));Copy the code
You can guess, b is equal to c.
Similarly, let’s decompile the Java file to see what the final assembly instruction looks like:Java unboxed the Integer wrapper type when b == cb.intValue()
.java.lang.Integer#intValue
The return value is the base type value, source code as follows:So,b == c
Was established.
Other basic types of caches
ValueOf (int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
Public static Boolean valueOf(Boolean b) {return (b? TRUE : FALSE); Byte public static byte valueOf(byte b) {final int offset = 128; return ByteCache.cache[(int)b + offset]; Byte public static Short valueOf(Short s) {final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } Character public static Character valueOf(char c) {if (c <= 127) {must cache return CharacterCache.cache[(int)c]; } return new Character(c); } public static Integer valueOf(int I) {if (I >= integerCache.low &&i <= integerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } public static Long valueOf(Long l) {final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); } public static double valueOf(double d) {return new double (d); } public static float valueOf(float f) {return new float (f); }Copy the code
As you can see from the valueOf method, all wrapper types have caching implementations except for Boolean, Double, and Float. The cache value range corresponding to each packaging type is as follows:
Basic types of | The size of the | Packing type | The cache scope | Whether custom cache ranges are supported |
---|---|---|---|---|
boolean | 6bit | Bloolean | / | / |
char | 8bit | Character | 0 ~ 127 | no |
byte | 8bit | Byte | – 128 ~ 127 | no |
short | 16bit | Short | – 128 ~ 127 | no |
int | 32bit | Integer | – 128 ~ 127 | support |
long | 64bit | Long | – 128 ~ 127 | no |
float | 32bit | Float | / | / |
double | 64bit | Double | / | / |
Four, conclusion
I am Tin, an ordinary engineer who is trying to make himself better. My experience is limited, knowledge is shallow, if you find something wrong with the article, you are very welcome to add me, I will carefully review and modify.
It is not easy to persist in creation. Your positive feedback is the most powerful motivation for me to persist in output. Thank you!
Finally, attach a link to the original text: mp.weixin.qq.com/s?__biz=MzI…