This article focuses on three types of constant pools: Class constant pools, string constant pools, and primitive type constant pools.

The default JDK version is JDK 1.8

A Class constant pool

The Class constant pool can be understood as a repository of resources in a Class file. In addition to the description information of the Class version, fields, methods, interfaces, etc., the Class constant pool can be understood as a repository of resources in a Class file. There is also the constant Pool table, which is used to store various literals and Symbolic References generated at compile time.

We typically generate readable JVM bytecode instruction files using javap commands:

Classfile /Users/zhengsh/cqvie.edu.cn/ssm-salt/ssm-salt-jvm/src/main/java/cn/edu/cqvie/jvm/Math.class Last modified 4 February 2021; size 499 bytes MD5 checksum 037cef02562301117a47bc10b250c770 Compiled from "Math.java" public class cn.edu.cqvie.jvm.Math minor version: 0 major version: 52 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #2 // cn/edu/cqvie/jvm/Math super_class: #7 // java/lang/Object interfaces: 0, fields: 0, methods: 3, attributes: 1 Constant pool:   #1 = Methodref          #7.#18 // java/lang/Object."
       
        ":()V
       
   #2 = Class              #19 // cn/edu/cqvie/jvm/Math
   #3 = Methodref          #2.#18 // cn/edu/cqvie/jvm/Math."
       
        ":()V
       
   #4 = Fieldref           #20.#21 // java/lang/System.out:Ljava/io/PrintStream;
   #5 = Methodref          #2.#22 // cn/edu/cqvie/jvm/Math.compute:()I
   #6 = Methodref          #23.#24 // java/io/PrintStream.println:(I)V
   #7 = Class              #25 // java/lang/Object
   #8 = Utf8 
      
   #9 = Utf8 ()V
  #10 = Utf8 Code
  #11 = Utf8 LineNumberTable
  #12 = Utf8 compute
  #13 = Utf8 ()I
  #14 = Utf8 main
  #15 = Utf8 ([Ljava/lang/String;)V
  #16 = Utf8 SourceFile
  #17 = Utf8 Math.java
  #18 = NameAndType        #8:#9 // "
       
        ":()V
       
  #19 = Utf8 cn/edu/cqvie/jvm/Math
  #20 = Class              #26 // java/lang/System
  #21 = NameAndType        #27:#28 // out:Ljava/io/PrintStream;
  #22 = NameAndType        #12:#13 // compute:()I
  #23 = Class              #29 // java/io/PrintStream
  #24 = NameAndType        #30:#31 // println:(I)V
  #25 = Utf8 java/lang/Object
  #26 = Utf8 java/lang/System
  #27 = Utf8 out
  #28 = Utf8 Ljava/io/PrintStream;
  #29 = Utf8 java/io/PrintStream
  #30 = Utf8 println
  #31 = Utf8 (I)V
Copy the code

Constant pool: The following content is the Constant pool, which mainly stores literal and symbolic references.

literal

A literal is a string or numeric constant composed of letters, numbers, etc.

Literals can only appear as rvalues, which are values to the right of the equal sign, such as int a = 1, where a is an lvalue and 1 is an rvalue. In this case, 1 is a literal.

int a = 1;
int b = 2;
String str = "Hello !";
Copy the code

Symbolic reference

Symbolic references are a concept in compilation principles, as opposed to direct references. It mainly includes the following three types of constants

  • Fully qualified names of classes and interfaces
  • Field name and descriptor
  • Method name and descriptor

For a above, b is a field noun, which is a symbolic reference, and for Ljava/ IO /PrintStream, which is a class permission name, main and compute, which are method names, these are symbolic references. The constant pool are static information now, after only to the runtime is loaded into memory, these symbols have the corresponding memory address information, the constant pool once loaded into memory becomes a runtime constant pool, the corresponding symbols referenced in the program will be loaded or runtime code to be loaded memory of direct reference, also is the dynamic connection we said, For example, the symbolic reference compute() is converted at run time to the in-memory address of the code in the compute() method, mainly through the type pointer in the object header.

