Constant pool in java

The runtime data area of JVM mainly includes heap, stack, method area, program counter, etc.

The method area is used to store class information, constants, static variables and code compiled by the real-time compiler that has been loaded by the virtual machine.

More specifically, static variables + constants + class information (version, method, field, etc.) + runtime constant pool exists in the method area. The method is part of the constant pool

The constant pool stores various literal and symbolic references generated by the compiler. Literal quantity means constant in Java. For example, text string, constant modified by final, etc. Method references include fully qualified names of classes and interfaces, method names and descriptors, field names and descriptors, etc.

What's the use of constant pools?

Advantages: constant pool avoids the frequent creation and destruction of objects and affects the system performance, and realizes the sharing of objects.

Take chestnuts for example: Integer constant pool (buffer pool), and string constant pool

Integer constant pool:

We know that = = basic data types compare values, while reference data types compare memory addresses.

public void TestIntegerCache()
{
    public static void main(String[] args)
    {
        
        Integer i1 = new Integer(66);
        Integer i2 = new integer(66);
        Integer i3 = 66;
        Integer i4 = 66;
        Integer i5 = 150;
        Integer i6 = 150;
        System.out.println(i1 == i2);//false
        System.out.println(i3 == i4);//true
        System.out.println(i5 == i6);//false
    }
    
}

i1 and i2 use the new keyword, and each time new creates an object on the heap, so i1 == i2 is false.

Why is i3 == i4 true? Integer i3 = 66 actually has a one-step boxing operation, that is, 66 of int type is boxed into integer through the valueOf method of integer.

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

The valueOf method of Integer is very simple. It determines whether the variable is between the minimum (- 128) and maximum (127) of IntegerCache. If it is, it returns the contents of the constant pool. Otherwise, new is an Integer object.

And  IntegerCache  is the static internal class of  Integer. Its function is to "cache" the number between [- 128127] in the cache array of IntegerCache class. The valueOf method is to call the cache array of constant pool, but point the i3 and i4  variable references to the constant pool without creating real objects. new Integer(i) creates objects directly in the heap.

The IntegerCache class contains a constructor, three static variables: low minimum, high maximum, and Integer array, as well as a static code block. The function of static code block is to initialize the high maximum value and Integer array when the IntegerCache class is loaded. That is, when the IntegerCache class is loaded, the maximum and minimum values and the Integer array have been initialized. This Integer array actually contains all values between - 128 and 127.

IntegerCache source code

private static class IntegerCache {
        static final int low = -128;//minimum value
        static final int high;//Maximum
        static final Integer cache[];//Cache array
 
        //Privatize the construction method and don't let others create it. The idea of singleton mode
        private IntegerCache() {}
 
        //When the class is loaded, the static code block is executed. The function is to buffer the number between - 128 and 127 in the cache [] array
        static {
            // high value may be configured by property
            int h = 127;
            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.
                }
            }
            high = h;
 
            cache = new Integer[(high - low) + 1];//Initialize the cache array, which is determined according to the maximum and minimum values
            int j = low;
            for(int k = 0; k < cache.length; k++)//Traversal puts data into cache array
                cache[k] = new Integer(j++);
 
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
 
    }

