JVM constant pools are divided into Class file constant pools, runtime constant pools, global string constant pools, and primitive wrapper object constant pools.
1.Class file constant pool
A class file is a set of binary data streams in bytes. During the compilation of Java code, the binary data in the. Class file format is compiled and stored on disk, including the class file constant pool. There are constant pools (non-runtime constant pools) in class files, which are determined at compile time. The JVM specification has a strict specification for class file structures, and class files that conform to this specification must be loaded by the JVM. Let’s write a simple class just to illustrate
class JavaBean{
private int value = 1;
public String s = "abc";
public final static int f = 0x101;
public void setValue(int v){
final int temp = 3;
this.value = temp + v;
}
public int getValue() {returnvalue; }}Copy the code
After compiling the file using the javac command, run the javap -v command to view the compiled file
class JavaBasicKnowledge.JavaBean
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Methodref #6.#29 // java/lang/Object."
":()V
#2 = Fieldref #5.#30 // JavaBasicKnowledge/JavaBean.value:I
#3 = String #31 // abc
#4 = Fieldref #5.#32 // JavaBasicKnowledge/JavaBean.s:Ljava/lang/String;
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#6 = Class #34 // java/lang/Object
#7 = Utf8 value
#8 = Utf8 I
#9 = Utf8 s
#10 = Utf8 Ljava/lang/String;
#11 = Utf8 f
#12 = Utf8 ConstantValue
#13 = Integer 257
#14 = Utf8
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LJavaBasicKnowledge/JavaBean;
#21 = Utf8 setValue
#22 = Utf8 (I)V
#23 = Utf8 v
#24 = Utf8 temp
#25 = Utf8 getValue
#26 = Utf8 ()I
#27 = Utf8 SourceFile
#28 = Utf8 StringConstantPool.java
#29 = NameAndType #14:#15 // "
":()V
#30 = NameAndType #7:#8 // value:I
#31 = Utf8 abc
#32 = NameAndType #9:#10 // s:Ljava/lang/String;
#33 = Utf8 JavaBasicKnowledge/JavaBean
#34 = Utf8 java/lang/ObjectCopy the code
As you can see from this command we have the version number of the class file, the constant pool, and the compiled bytecode (not listed here). Since it is a constant pool, it must contain constants, so what is a “constant”? The class file constant pool contains two main constants: literals and symbolic references.
1) Literals: Literals are close to the constant concept at the Java language level, mainly including:
Public String s = “ABC “; The “ABC”
#9 = Utf8 s
#3 = String #31 // abc
#31 = Utf8 abcCopy the code
Member variables modified with final include static variables, instance variables, and local variables
#11 = Utf8 f
#12 = Utf8 ConstantValue
#13 = Integer 257Copy the code
One thing to note here is that the literals in the constant pool above refer to the data values, namely ABC and 0x101(257). From the observation of the constant pool above, we know that these two literals do exist in the constant pool.
For primitive data (or even local variables in methods), private int Value = 1; Only the field descriptor I and the field name value remain in the constant pool. Their literals do not exist in the constant pool.
2) Symbolic reference Symbolic reference mainly involves the concept of compilation principle, including the following three constants:
Fully qualified names of classes and interfaces, i.e. Java /lang/String; Thus, replacing the original “.” in the class name with a “/” is mainly used to parse a direct reference to the class at runtime, as above
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#33 = Utf8 JavaBasicKnowledge/JavaBeanCopy the code
The name and descriptor of a field, which is a variable declared in a class or interface, including class-level variables and instance-level variables
#4 = Fieldref #5.#32 // JavaBasicKnowledge/JavaBean.value:I
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#32 = NameAndType #7:#8 // value:I
#7 = Utf8 value
#8 = Utf8 I// These two are local variables, and the values retain the field names#23 = Utf8 v
#24 = Utf8 tempCopy the code
As you can see, for local variable names in methods, the class file’s constant pool only holds field names.
The name and descriptor in the methodThat is, the parameter type + return value
#21 = Utf8 setValue
#22 = Utf8 (I)V
#25 = Utf8 getValue
#26 = Utf8 ()ICopy the code
1. Runtime constant pool
The runtime constant pool is part of the method area and is therefore globally contributed. As we know, when a JVM executes a class, it must be loaded, linked (validated, prepared, parsed), initialized, and loaded in the first step:
-
Gets the binary byte stream of a class by its fully qualified name
-
Transform the static storage structure represented by this byte stream into the runtime data structure of the method area
-
Generate a Class object in memory that represents the loaded Class. This object is java.lang.Class, which acts as an entry point to the various data accesses of the Class in the method area.
Class objects are different from ordinary objects. Class objects are created at class load time, are created by the JVM, and are singletons that act as the entry point for the class to interact with the outside world, whereas ordinary objects are created after a call to new.
The static storage structure represented by the class byte stream is converted into the runtime data structure of the method area, which contains the process of entering the runtime constant pool of the class file. It is important to note that different classes share the same runtime constant pool. There is only one copy of the same string in the constant pool of multiple class files in the runtime constant pool. This is also an optimization.
The runtime constant pool is used to store symbolic information in the constant pool of Java Class files. The runtime constant pool holds symbolic references described in class files and translates these symbolic references into direct references (Pointers to instance objects, memory addresses) during the class parsing phase. Translated direct references are also stored in the runtime constant pool.
Runtime constant pools are dynamic compared to class constant pools. The Java specification does not require that constants be generated only at runtime. That is, the contents of the runtime constant pool do not come from the Class constant pool, but can be generated by code at runtime and put into the runtime constant pool. The most commonly used feature is string.intern ().
2. Global string constant pool
1) Two ways to create string objects in Java
Generally, there are two kinds:
String s0 = "hellow";
String s1 = new String("hellow");Copy the code
In the first way, the literal hellow is declared at compile time and goes directly into the class file constant pool. A reference to it is saved in the global string constant pool at run time. You actually end up creating a “Hellow” object on the heap, which we’ll talk about later. The second method uses new String(), which calls the constructor of the String class. We know that the new instruction creates an instance object of the class and completes the load initialization. Therefore, the String object is determined at runtime, and the created String object is in heap memory. System.out.println(s0 == s1); The return is definitely flase, so the == symbol compares the addresses of the elements on both sides, s1 and s0 both exist on the heap, but the addresses are definitely different.
Let’s look at some common questions:
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
System.out.println(s1 == s9); // falseCopy the code
String s1 = “Hello”, is there any object created in the heap?
This diagram shows the structure of the JVM runtime data area as we understand it, but there are still some imperfections.
In this diagram, you can see that the method area is actually contained in an area called the “non-heap” — roughly speaking, the non-heap contains the immortal generation, which in turn contains the method area and the string constant pool.
The Interned String is a globally shared String Pool, not a run-time constant Pool. But in our code we say String s1 = “Hello”; After this, during class loading, information from the class file is parsed into the method section of memory.
Most of the data in the constant pool in the class file is loaded into the “runtime constant pool”, including String literals; But at the same time a reference to the “Hello” string is stored in the “String constant pool”, also in the “non-heap” area, and the “Hello” ontology is created in the Java heap, as all objects are.
When the main thread starts to create s1, the virtual machine checks the String pool for equals(” Hello “) strings. If so, it copies a reference to s1 from the String pool for “Hello”. If no equal string is found, a new object is created in the heap, the reference is hosted in the string pool, and the reference is assigned to STR.
When strings are created using literal assignment, no matter how many times they are created, as long as they have the same value, they all refer to the same object in the heap.
2) The nature of the string constant pool
The string constant pool is a reference table to a string instance maintained by the JVM, which in HotSpot VM is a global table called StringTable. A reference to a string instance is maintained in the string constant pool, and the underlying C++ implementation is a Hashtable. The string instances to which these maintained references refer are referred to as “hosted strings” or “interned Strings” or commonly referred to as “strings entered into the string constant pool.”
Again, the runtime constant pool is in the non-heap, whereas after JDK1.7, the string constant pool was moved to the heap, so the two are not the same concept at all.
When does a String” literal “enter the String constant pool?
Conclusion: When executing the LDC directive, it means that int, float, or String constants are pushed from the constant pool to the top of the stack.
There are two types of constant pool entries for Class files in the JVM specification:
-
CONSTANT_Utf8_info
-
CONSTANT_String_info
In HotSpot VM, in the runtime constant pool, CONSTANT_Utf8_info can represent Class file methods, fields, and so on, with the following structure:
The first is a 1-byte tag, indicating that this is a constant of the CONSTANT_Utf8_info structure, followed by a two-byte length, indicating the length of the bytes to be stored, followed by a one-byte array, indicating the actual length of the string to be stored. It is important to note that a byte simply means there is an array of type byte, which of course can be much longer than a byte. Of course, since the CONSTANT_Utf8_info structure can only represent length in u2, which is two bytes, the maximum length is 2 bytes, which is 65535.
CONSTANT_String_info is a String constant, but it does not hold the contents of a String constant directly. Instead, it holds only an index. The other constant pool entry specified by this index must be a constant of type CONSTANT_Utf8. This is where you actually hold the contents of the string
CONSTANT_Utf8 is created entirely during class loading, while CONSTANT_String is lazy resolve, which is resolved when the LDC directive that first references this item is first executed. Before resolve, HotSpot VM calls its type JVM_CONSTANT_UnresolvedString and the content is just an index as in the Class file; After resolve, this item’s constant type will become the final JVM_CONSTANT_String.
That is, in the case of HotSpot VM, when the class is loaded, those string literals go into the run-time constant pool of the current class, not into the global string constant pool (i.e., there is no reference in StringTable and no object generated in the heap), and when LDC is executed, Trigger lazy Resolution.
The execution semantics of LDC bytecode here are: Find the index item in the runtime constant pool of the current class (ConstantPool + ConstantPoolCache) and resolve it if it is not already resolved. And returns the content after Resolve.
When it encounters a String constant, the resolve procedure returns a java.lang.String reference if it finds that StringTable already has a reference to a java.lang.String with a matching content, whereas if StringTable does not already have a reference to a matching String instance, It creates a String object in the Java heap, records the reference in StringTable, and returns the reference.
Whether the LDC directive creates a new String instance depends on whether the StringTable has already recorded a reference to the corresponding String when the LDC directive is first executed.
3) String. Intern (
String. Intern ()
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
If it exists, it returns a reference. If it doesn’t exist, it stores a “reference” to that object in a Stringtable.
public class RuntimeConstantPoolOOM{
public static void main(String[] args) {
String str1 = new StringBuilder("Computer").append("Software").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern() == str2); }}Copy the code
The above code will result in false and false under JDK6 and true and false above JDK7.
First we call StringBuilder to create a “computer software “String, which was created at run time because the new keyword was called, and was not previously available in the JVM.
Under JDK6, intern() copies the first encountered string instance into the persistent generation and returns a reference to the string instance in the persistent generation. In JDK1.7, instead of copying String instances, intern() of String will first try to find references to the object in the constant pool, and if found, return the reference address of the object in the constant pool.
So in 1.7, only one instance of the string “computer software” exists, in the Java heap! String str1 = new StringBuilder(” computer “).appEnd (” software “).toString(); Str1.intern () == str1; str1.intern() == str1; str1.intern() == str1; For str2 references, since “Java” already exists in the JVM, new StringBuilder(“ja”).append(“va”).toString() recreates a new “Java” string object, Intern () returns an instance reference to the constant encountered for the first time, so it returns a reference to the “Java” string object in the system (for the first time), so it returns false.
In JDK6, str1 and str2 point to newly created objects that will be created in the Java Heap, so str1 and str2 point to memory addresses in the Java Heap. If str1/str2.intern() points to the address in the constant pool, it is isolated from the heap. If str1/str2.intern() points to the address in the constant pool, it is isolated from the heap. So of course s1.Intern () and s1 have different addresses.
Public class Test2 {public static void main(String[] args) {public static void main(String[] args) { -XX:PermSize=10M -XX:MaxPremSize=10M */ List<String> list = new ArrayList<String>(); Int I = 0; int I = 0; int I = 0;while (trueList.add (string.valueof (i++).intern()); list.add(string.valueof (i++).intern()); }}}Copy the code
The above code will run out of Perm memory in JDK6, JDK7 or high.
The persistent generation exists in the JDK6 constant pool. If the persistent generation size is set, the continuous while loop will overflow the Perm. The JDK7 constant pool is moved to the Native Heap(Java Heap,HotSpot VM does not distinguish between Native and Java heaps), so even if the persistent generation size is set, there is no impact on the constant pool; In the current code, all the strings of ints do not add up to fill the Heap area, so there is no exception.
Encapsulating classes and corresponding constant pools for JAVA primitive types
Most of basic types in the Java wrapper class implements the constant pool technology, these classes are Byte, Short, Integer, Long, Character, Boolean, the other two floating-point types of wrapper classes are not implemented. In addition, the wrapper class of the above five integers can only use the object pool if the corresponding value is less than or equal to 127, that is, the object is not responsible for creating and managing objects of these classes greater than 127.
Public class StringConstantPool {public static void main (String [] args) {/ / 5 kinds of plastic packaging type Byte, Short, Integer, Long, Character of objects, // Use constant pool Integer i1=127 when the value is less than 127; Integer i2=127; System.out.println(i1==i2); / / outputtrueInteger i3=128; Integer i4=128; System.out.println(i3==i4); / / outputfalseThe Boolean class also implements constant pool technology Boolean BOOL1 =true;
Boolean bool2=true; System.out.println(bool1==bool2); / / outputtrue// The floating-point wrapper class does not implement constant pool technology Double d1=1.0; Double d2 = 1.0; System.out.println(d1==d2); / / outputfalse}}Copy the code
Prior to JDK5.0, it was not allowed to assign a primitive data type directly to its corresponding wrapper class, e.g. Integer I = 5; This is supported in JDK5.0, however, because the compiler automatically converts the above code to the following code: Integer I = integer.valueof (5); This is Java boxing. JDK5.0 also provides automatic unpacking: Integer I =5; int j = i;