String constant pool

The design idea of string constant pool

  1. String allocation, and other object allocation, expensive time and space costs, as the most basic data type, a large number of frequent common strings, greatly affect the performance of the program.
  2. The JVM makes some optimizations when instantiating string constants to improve performance and reduce overhead
    • Creates a string constant pool for strings, similar to a buffer
    • When you create a string constant, you first query the string constant pool to see if it exists
    • If the string exists, return the reference instance, if it does not, instantiate the string and place it in the constant pool

Three string operations (Jdk1.7 and above)

  • Direct assignment operation
String a = "abc"; // point to constant
Copy the code

String objects created this way will only be in the constant pool.

Because of the literal “ABC”, the JVM first checks the equals(key) method to determine whether there are identical objects in the constant pool. If so, the JVM returns a reference to that object in the constant pool.

If not, a new object is created in the constant pool and the reference is returned.

  • New Create object
String s1 = new String("abc"); // s1 is a reference to an in-memory object
Copy the code

This ensures that the object is present in both the string constant pool and the heap, creates none, and returns a reference to the object in the heap.

The steps are as follows:

Since there is a literal “ABC”, we first check to see if the string “ABC” exists in the string constant pool.

If no, create a string object in the string constant pool. Create a string object “ABC” in memory.

If one exists, go directly to the heap and create a string object “ABC”.

Finally, the in-memory reference is returned.

  • Intern method
String s1 = new String("abc");
String s2 = s1.intern();

System.out.println(s1 == s2) //false
Copy the code

Intern is a native method that returns strings in the String constant pool if they already contain a String equal to the String (determined by equals(object)). Otherwise, the reference returned by intern points to the current string s1(jdk1.6 requires s1 to be copied into the string constant pool).

The location of the string constant pool

In jdk1.6 and earlier, there are persistent generations, run-time constant pools, which contain a string constant pool

Jdk1.7: There are persistent generations but it has been progressively “de-perpetuated”, separating the constant pool of strings from the run-time constant pool in the persistent generation into the heap

Jdk1.8 and beyond: No persistent generation, runtime constant pool in meta-space, string constant pool still in heap

Case study:

/** * VM args: -Xms10M -Xmx10M */
public class RuntimeConstantPoolOOM {

    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            for (int j = 0; j < 10000000; j++) {
                list.add(String.valueOf(i + j / 1000000).intern()); }}}}Copy the code

Running results:

Jdk1.7 and above the Exception in the thread “is the main” Java. Lang. OutOfMemoryError: Java heap space

Jdk1.6 Exception in the thread “is the main” Java. Lang. OutOfMemoryError: PermGen span

String constant pool design principle

The string constant pool bottom layer is implemented by hotspot in C++. The bottom layer is like a HashTable and holds essentially references to string objects.

For example:

String s1 = new String("he") + new String("llo");
String s2 = s1.intern();

System.out.println(s1 == s2); 

Under JDK 1.6 the output is false and 6 objects are created;
In JDK 1.7 and above the output is true and 5 objects are created
Copy the code

Why these changes in output? The string pool is removed from the permanent generation and moved to the heap. Intern () also changes:

  1. In JDK 1.6, the call to intern() first looks for strings equal to () in the string pool and returns a reference to that string in the string pool if it exists. If the string does not exist, the virtual machine recreates an instance of the persistent generation and points one entry of StringTable to the newly created instance.

  1. In JDK 1.7 (and above), since the string pool is no longer a permanent generation, intern() made some changes to make it easier to leverage objects in the heap. Strings exist as in JDK 1.6, but you no longer need to recreate the instance if the string does not exist and can point directly to the instance on the heap.

Constant pool case

A few examples of constant pools

Case 1:

String s0 = "abc";
String s1 = "abc";
String s2 = "a" + "bc"; 

boolean b1 = s0 == s1; // true
boolean b2 = s1 == s2; // true
Copy the code