i5 == i6 is false, which is because {150 is not between the maximum and minimum values of Integer constant pool [- 128127], so new creates an object, so it is false.

Take another look at the code of unpacking.

public static void main(String[] args){
       Integer i1 = new Integer(4);
       Integer i2 = new Integer(6);
       Integer i3 = new Integer(10);
       System.out.print(i3 == i1+i2);//true
    }

Since i1 and i2 are Integer objects, the + operator cannot be used. First, i1 and i2 perform automatic unpacking operation, and then perform numerical addition operation after unpacking into int. i3 is also used to compare whether the values are equal after unpacking. Therefore, i3 == i1+i2 is actually to compare whether the int values are equal, so it is true.

String constant pool:

String is a class modified by "final" and cannot be inherited. There are usually two ways to create objects.

//1,
String str = new String("abcd");
 
//2,
String str = "abcd";

The first kind of object created with new is stored in the heap. Each call creates a new object.

The second method is to create an object reference variable STR of String class on the stack, and then use symbolic reference to find out whether there is "abcd" in the String constant pool. If not, store "abcd" in the String constant pool, and point the str variable reference on the stack to "abcd" in the constant pool. If there is already an "abcd" in the constant pool, the "abcd" will not be created in the constant pool, but the str reference will be directly pointed to the "abcd" in the constant pool.

For the String class, the equals method is used to compare whether the String contents are the same== Number is used to compare whether the memory addresses are the same, that is, whether they point to the same object. Verify the above theory through code.

public static void main(String[] args){
       String str1 = "abcd";
       String str2 = "abcd";
       System.out.print(str1 == str2);//true
    }

First, store the variable reference str1 on the stack, and then use the symbolic reference to find out whether there is an abcd in the constant pool. If not, store the abcd in the constant pool, and then point str1 to the abcd of the constant pool. When the str2 object is created and the abcd is found in the constant pool, the str2 reference is directly pointed to the abcd. So str1 == str2, pointing to the same memory address.
 

public static void main(String[] args){
       String str1 = new String("abcd");
       String str2 = new String("abcd");
       System.out.print(str1 == str2);//false
    }

str1 and str2 use new to create objects, and create different objects on the heap respectively. The two references point to two different objects in the heap, so it is false.

About string + connection:

For the + connection of string constants, the JVM will optimize it to the value after the + connection during program compilation. Therefore, the value of its string constant is determined at compile time.

String a = "a1";   
String b = "a" + 1;   
System.out.println((a == b)); //result = true  
 
String a = "atrue";   
String b = "a" + "true";   
System.out.println((a == b)); //result = true 
 
String a = "a3.4";   
String b = "a" + 3.4;   
System.out.println((a == b)); //result = true 

About string reference + connection:

For the connection problem of + sign of string reference, since the string reference cannot be determined at the compilation time, a new address storage object is dynamically allocated and created at the run time of the program.

public static void main(String[] args){
       String str1 = "a";
	   String str2 = "ab";
	   String str3 = str1 + "b";
	   System.out.print(str2 == str3);//false
    }

For the above code, str3 is equal to str1 reference + string constant "b", which cannot be determined at compile time. The new address after connection is dynamically allocated and assigned to str3 at run time. Therefore, the memory addresses referenced by str2 and str3 are different, so the result of str2 == str3 is false

After the jad decompiler decompiles the code, the code is as follows

public class TestDemo
{
 
    public TestDemo()
    {
    }
 
    public static void main(String args[])
    {
        String s = "a";
        String s1 = "ab";
        String s2 = (new StringBuilder()).append(s).append("b").toString();
        System.out.print(s1 = s2);
    }
}

A StringBuilder object is found in {new, and then the + operator is optimized using the append method. New creates objects on the heap, while String s1 = "ab" creates objects in the constant pool. The memory addresses pointed to by the two applications are different, so s1 == s2 results in false.

Note: we already know that the + sign connection problem of string reference is actually to create a StringBuilder object during operation and use its append method to connect strings. This is also a problem we need to pay attention to in our development, that is, try not to use the + sign in the for loop to operate the string. Look at the following code:

public static void main(String[] args){
        String s = null;
        for(int i = 0; i < 100; i++){
            s = s + "a";
        }
    }

Use the + connection string in the for loop. Each time you loop, you will create a new StringBuilder object and "discard" it after append. If we create a StringBuilder object outside the loop and then append the string using the append method in the loop, we can save n-1 times of creating and destroying the object. Therefore, when connecting strings in a loop, you usually use StringBuilder or StringBuffer instead of + operation.

public static void main(String[] args){
        StringBuilder s = new StringBuilder();
        for(int i = 0; i < 100; i++){
            s.append("a");
        }
    }

String decorated with final

public static void main(String[] args){
        final String str1 = "a";
        String str2 = "ab";
        String str3 = str1 + "b";
        System.out.print(str2 == str3);//true
    }

The variable modified by final is a constant, and its value can be determined at compile time. So str1 + "b" is equal to "a" + "b", so the result is true.

The intern method of the String object.

public static void main(String[] args){
        String s = "ab";
        String s1 = "a";
        String s2 = "b";
        String s3 = s1 + s2;
        System.out.println(s3 == s);//false
        System.out.println(s3.intern() == s);//true
    }

From the previous study, we know that s1+s2 actually creates a new StringBuilder object on the heap, while s creates the object "ab" in the constant pool, so s3 == s is false. However, s3 calls the {intern method and returns the address value of the content (AB) of s3 in the constant pool. So s3 Intern() = = s, the result is true.

Article reference: https://blog.csdn.net/rongtaoup/article/details/89142396

 

Keywords: Java

Added by mhoard8110 on Sat, 19 Feb 2022 06:50:24 +0200