Java knowledge learning - method area (2)

runtime constant pool

A binary bytecode file includes three parts: basic information of the class, constant pool, class method definition (including virtual machine instructions). If you want to view the binary bytecode information of a class, you can use Javap -v bytecode file to view the corresponding information. The following is the binary bytecode information corresponding to a simple Helloword Java Demo.

Classfile /F:/JavaProject/day_12/src/_01Calender/SayHello.class
  Last modified 2020-2-23; size 421 bytes
  MD5 checksum e5bd88fa5c10f13df7396dc70d7759f7
  Compiled from "SayHello.java"
public class SayHello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // Hello world
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // SayHello
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               SayHello.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               Hello world
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               SayHello
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public SayHello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
}
SourceFile: "SayHello.java"

At the end of the whole analysis, there is a description about the main method, including parameters, access types, and final virtual machine instructions. First look at the first line "getstatic #2" of the virtual machine instruction. The interpreter actually only recognizes this content when interpreting the binary bytecode. #2 means to go to #2 in the constant pool Costant pool to find , corresponds to a FieldRef, which is a variable reference, and corresponds to the information of #16 and #17, and then determines through the content behind #16 and #17 to finally get the variable PrintStrem. Then execute the second line of virtual machine instructions - "ldc #3". Find #3 in the Constant Pool and see that it is a string type of data corresponding to the content of #18. Find line #18 and find that it is the string "Hello World" .

The so-called constant pool is a table. The virtual machine instructions use this table to find the class name, method name, parameter type, and literal value to be executed according to this constant table (like the above Hello world string, integer, Boolean type) information.

Runtime constant pool, the constant pool is a *.class file, when the class is loaded, its constant pool information will be loaded into the runtime constant pool, and the symbol address inside will be changed to a real address.

 

Constant pool and StringTable (string pool)

First look at the binary file for a simple string.

The binary file obtained through Javap is:

The content in the constant pool will be loaded into the runtime constant pool at runtime. At this time, a, b, and ab are all symbols of the constant pool, and they are not Java string objects. At this time, they are like #2, #3, #4 This notation.

When "ldc #2" is executed, it will turn the a symbol into an "a" string object. After it becomes the "a" string object, it will prepare a space. This space is called StringTable, that is, String pool, at first it was empty with nothing in it. After the "a" string object is ready, "a" will be used as the key, and the StringTable will be used to find out whether there is a Key with the same value (the data structure of the StringTable is actually a hash table, and the length is fixed and cannot be expanded. ), if it is not found, it will put the newly generated "a" string object into the string pool, and use the object in the string pool directly if it exists. In short, only one string object in the string pool can exist.

We often come across the following piece of code:

Although the contents of s4 and s5 are the same, they are both "ab", but the specific positions are different. It can be understood through Javap decompilation, and the decompilation result is as follows:

         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: ldc           #4                  // String ab
        31: astore        5
        33: return

In the binary file above, from the 9th to the 27th are the contents of the binary file of String s4=s1+s2. By analyzing this sentence, we first find the StringBuilder class, create a StringBuilder object, and call the Append object twice. Then call ToString() of StringBuilder. By looking at the source code, we find that the ToString() method in StringBuilder is actually new String (value, 0, count), which creates an object. We learned earlier that all objects created through the new keyword are It is placed in the heap, so this line actually does a lot of things (new StringBuilder().append("a").append("b").toString() => new String("ab") ). From 29 to 31 is actually String s5="a"+"b", which is actually the optimization of JavaC during compilation. As a result, it has been determined to be "ab" during compilation, and they are all constants. During compilation, they all know that they are constants "ab" ", and the above s1 and s2 are variables, which cannot be determined during compilation. Therefore, although the values ​​of the two rows are the same, the final sources are different.

Guess you like

Origin blog.csdn.net/qq_35363507/article/details/104469987