These two are ==, because at compile time, the program will optimize for us once, and the + will optimize away.

Case 2:

String s2="sh" + new String("en");
Copy the code

This can’t be done in the compiler because of the presence of new, so it automatically adds a new String to “sh” at runtime.

String a = "ab";
String b = "b";
String c = "a" + b;

boolean b1 = a == c; // false
Copy the code

Such a! =b, in the string “+” concatenation, there is a string reference, and the value of the reference is not determined at compile time. But the bottom one is theta = theta

** Case 3: **

String a = "ab";
final String b = "b";
String c = "a" + b;
Copy the code

For a final modified variable, a local copy of its constant value is resolved at compile time and stored in its own constant pool or embedded in its bytecode stream.

Case 4:

String a = "ab";
final String b = getB();
String c = "a" + b;

public static String getB(a){
    return "b";
} 
Copy the code

The JVM can’t determine the value of a string reference b at compile time. The result of the above program is false because the return value of the method is dynamically concatenated with “A” after the program is called at run time.

The “+” operation of C becomes the following operation:

StringBuilder temp = new StringBuilder();
temp.append(a).append(b).append(c);
String s = temp.toString();
Copy the code

A couple of special cases

String str2 = new StringBuilder("Computer").append("Technology").toString();
// Because there is no "computer technology" literal, there is only "computer" and "technology", so "computer technology" is not generated in the constant pool.
System.out.println(str2 == str2.intern()); // true

String str1=new StringBuilder("ja").append("va").toString(); 
// This is because Java is a keyword and must already be in the string constant pool in the related classes initialized by the JVM.
System.out.println(str1 == str1.intern()); // false
 
String s1=new String("test");
System.out.println(s1==s1.intern()); // true
 
String s2=new StringBuilder("abc").toString();
// The toString method of StringBuilder will call new String(), and "ABC" as a literal will be put into the constant pool
System.out.println(s2==s2.intern()); // false
Copy the code

Basic type constant pool

Most of the wrapper classes for primitive types in Java implement constant pooling (technically called object pooling, on the heap). These classes are Byte, Short, Integer, Long, Character, and Boolean. The other two floating point wrapper classes are not implemented. In addition, the wrapper classes of Byte, Short, Integer, Long, and Character can use the object pool only when the corresponding value is less than or equal to 127. That is, objects are not responsible for creating and managing objects of these classes greater than 127. Because in general, this smaller number is used more often.

/ / 5 kinds of plastic packaging type Byte, Short, Integer, Long, Character of the object,
Object pools can be used when the value is less than 127
Integer i1 = 127; // The underlying call is integer.valueof (127), which uses the pool of IntegerCache objects
Integer i2 = 127;
System.out.println(i1 == i2); / / output true

If the value is greater than 127, objects are not fetched from the object pool
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); / / output is false

// New objects with the new keyword do not use object pools
Integer i5 = new Integer(127);
Integer i6 = new Integer(127);
System.out.println(i5 == i6); / / output is false

// The Boolean class also implements object pooling technology
Boolean bool1 = true;
Boolean bool2 = true;
System.out.println(bool1 == bool2); / / output true

// Floating point wrapper classes do not implement object pooling technology
Double d1 = 1.0;
Double d2 = 1.0;
Copy the code

Integer.java part of the source code

/ / the 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);
}

/ / IntegerCache implementation
private static class IntegerCache {
  static final int low = -128;
  static final int high;
  static final Integer cache[];

  // static block precache data from -128 to 127, same with Long
  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];
    int j = low;
    for(int k = 0; k < cache.length; k++)
      cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
  }

  private IntegerCache(a) {}}Copy the code

The resources

  1. Oracle Jdk documentation
  2. Deep Understanding of the Java Virtual Machine, 3rd edition, zhiming Zhou
  3. Java Virtual Machine specification (Java SE 8 edition), translated by Love Fly, Zhou Zhiming et al
  4. The JVM dives into three constant pool CSDN runs up