JVM-Memory Structure-Method Area

 

Note: Although the method area is discussed in this article, knowledge about Class file structure and Class loading is also involved in order to answer some questions.
java7 and before, method area implementations were permanent, and after java8, method area implementations were Metaspace

5.1 Definition

  • A method area is a conceptual area defined in the java virtual machine specification that can be implemented differently by different vendors.
  • Method areas share many common features with heaps: thread sharing, memory discontinuity, scalability, garbage collection, and throwing an OutOfMemoryError exception when it can no longer be extended.

5.2 Composition

5.3 Method Area Memory Overflow

java7 and earlier, using -XX:MaxPermSize to limit the maximum memory of the permanent generation, throws a java.lang.OutOfMemoryError:PermGen space exception when a large number of classes are loaded, and triggers FullGC when the permanent generation is out of memory

After java8, use -XX:MaxMetaspaceSize to limit the maximum memory of metaspace, throw a java.lang.OutOfMemoryError:Metaspace exception when a large number of classes are loaded, and trigger FullGC when Metaspace memory is low

Sample code (with jdk1.8, increase limit -XX:MaxMetaspaceSize=100m)

public class MetaspaceDemo extends ClassLoader{

    public static void main(String[] args) {
        //Class Holdings
        List<Class<?>> classes = new ArrayList<Class<?>>();
        //Loop 1000w to generate 1000w different classes.
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            //Define a class named Class{i} with a public access domain and a java.lang.Object parent without implementing any interfaces
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
                    "java/lang/Object", null);
            //Define constructor <init>method
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                    "()V", null, null);
            //The first instruction is to load this
            mw.visitVarInsn(Opcodes.ALOAD, 0);
            //The second instruction is to call the constructor of the parent class Object
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
                    "<init>", "()V", false);
            //The third directive is return
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();

            MetaspaceDemo test = new MetaspaceDemo();
            byte[] code = cw.toByteArray();
            //Define Classes
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
            classes.add(exampleClass);
        }
    }

}

output

Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at com.loksail.learndemo.MetaspaceDemo.main(Metaspace.java:26)

This is inconsistent with our expected Metaspace exception because class pointer compression is turned on by default in jdk8, that is, -XX:+useCompressedClassPointers, which compresses the _klass pointer in the object header and opens a space in the Metaspace (Compressed Class Space) to store meta-information and virtual method tables of classes, etc. The default size is 1G, which can be passed through -XX:CompressedClassSpaceSize=100m adjustment, here we choose to turn off this function and add -XX:-useCompressedClassPointers to the operation parameters

Run Results

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at com.loksail.learndemo.memory.MetaspaceDemo.main(MetaspaceDemo.java:43)

5.4 Runtime Constant Pool

By decompiling the class binary byte code file with javap, we can see that the components are

public class Demo {

    private final static int count = 10;

    public static void main(String[] args) {
        String test = "Hell World";
        int days = 20;
        System.out.println(days + test + count);
    }

}
  • Basic information about classes

    Classfile /D:/JWF/Gitee/learn-demo/src/main/java/com/loksail/learndemo/Demo.class
    Last modified 2019-12-20; size 689 bytes
    MD5 checksum e4cd8355bc1aa4e1e9d2b2c65a5ede87
    Compiled from "Demo.java"
    public class com.loksail.learndemo.Demo
    minor version: 0
    major version: 52
    flags: ACC_PUBLIC, ACC_SUPER
    
  • class constant pool (The Constant Pool)

    Constant pool:
     #1 = Methodref          #11.#24        // java/lang/Object."<init>":()V
     #2 = String             #25            // Hell World
     #3 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
     #4 = Class              #28            // java/lang/StringBuilder
     #5 = Methodref          #4.#24         // java/lang/StringBuilder."<init>":()V
     #6 = Methodref          #4.#29         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     #7 = Methodref          #4.#30         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     #8 = Class              #31            // com/loksail/learndemo/Demo
     #9 = Methodref          #4.#32         // java/lang/StringBuilder.toString:()Ljava/lang/String;
    #10 = Methodref          #33.#34        // java/io/PrintStream.println:(Ljava/lang/String;)V
    #11 = Class              #35            // java/lang/Object
    #12 = Utf8               count
    #13 = Utf8               I
    #14 = Utf8               ConstantValue
    #15 = Integer            10
    #16 = Utf8               <init>
    #17 = Utf8               ()V
    #18 = Utf8               Code
    #19 = Utf8               LineNumberTable
    #20 = Utf8               main
    #21 = Utf8               ([Ljava/lang/String;)V
    #22 = Utf8               SourceFile
    #23 = Utf8               Demo.java
    #24 = NameAndType        #16:#17        // "<init>":()V
    #25 = Utf8               Hell World
    #26 = Class              #36            // java/lang/System
    #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
    #28 = Utf8               java/lang/StringBuilder
    #29 = NameAndType        #39:#40        // append:(I)Ljava/lang/StringBuilder;
    #30 = NameAndType        #39:#41        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    #31 = Utf8               com/loksail/learndemo/Demo
    #32 = NameAndType        #42:#43        // toString:()Ljava/lang/String;
    #33 = Class              #44            // java/io/PrintStream
    #34 = NameAndType        #45:#46        // println:(Ljava/lang/String;)V
    #35 = Utf8               java/lang/Object
    #36 = Utf8               java/lang/System
    #37 = Utf8               out
    #38 = Utf8               Ljava/io/PrintStream;
    #39 = Utf8               append
    #40 = Utf8               (I)Ljava/lang/StringBuilder;
    #41 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
    #42 = Utf8               toString
    #43 = Utf8               ()Ljava/lang/String;
    #44 = Utf8               java/io/PrintStream
    #45 = Utf8               println
    #46 = Utf8               (Ljava/lang/String;)V
    
  • Class method definition (including jvm directives)

    {
    public com.loksail.learndemo.Demo();
      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 6: 0
    
    public static void main(java.lang.String[]);
      descriptor: ([Ljava/lang/String;)V
      flags: ACC_PUBLIC, ACC_STATIC
      Code:
        stack=3, locals=3, args_size=1
           0: ldc           #2                  // String Hell World
           2: astore_1
           3: bipush        20
           5: istore_2
           6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
           9: new           #4                  // class java/lang/StringBuilder
          12: dup
          13: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
          16: iload_2
          17: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          20: aload_1
          21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          24: bipush        10
          26: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          32: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          35: return
        LineNumberTable:
          line 11: 0
          line 12: 3
          line 13: 6
          line 14: 35
    }
    

5.4.1 Class File Constant Pool

Class file constant pool refers to the class byte code file generated by compilation. One of its structures is Constant Pool, which stores various literal and symbol references generated during compilation. This part of the structure is stored in the run-time constant pool that enters the method area after the class is loaded.

5.4.1.1 Literal quantities and symbol references

case

public class Demo01 {

    private int field1;
    private Instan field2;

    private float m1 = 10.1f;
    private double m2 = 10.2;
    private long m3 = 103;
    private int a = 10;
    private static int b = 20;
    private static final long c = 10000L;
    private static final double d = 5.5;
    private static final int e = 5;
    private static final short f = 6;
    private static final boolean g = false;
    private static final boolean h = true;
    private static final char i = 'k';
    private static final byte j = 7;
    private static final float k = 6.6f;
    private static final Instan instan = new Instan();
    private String str = "Hello World";

    public void method1(String m1) {
        method3();
    }

    public static int method2(int m2) {
        method4();
        return 0;
    }

    public String method3() {
        return null;
    }

    public static final Long method4() {
        return null;
    }


    public static void main(String[] args) throws IOException {
        String string = "Hello Method";
        System.in.read();
    }
}

class Instan{

}

Byte code file

Classfile /D:/JWF/Gitee/learn-demo/src/main/java/com/loksail/learndemo/demo/Demo01.class
  Last modified 2019-12-26; size 1531 bytes
  MD5 checksum 06e8b620afef54675919d0162c48ec90
  Compiled from "Demo01.java"
public class com.loksail.learndemo.demo.Demo01
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
    #1 = Methodref          #23.#83       // java/lang/Object."<init>":()V
    #2 = Float              10.1f
    #3 = Fieldref           #22.#84       // com/loksail/learndemo/demo/Demo01.m1:F
    #4 = Double             10.2d
    #6 = Fieldref           #22.#85       // com/loksail/learndemo/demo/Demo01.m2:D
    #7 = Long               103l
    #9 = Fieldref           #22.#86       // com/loksail/learndemo/demo/Demo01.m3:J
   #10 = Fieldref           #22.#87       // com/loksail/learndemo/demo/Demo01.a:I
   #11 = String             #88           // Hello World
   #12 = Fieldref           #22.#89       // com/loksail/learndemo/demo/Demo01.str:Ljava/lang/String;
   #13 = Methodref          #22.#90       // com/loksail/learndemo/demo/Demo01.method3:()Ljava/lang/String;
   #14 = Methodref          #22.#91       // com/loksail/learndemo/demo/Demo01.method4:()Ljava/lang/Long;
   #15 = String             #92           // Hello Method
   #16 = Fieldref           #93.#94       // java/lang/System.in:Ljava/io/InputStream;
   #17 = Methodref          #95.#96       // java/io/InputStream.read:()I
   #18 = Fieldref           #22.#97       // com/loksail/learndemo/demo/Demo01.b:I
   #19 = Class              #98           // com/loksail/learndemo/demo/Instan
   #20 = Methodref          #19.#83       // com/loksail/learndemo/demo/Instan."<init>":()V
   #21 = Fieldref           #22.#99       // com/loksail/learndemo/demo/Demo01.instan:Lcom/loksail/learndemo/demo/Instan;
   #22 = Class              #100          // com/loksail/learndemo/demo/Demo01
   #23 = Class              #101          // java/lang/Object
   #24 = Utf8               field1
   #25 = Utf8               I
   #26 = Utf8               field2
   #27 = Utf8               Lcom/loksail/learndemo/demo/Instan;
   #28 = Utf8               m1
   #29 = Utf8               F
   #30 = Utf8               m2
   #31 = Utf8               D
   #32 = Utf8               m3
   #33 = Utf8               J
   #34 = Utf8               a
   #35 = Utf8               b
   #36 = Utf8               c
   #37 = Utf8               ConstantValue
   #38 = Long               10000l
   #40 = Utf8               d
   #41 = Double             5.5d
   #43 = Utf8               e
   #44 = Integer            5
   #45 = Utf8               f
   #46 = Utf8               S
   #47 = Integer            6
   #48 = Utf8               g
   #49 = Utf8               Z
   #50 = Integer            0
   #51 = Utf8               h
   #52 = Integer            1
   #53 = Utf8               i
   #54 = Utf8               C
   #55 = Integer            107
   #56 = Utf8               j
   #57 = Utf8               B
   #58 = Integer            7
   #59 = Utf8               k
   #60 = Float              6.6f
   #61 = Utf8               instan
   #62 = Utf8               str
   #63 = Utf8               Ljava/lang/String;
   #64 = Utf8               <init>
   #65 = Utf8               ()V
   #66 = Utf8               Code
   #67 = Utf8               LineNumberTable
   #68 = Utf8               method1
   #69 = Utf8               (Ljava/lang/String;)V
   #70 = Utf8               method2
   #71 = Utf8               (I)I
   #72 = Utf8               method3
   #73 = Utf8               ()Ljava/lang/String;
   #74 = Utf8               method4
   #75 = Utf8               ()Ljava/lang/Long;
   #76 = Utf8               main
   #77 = Utf8               ([Ljava/lang/String;)V
   #78 = Utf8               Exceptions
   #79 = Class              #102          // java/io/IOException
   #80 = Utf8               <clinit>
   #81 = Utf8               SourceFile
   #82 = Utf8               Demo01.java
   #83 = NameAndType        #64:#65       // "<init>":()V
   #84 = NameAndType        #28:#29       // m1:F
   #85 = NameAndType        #30:#31       // m2:D
   #86 = NameAndType        #32:#33       // m3:J
   #87 = NameAndType        #34:#25       // a:I
   #88 = Utf8               Hello World
   #89 = NameAndType        #62:#63       // str:Ljava/lang/String;
   #90 = NameAndType        #72:#73       // method3:()Ljava/lang/String;
   #91 = NameAndType        #74:#75       // method4:()Ljava/lang/Long;
   #92 = Utf8               Hello Method
   #93 = Class              #103          // java/lang/System
   #94 = NameAndType        #104:#105     // in:Ljava/io/InputStream;
   #95 = Class              #106          // java/io/InputStream
   #96 = NameAndType        #107:#108     // read:()I
   #97 = NameAndType        #35:#25       // b:I
   #98 = Utf8               com/loksail/learndemo/demo/Instan
   #99 = NameAndType        #61:#27       // instan:Lcom/loksail/learndemo/demo/Instan;
  #100 = Utf8               com/loksail/learndemo/demo/Demo01
  #101 = Utf8               java/lang/Object
  #102 = Utf8               java/io/IOException
  #103 = Utf8               java/lang/System
  #104 = Utf8               in
  #105 = Utf8               Ljava/io/InputStream;
  #106 = Utf8               java/io/InputStream
  #107 = Utf8               read
  #108 = Utf8               ()I
{
  public com.loksail.learndemo.demo.Demo01();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // float 10.1f
         7: putfield      #3                  // Field m1:F
        10: aload_0
        11: ldc2_w        #4                  // double 10.2d
        14: putfield      #6                  // Field m2:D
        17: aload_0
        18: ldc2_w        #7                  // long 103l
        21: putfield      #9                  // Field m3:J
        24: aload_0
        25: bipush        10
        27: putfield      #10                 // Field a:I
        30: aload_0
        31: ldc           #11                 // String Hello World
        33: putfield      #12                 // Field str:Ljava/lang/String;
        36: return
      LineNumberTable:
        line 12: 0
        line 17: 4
        line 18: 10
        line 19: 17
        line 20: 24
        line 32: 30

  public void method1(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokevirtual #13                 // Method method3:()Ljava/lang/String;
         4: pop
         5: return
      LineNumberTable:
        line 35: 0
        line 36: 5

  public static int method2(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: invokestatic  #14                 // Method method4:()Ljava/lang/Long;
         3: pop
         4: iconst_0
         5: ireturn
      LineNumberTable:
        line 39: 0
        line 40: 4

  public java.lang.String method3();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aconst_null
         1: areturn
      LineNumberTable:
        line 44: 0

  public static final java.lang.Long method4();
    descriptor: ()Ljava/lang/Long;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    Code:
      stack=1, locals=0, args_size=0
         0: aconst_null
         1: areturn
      LineNumberTable:
        line 48: 0

  public static void main(java.lang.String[]) throws java.io.IOException;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #15                 // String Hello Method
         2: astore_1
         3: getstatic     #16                 // Field java/lang/System.in:Ljava/io/InputStream;
         6: invokevirtual #17                 // Method java/io/InputStream.read:()I
         9: pop
        10: return
      LineNumberTable:
        line 53: 0
        line 54: 3
        line 55: 10
    Exceptions:
      throws java.io.IOException

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: bipush        20
         2: putstatic     #18                 // Field b:I
         5: new           #19                 // class com/loksail/learndemo/demo/Instan
         8: dup
         9: invokespecial #20                 // Method com/loksail/learndemo/demo/Instan."<init>":()V
        12: putstatic     #21                 // Field instan:Lcom/loksail/learndemo/demo/Instan;
        15: return
      LineNumberTable:
        line 21: 0
        line 31: 5
}
SourceFile: "Demo01.java"
  • Literal quantity:

In computer science, literal is a notation used to express a fixed value in the source code.Almost all computer programming languages have literal representations of base values, such as integers, floating-point numbers, and strings; many also support literal representations of Boolean and character type values; and some even support literal representations of elements of enumeration type and values of composite types such as arrays, records, and objects.

Literals here refer to string literals, values of the eight basic data types declared final, and values of non-final floats, double, long data types, etc., based on the constant pool above

  • String literals correspond to

    private String str = "Hello World" corresponds to the literal quantity of Hello World #88

    String string = "Hello Method" corresponds to a Hello Method with a literal quantity of_

  • The eight basic data type values declared final are

    private static final long c = 10000L literal equivalent to 10000l of #383

    private static final double d = 5.5 corresponds to 5.5d of 41

    Private static final int = 5 corresponds to a literal quantity of #5

    private static final short f = 6 corresponds to 6 literals of #47

    private static final boolean g = false corresponds to a literal amount of 0 of #50

    private static final boolean h = true corresponds to a literal quantity of #521

    Private static final char I ='k'corresponds to 107 of #55

    private static final byte j = 7 corresponds to a literal amount of 7

    private static final float k = 6.6f The literal equivalent is #60 6.6f

  • Values of non-final float,double,long data types

    private float m1 = 10.1f corresponds to 10.1f of #2

    private double m2 = 10.2 corresponding to 10.2d literal #4

    private long m3 = 103 corresponds to 103l of #7

    The values in red above are literals. You can see that values of byte, char, short, boolean types are all stored in the constant pool as Integer types, while string literals include literals in member variables and local variables, while literals in basic data types are literals in member variables only.

  • Symbol references: Symbol references are concepts of compilation principles and are relative to direct references. They include the following three types of constants:

  1. Fully Qualified Name of Class and Interface

    • The fully qualified name of the interface corresponds to #101`java/lang/Object`
    • The fully qualified name of the class corresponds to #100`com/loksail/learndemo/demo/Demo01`
  2. Name and descriptor of the method (Descriptor)

    The <init>method is the instance instance instance constructor, which initializes non-static variable resolution (non-static member variable initialization, construction method, non-static code block, which are merged into the <init>method sequentially at compile time), while the <clinit>method is the class class constructor that initializes static variables (non-final static member variable initialization, static code block, which are both at compile time)Merge into <clinit>methods in order)

    • Method names and descriptors correspond to symbolic references to #64` and #65`()V`

    • The symbolic reference to the name of the method is #76` `

    • The symbolic references corresponding to the name of the main method are #76's <clinit>and #77 ([Ljava/lang/String;)V

    • The symbolic references to the names and descriptors of the method1 methods are `method1` of #68 and `(Ljava/lang/String;)V`

    • The symbolic references to the names and descriptors of the method2 methods are #70`method2` and #71`(I)I`

    • The symbolic references corresponding to the names and descriptors of method3 methods are `method3` and () Ljava/lang/String;`

    • The symbolic references corresponding to the names and descriptors of the method4 methods are `method4` of #74and `() Ljava/lang/Long; `

    • The name and descriptor of the main method correspond to the symbol references of #76`main` and #77` ([Ljava/lang/String;)V`

  3. The name and descriptor of the field

    • The symbolic references to the names and descriptors of the field1 fields are Field1 of #24 and I of #25, respectively.

    • The symbolic references corresponding to the names and descriptors of the field2 fields are #field2 and #27's Lcom/loksail/learndemo/demo/Instan, respectively;

    • The name of the m1 field and the descriptor corresponding to the symbol reference are m1 of #and F of #

    • The corresponding symbolic references for the name and descriptor of the M2 field are m2 of #30 and D of #31, respectively

    • The corresponding symbolic references for the name and descriptor of the M3 field are m3 of #and J of #

    • The name of the a field and the descriptor corresponding to the symbol reference are a of #34and I of #25

    • The name of the B field and the descriptor corresponding to the symbol reference are #b and #I, respectively

    • The name of the C field and the descriptor corresponding to the symbol reference are #c and #J, respectively

    • The name of the D field and the descriptor corresponding to the symbol reference are D of #40 and D of #31, respectively

    • The symbolic references for the name and descriptor of the E field are e for and I for

    • The symbolic references for the name and descriptor of the f field are f for #45 and S for #46, respectively

    • The name of the G field and the descriptor corresponding to the symbol reference are g of #48and Z of #49

    • The name of the H field and the descriptor corresponding to the symbol reference are h of#51and Z of#49

    • The name of the i field and the descriptor corresponding to the symbol reference are i of #53 and B of #57, respectively

    • The name of the j field and the descriptor, respectively, correspond to the symbol references of #j and #I of #

    • The name of the K field and the descriptor corresponding to the symbol reference are k for #59and F for #29

    • The name of the instan field and the descriptor corresponding to the symbol reference are Lcom/loksail/learndemo/demo/Instan for #61 and #27, respectively;

    • The name of the str field and the descriptor corresponding to the symbol reference are `str` of #62 and `Ljava/lang/String`;

    The value marked red above is the symbol reference

The Role of Symbol References in 5.4.1.2

Reference material

How are symbol references stored in JVM?

jvm class loading and parsing process (summary nature)

  • Fully qualified names of classes and interfaces, as well as symbolic references to field names and descriptions, are used primarily for initializing custom values for member variables.

    For example, 2: putstatic #18 // Field b:I in 216 lines of the byte code file can be obtained from a constant item association relationship #2 -> #22. #84 -> #100. #28:#29 -> com/loksail/learndemo/demo/Demo01.m1:F, which takes a ClassClass object of a class by its fully qualified name (the parsing phase of class loading changes a class's symbol reference to a direct pointer to the InstanceKlass object).Then, according to the field name and descriptor, find a matching field pointer to the list of fields recorded on the ClassClassClass object (its position is relative offset) and write it back to the constant pool item#2, press the 0:bipush 20 of 215 lines into the 20-value stack at the top of the operand stack, and initialize the custom value 20 for the found field pointer B

    1. Initialization of static member variables is when the class is loaded into memory, during the preparatory phase of class loading, default values are initialized for static member variables, such as private static int a = 1; default values of 0 are initialized for a at this time, then custom values for static member variables, such as private static int a = 1, are initialized during the initialization phase of class loading; and a is initialized for a at this timeCustom Value 1

    2. Initialization of instance member variables is to execute the JVM's new directive when an object is created, first copy the definition of the instance variable from the method area to the heap area, and initialize the default value for the member variable, such as private int a = 1; then initialize the default value of 0 for a; and then call <init>of the instance constructor to initialize the custom value for the member variable, such as private int a = 1;Initialize custom value 1 for a at this time

    2. For the values of the basic data type of final static, the custom values are initialized directly for the member variables only in the preparatory phase of class loading, and can no longer be assigned during the initial phase of class loading.

    3. Initialization custom values for static member variables correspond to byte codes in the <clinit>method during the initialization phase of class loading

    static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=0, args_size=0
             0: bipush        20
             2: putstatic     #18                 // Field b:I
             5: new           #19                 // class com/loksail/learndemo/demo/Instan
             8: dup
             9: invokespecial #20                 // Method com/loksail/learndemo/demo/Instan."<init>":()V
            12: putstatic     #21                 // Field instan:Lcom/loksail/learndemo/demo/Instan;
            15: return
          LineNumberTable:
            line 21: 0
          line 31: 5
    

    4. Initialization custom values for instance member variables correspond to byte code files in the <init>method during the initialization phase of class loading

      public com.loksail.learndemo.demo.Demo01();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // float 10.1f
             7: putfield      #3                  // Field m1:F
            10: aload_0
            11: ldc2_w        #4                  // double 10.2d
            14: putfield      #6                  // Field m2:D
            17: aload_0
            18: ldc2_w        #7                  // long 103l
            21: putfield      #9                  // Field m3:J
            24: aload_0
            25: bipush        10
            27: putfield      #10                 // Field a:I
            30: aload_0
            31: ldc           #11                 // String Hello World
            33: putfield      #12                 // Field str:Ljava/lang/String;
          36: return
    
  • Fully qualified names of classes and interfaces, as well as symbolic references to method names and descriptions, are used primarily to provide connections (static resolution and dynamic joins) for method calls.

    For example, in line 152 of the byte code file, 1: invokevirtual #13 // Method method3:()Ljava/lang/String; #13 -> #22. #90 -> #100. #72:#73 -> com/loksail/learndemo/demo/Demo01.method3:() Ljava/lang/String;, the Class object of a class can be obtained by the fully qualified name of the class (the parsing phase loads a class into a class).The number reference becomes a direct pointer to the InstanceKlass object, then matches the method memory address based on the method name and descriptor to the list of methods recorded on the ClassClass Class object, and finally writes the found method memory address back to the constant pool item #13, which is to convert the symbol reference to a direct reference and make the method call.

    Method calls in byte code take a symbolic reference to the method in the constant pool as an argument.Some of these symbolic references are converted to direct references during the class loading phase or the first time they are used, a transformation known as static resolution.The other part will be converted to a direct reference during each run, which is called a dynamic connection.

5.4.1.3 Symbol Reference to Direct Reference

Class files do not determine the location of classes or interfaces, fields, and methods in memory during the compilation phase, so class or interface, field, and method calls are all stored in the Class file as symbol references. Some symbol references are converted into direct references during the parsing phase of class loading (such as class/interface symbol references currently loaded, field symbol references)Symbol references that conform to the static parsing method) are in part converted to direct references at run time (such as other class/interface symbol references defined by the current class, symbolic references that conform to dynamically connected methods).

The parsing phase of class loading is to replace a symbolic reference with a direct reference, and the parsing action resolves the methods of the class or interface, field, class or interface.The first is to load the class with the class loader.The fields and methods in the class are resolved step by step during loading.

Symbol references are explicitly defined as literals in the constant pool, where direct references are pointers to the target, or relative offsets.

  • Class parsing:
    Class parsing changes a class's symbolic reference to a direct pointer to an InstanceKlass object.When an object is created, this pointer is assigned to the _kclass pointer in the object header.This locates the metadata information for the class.Accessing the metadata information of a class is achieved by describing the InstanceKlass object of that class, although each class corresponds to only one InstanceKlass object.This is how the class itself is described as a form of memory.Because the data inside the object is stacked continuously in memory, when you access a field of a class, you need to record the offset of the field from the object header through the metadata InstanceKlass object.Of course, the way to call an object is to locate the InstanceKlass object's method table instead of its memory area.Creating an object is simply writing the values of various fields corresponding to the class metadata to a single area of memory.Accessing the value of an object's field is by locating the relative offset of the field from the object's starting address.Determining the relative offset is done during the field resolution phase.

  • Resolution of the field:
    Field analysis is to determine the access address of the field of an object and to calculate the offset from the starting address of the relative object.

  • To parse a field symbol reference, first parse the class reference symbol CONSTANT_CLASS_INFO, which is the address of the class meta information, and then parse the field in the class.

  • HotSpot VM has another item called constant pool cache to store it, since the relative offset of the field resolution plus additional metadata is wider than the original constant pool index and cannot be placed in the original constant pool.

    getfield cp#12 (cp stands for constant pool)

    Will be resolved to

    fast_bgetfield cpc#5 // (offset: +40, type: boolean,...) (cpc#5 here means item 5 of constant pool cache)
    After parsing, the offset information is recorded in the constant pool cache, and getfield is rewritten to the byte code fast_bgetfield of the corresponding type based on the type information recorded in the parsed constant pool cache entry to avoid parsing it once at a later time, then fast_bgetfield can access the field according to the correct type of offset information.

  • Static variable fields are stored in Class objects with offsets relative to Class objects, while instance member variable fields are stored in instance objects with offsets relative to instance object headers.

  • Method parsing:

  • A part of the method call's symbolic reference is converted to a direct reference during the parsing phase of class loading by taking a methodblock pointer matching the method name and descriptor from the Array<Method*>* type_methods array in the ClassClass object (metadata information of the class) and replacing the symbols of the corresponding methodref type items in the constant table with this method pointerReferences (if no matching method exists in the current class, continue to look from the parent class, where as with field parsing, the resolved method pointer will be present in the constant pool cache, and subsequent calls will get the direct reference directly from the cpc). When invokestatic or invokespecial directives call these methods, find the corresponding method pointer based on the direct reference and execute the partyMethod, which is part of static parsing (non-virtual methods, including static methods, private methods, instance construction methods, parent methods of super calls, final methods).

  • The other part of the method call's symbolic reference is converted to a direct reference during each run.

    • When invokevirtual directives are executed to invoke a method, the directive parameter is the symbolic reference to the method, such as invokevitual #2, at which point the JVM will find that the directive has not yet been resolve d, so it will parse it first, find the constant pool item #2 through the reference relationship of the methodref type item in the constant table through the 0x0002 under the constant pool recorded by its operands.Value.The process of parsing an item of type methodref finds class information through the class_index item of the item, method name and method descriptor through the name_and_type_index item, and then virtual method table (default_vtable_indices of type Array<int>*) is found in the ClassClass Class object, corresponding subscript to the matching method is found based on the method name and descriptor, which points to the methodblock*pointerThat is, the corresponding method memory address entry, then replaces the previous symbol reference with the subscript and number of parameters of the virtual method table.That is, the symbol reference becomes a subscript to the virtual method table.This subscript is a direct reference.
      Symbolic reference to class --> instanceKlass --> vtable_index --> methodblock pointer

    • After parsing the constant pool item #2 back to the invokevirtual directive, the invokevirtual directive becomes invokevirtual_quick with parameters such as the subscript of the virtual method table (vtable index) and the number of parameters of the method, such as invokevirtual_quick vtable_index=6 and args_size=1.So instead of calling the method block directly, the method block is called based on the subscript after finding the virtual method table.

    This part is dynamic connection

5.4.2 Runtime Constant Pool

The runtime constant pool is part of the method area and a memory area.

  • A pool of runtime constants is essentially a region of the runtime where compiled class information is placed to dynamically retrieve class information.

  • Class file constant pools are stored in the run-time constant pool that enters the method area after the class is loaded.A class is loaded into the JVM and then corresponds to a pool of runtime constants. The pool of runtime constants is dynamic relative to the pool of Class file constants. Class file constants are only a static storage structure with references to symbols.

  • Runtime constant pools can resolve symbolic references to direct references during runtime.Runtime constant pools can be said to be used to index and find field and method names and descriptors.Given the index of any method or field, the type information and name and descriptor information that the method or field belongs to can be obtained from this index, which involves method invocation and field acquisition.

5.4.3 String Constant Pool

  • The string constant pool is global and unique to the JVM, so it is also known as the global string constant pool.
  • String literals in the run-time constant pool are used as members during the load initialization phase of the class; if local, the string constant pool is used only when it is used (when this code is executed).
  • In fact, the byte code corresponding to "Use Constant Pool" is an ldc instruction that is executed when assigning a reference to a String type to see if a reference to this string object exists in the constant pool, returns the reference directly if it exists, or creates the string object in the heap and records the reference in the string constant pool (jdk1.7).The String class's intern() method also places strings in the string constant pool at run time.
  • In addition to string constant pools, buffer pool technology is used for the packaging classes of the six basic data types in the JVM except for the two floating point types. However, the packaging classes of Byte, Short, Integer, Long and Character integers only use buffer pools when their corresponding values are [-128,127], beyond which new objects will still be created.
  • After java7, the pool of string constants is no longer stored in the method area but in the heap.

5.5 String Constant Pool

Definition

  • StringTable, also known as String Pool, is a pool of string constants that exists in the heap (changed after jdk1.7).
  • StringTable has a storage structure similar to HashTable, but it does not trigger rehash, which means it cannot be expanded.

Characteristic

  • Strings in the class and runtime constant pools are literal only (symbols) and become string objects the first time they are used to join the string constant pool

    (Note: String literals stored in the compile-time class constant pool are only added to the string constant pool when it is run, and string variables in the program are only present in the heap)

  • Using a pool of string constants, you can avoid creating string objects repeatedly

  • String Builder (1.8) is used to stitch string variables.

  • String constant stitching works by compiler optimization

  • You can use the intern method to actively put string objects not yet in the string constant pool into the string pool

5.5.1 Analyzing string storage locations with byte code instructions

Case:

public class StringDemo03 {


    public static void main(String[] args) throws Exception {
        String str1 = "a";
        String str2 = "b";
        String str3 = "ab";
        String str4  = str1 + str2;
        String str5  = "a" + "b";

        System.out.println(str3 == str4);
        System.out.println(str3 == str5);
    }
}

Byte code instruction

Classfile /D:/JWF/Gitee/learn-demo/src/main/java/com/loksail/learndemo/stringp/StringDemo03.class
  Last modified 2019-12-24; size 887 bytes
  MD5 checksum dd4ccbd827ec00728aa1f85c60b7a2fa
  Compiled from "StringDemo03.java"
public class com.loksail.learndemo.stringp.StringDemo03
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#27        // java/lang/Object."<init>":()V
   #2 = String             #28            // a
   #3 = String             #29            // b
   #4 = String             #30            // ab
   #5 = Class              #31            // java/lang/StringBuilder
   #6 = Methodref          #5.#27         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#32         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#33         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Fieldref           #34.#35        // java/lang/System.out:Ljava/io/PrintStream;
  #10 = Methodref          #36.#37        // java/io/PrintStream.println:(Z)V
  #11 = Class              #38            // com/loksail/learndemo/stringp/StringDemo03
  #12 = Class              #39            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #40            // "[Ljava/lang/String;"
  #21 = Class              #41            // java/lang/String
  #22 = Class              #42            // java/io/PrintStream
  #23 = Utf8               Exceptions
  #24 = Class              #43            // java/lang/Exception
  #25 = Utf8               SourceFile
  #26 = Utf8               StringDemo03.java
  #27 = NameAndType        #13:#14        // "<init>":()V
  #28 = Utf8               a
  #29 = Utf8               b
  #30 = Utf8               ab
  #31 = Utf8               java/lang/StringBuilder
  #32 = NameAndType        #44:#45        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #33 = NameAndType        #46:#47        // toString:()Ljava/lang/String;
  #34 = Class              #48            // java/lang/System
  #35 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #36 = Class              #42            // java/io/PrintStream
  #37 = NameAndType        #51:#52        // println:(Z)V
  #38 = Utf8               com/loksail/learndemo/stringp/StringDemo03
  #39 = Utf8               java/lang/Object
  #40 = Utf8               [Ljava/lang/String;
  #41 = Utf8               java/lang/String
  #42 = Utf8               java/io/PrintStream
  #43 = Utf8               java/lang/Exception
  #44 = Utf8               append
  #45 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               println
  #52 = Utf8               (Z)V
{
  public com.loksail.learndemo.stringp.StringDemo03();
    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 8: 0

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=6, args_size=1
         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: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        36: aload_3
        37: aload         4
        39: if_acmpne     46
        42: iconst_1
        43: goto          47
        46: iconst_0
        47: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        50: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        53: aload_3
        54: aload         5
        56: if_acmpne     63
        59: iconst_1
        60: goto          64
        63: iconst_0
        64: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        67: return
      LineNumberTable:
        line 12: 0
        line 13: 3
        line 14: 6
        line 15: 9
        line 16: 29
        line 18: 33
        line 19: 50
        line 20: 67
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 46
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 79 /* same_locals_1_stack_item */
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
    Exceptions:
      throws java.lang.Exception
}
SourceFile: "StringDemo03.java"
  • From the byte code file above, the class file constant pool stores a #28, b #29, a b #30 string literals defined in the program. When loaded into the runtime constant pool, a, b and a b are still literals (symbols) in the constant pool and have not yet become String objects and are placed in the StringTable.String literals in the run-time constant pool become string objects only when the program runs to the corresponding bytecode instruction

    String str1 = "a";
    String str2 = "b";
    String str3 = "ab";
    
  • ldc #2 turns a string literal in the runtime constant pool into a string object, puts it into a StringTable, and pushes the string object reference into the operand stack

  • ldc #3 turns a b-string literal in the run-time constant pool into a string object, puts it into a StringTable, and pushes the string object reference into the operand stack

  • ldc #4 turns the ab string literal in the run-time constant pool into a string object, puts it into a StringTable, and pushes the string object reference into the operand stack

  • String str4 = str1 + str2; the corresponding byte code instruction is

    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
    
  • First, new #5 creates and defaults a StringBuilder-type object and puts its reference on the operand stack;

    dup duplicates this object reference of the operand stack and pushes it onto the top of the stack;

    invokespecial #6 references this object from the operand stack and calls its parameterless construction method;

    aload_1 pushes the variable (a string object) at position 1 in the local variable table onto the top of the operand stack;

    invokevirtual #7 operand stack, call append method of StringBuilder object to add a string object

    aload_2 pushes the variable (b string object) at position 2 in the local variable table onto the top of the operand stack;

    invokevirtual #7 operand stack, call append method of StringBuilder object to add b string object

    invokevirtual #8 Calls the toString method of the StringBuilder object

    astore 4 stores references to StringBuilder objects in 4 locations in the local variable table

  • String str4 = str1 + str2; equivalent to new StringBuilder().append("a").append("b").toString() when a byte code instruction is run

  • toString() of StringBuilder

    char[] value;
    int count;
    @Override
    public String toString() {
      // Create a copy, don't share the array
      return new String(value, 0, count);
    }
    
  • String str5 = "a" + "b"; corresponding byte code instruction is

    29: ldc           #4                  // String ab
    31: astore        5
    
  • ldc #4 converts the string literal ab corresponding to #4 into a string object and pushes its reference to the top of the stack

  • astore operand stack, putting ab's object reference in position 5 of the local variable table

  • That is String str5 = "a" + "b" is equivalent to String str5 = "a b". This is an optimization of JVM at compile time and does not require re-splicing in byte code instructions.

Taken together, String str4 = str1 + str2; String str5 = "a" + "b" in the heap; String str5 = "a" + "b" in the string constant pool; String str5 = "a" + "b"; equivalent to String str3 = "a b";

So the end result is

false
true

5.5.2 Verify delayed loading of string objects

Verify that the string literals loaded from the class constant pool into the runtime constant pool during class loading are only symbols, not string objects, and are converted to string objects only when the corresponding bytecode instruction is run

case

public class StringDemo03 {

    public static void main(String[] args) throws Exception {
        System.out.println();

        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");

        System.out.println();
    }
}
  • At line 6 breakpoint, run in IDEA debug mode to view the console's memory information, where the number of string objects is 2304

  • After running the sixth line, the number of string objects is 2305, which means that.The literal value of the corresponding "1" string in the run-time constant pool is simply a symbol, and is not converted to a string object until the corresponding bycode instruction is run

  • After line 7, the number of string objects is 2306, and the corresponding "2" string literal in the run-time constant pool is converted to a string object

  • After line 8 is run, the number of string objects is 2307, and the corresponding "3" string literal in the run-time constant pool is converted to a string object

  • After running the code at line 9 or later, the number of string objects is still 2307 because the string objects "1", "2", "3" have been put into the string constant pool, reusing will reference the same address, and will not create a new string object.

5.5.3 String's Internal Method

  • Before jdk1.6, intern's processing was to determine if the literal amount of a string object was in the string constant pool, if there was a direct return string object reference in the string constant pool, and if it was not found, create a string object with the same literal amount in the string constant pool and return a reference to a new string object.
  • After jdk1.7, intern first determines if the literal amount of a string object is in the string constant pool, if there is a direct return string object reference in the string constant pool, and if it is not found, adds a reference to the string object that is currently invoked to the string constant pool and returns a reference to the string object.

Case (based on 1.8)

public class StringDemo03 {


    public static void main(String[] args) throws Exception {

        String str1 = new String("a") + new String("b");

        String str2 = str1.intern();

        System.out.println(str2 == str1);

        System.out.println(str2 == "ab");

    }
}
  • String str1 = new String("a") + new String("b"); on execution, "a" and "b" are placed in the string constant pool, and then a string object with literal value "a b" is created in the heap through StringBuilder's append and toString methods, and its object reference is returned, that is, a string object with literal value "a b" in the heap and literal values "a" and "b" in the string constant pool.String object

  • String str2 = str1.intern(); on execution, the "ab" string object in the heap corresponding to STR1 is attempted to be added to the string constant pool, since there is no string object literally "ab" in the string constant pool at this time, so a reference to the "ab" string object in the heap corresponding to STR1 is added to the string constant pool and this object reference is returned.That is, in the heap, there is a string object with the literal value "a b". In the string constant pool, there is a string object with the literal values "a" and "b", and a reference to a string object with the literal value "a b" in the heap.

  • System.out.println(str2 == str1); when executed, STR1 is a reference to the'ab'string object in the heap and STR2 is a reference to the'ab' string object in the heap returned from the string constant pool, both pointing to the'ab'string object in the heap, so the result is true

  • System.out.println(str2 == "ab"); when executed, STR2 is a reference to the "ab" string object in the heap returned from the string constant pool, while "ab" is a string object that looks up the corresponding literal value from the string constant pool, where the string constant pool returns a reference to the "ab" string object in the heap, so the result is true

  • Adjust the case by adding String str = "ab" in line 7

  • The "ab" string object is first added to the string constant pool.

  • When line 8 is executed, the literal value of the string object corresponding to str1 already exists in the string constant pool, so the object reference corresponding to str1 will not be added to the string constant pool, but str2 will point to the "ab" string object in the string constant pool.

  • So STR1 is pointing to an "ab" string object in the heap and STR2 is pointing to an "ab" string object in the string constant pool, so str2!= str1, str2 == "ab".

5.5.4 Comprehensive Cases

public class StringDemo03 {


    public static void main(String[] args) throws Exception {
        String s1 = "a";
        String s2 = "b";
        String s3 = "a" + "b";
        String s4 = s1 + s2;
        String s5 = "ab";
        String s6 = s4.intern();

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);

        String x2 = new String("c") + new String("d");
        String x1 = "cd";
        x2.intern();

        //What if you swap lines 17,18?
        //If it is jdk1.6?
        System.out.println(x1 == x2);

    }
}
  • s3 == s4 false, s3 is a reference to an object in the string constant pool, s4 is a reference to an object in the heap
  • s3 == s5 true, "a" + "b" will be optimized to "a b" by the compiler, s3 and s5 all point to the same object reference in the string constant pool
  • s3 == s6 true, s4.intern() returns a reference to the "ab" string object in the string constant pool, so s3 and s6 are equal
  • x1 == x2 false, because x1 = "cd" is defined first, then there is already a "cd" string object in the string constant pool, so x2.intern(), does not add the string object reference corresponding to X2 to the string constant pool, all x1!= x2
  • If you swap the position of 17,18 lines, x1 == x2 true, because when x2.intern() adds a reference to the string object of X2 to the string constant pool, the string constant pool does not have a string object literally "cd", so when x1 = "cd" is defined, it returns a reference to the string object of x2, so the two are equal
  • If it is jdk1.6, x1 == x2 false, similarly, x1 = "cd" is defined first, one is an object in the heap, the other is an object in the string constant pool
  • If it is jdk1.6 and has swapped the position of 17,18 lines, x1 == x2 false, x2.intern() executes, although there is no string object literally "cd" in the string constant pool, 1.6 handles creating a new string object literally "cd" in the string constant pool, so x1 = "cd" gets an object reference in the string constant pool, and X2 is an object reference in the heapSo they are not equal.

Position of 5.5.5 String Constant Pool

  • Prior to jdk1.7, the string constant pool was in the permanent generation
  • jdk1.7 and later, the string constant pool is in the heap

Validation cases

Add -Xmx10m to the run parameters to make heap space overflow easily

public class StringDemo05 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int j = 0;
        try {
            for (int i = 0; i < 260000; i++, j++) {
                list.add(String.valueOf(i).intern());
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(j);
        }
    }
}

results of enforcement

java.lang.OutOfMemoryError: GC overhead limit exceeded
145776
    at java.lang.Integer.toString(Integer.java:401)
    at java.lang.String.valueOf(String.java:3099)
    at com.loksail.learndemo.stringp.StringDemo05.main(StringDemo05.java:16)
  • The output is not the expected java heap space because the parallel/concurrent reclaimer throws OutOfMemroyError when GC recycling takes too long.Too long is defined as more than 98% of the time spent on GC and less than 2% of heap memory reclaimed.Used to prevent applications from working properly due to too little memory.This means that GC is frequent but less memory is reclaimed because the jvm is turned on by default -XX:+UseGCOverheadLimit
  • Add -XX:-UseGCOverheadLimit to the run parameters

results of enforcement

java.lang.OutOfMemoryError: Java heap space
146427
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create byte arrau at JPLISAgent.c line: 813
  • Java.lang.OutOfMemoryError: The Java heap space heap heap heap heap memory overflow verifies that the string constant pool exists in the heap.
  • Running with jdk1.6 throws a java.lang.OutOfMemoryError: PermGen space exception, verifying that the string constant pool exists in a permanent generation (not shown here).

Garbage Collection for 5.5.6 String Constant Pool

The string constant pool is in the heap and triggers garbage collection when the heap is full

case

Add -Xmx10m-XX:+PrintStringTableStatistics-XX:+PrintGCDetails-verbose:gc to the run parameters

  • -Xmx10m Set maximum heap memory to 10m
  • -XX:+PrintStringTableStatistics Print string constant pool information
  • -XX:+PrintGCDetails-verbose:gc Print GC information
public class StringDemo02 {

    public static void main(String[] args) throws Exception {
        int j = 0;
        try {

        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(j);
        }
    }
}

results of enforcement

0
Heap
 PSYoungGen      total 2560K, used 1958K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 95% used [0x00000000ffd00000,0x00000000ffee9a28,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 0K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)
 Metaspace       used 3216K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 347K, capacity 388K, committed 512K, reserved 1048576K
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     13268 =    318432 bytes, avg  24.000
Number of literals      :     13268 =    567360 bytes, avg  42.762
Total footprint         :           =   1045880 bytes
Average bucket size     :     0.663
Variance of bucket size :     0.662
Std. dev. of bucket size:     0.814
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :      1770 =     42480 bytes, avg  24.000
Number of literals      :      1770 =    158488 bytes, avg  89.541
Total footprint         :           =    681072 bytes
Average bucket size     :     0.029
Variance of bucket size :     0.030
Std. dev. of bucket size:     0.172
Maximum bucket size     :         3

As you can see, the results did not trigger the GC, focusing on information about StringTable statistics

  • Number of buckets is also the length of the array, defaulting to 60013
  • Number of entries key pair number, that is, the number of string objects is 1770
  • Number of literals string literal number is 1770

Adjust code to add string objects to the string constant pool

public class StringDemo02 {

    public static void main(String[] args) throws Exception {
        int j = 0;
        try {
            for (int i = 0; i < 100; i++, j++) {
               String.valueOf(i).intern();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(j);
        }
    }
}

Execution results (partial)

StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :      1870 =     44880 bytes, avg  24.000
Number of literals      :      1870 =    163288 bytes, avg  87.320
Total footprint         :           =    688272 bytes
Average bucket size     :     0.031
Variance of bucket size :     0.031
Std. dev. of bucket size:     0.177
Maximum bucket size     :         3

You can see that the number of string objects in the string constant pool has been increased by 100

Adjust code to 10000 loops

public class StringDemo02 {

    public static void main(String[] args) throws Exception {
        int j = 0;
        try {
            for (int i = 0; i < 10000; i++, j++) {
               String.valueOf(i).intern();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(j);
        }
    }
}

results of enforcement

[GC (Allocation Failure) [PSYoungGen: 2048K->488K(2560K)] 2048K->761K(9728K), 0.0011489 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
10000
Heap
 PSYoungGen      total 2560K, used 856K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 18% used [0x00000000ffd00000,0x00000000ffd5c3a8,0x00000000fff00000)
  from space 512K, 95% used [0x00000000fff00000,0x00000000fff7a020,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 7168K, used 273K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 3% used [0x00000000ff600000,0x00000000ff6446c0,0x00000000ffd00000)
 Metaspace       used 3224K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 348K, capacity 388K, committed 512K, reserved 1048576K
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     13268 =    318432 bytes, avg  24.000
Number of literals      :     13268 =    567360 bytes, avg  42.762
Total footprint         :           =   1045880 bytes
Average bucket size     :     0.663
Variance of bucket size :     0.662
Std. dev. of bucket size:     0.814
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :      8712 =    209088 bytes, avg  24.000
Number of literals      :      8712 =    491784 bytes, avg  56.449
Total footprint         :           =   1180976 bytes
Average bucket size     :     0.145
Variance of bucket size :     0.157
Std. dev. of bucket size:     0.396
Maximum bucket size     :         4

As you can see, the number of string objects in the string constant pool is not more than 10,000, which is because the GC was triggered because the string objects we created were not referenced, so some of the string objects were reclaimed during GC, so the number of final string objects did not meet expectations.

5.5.7 String Constant Pool Performance Tuning

  1. Adjustment-XX:StringTableSize=Number of Buckets
  • As mentioned above, StringTable s are stored in a HashTable structure, i.e. by adding an array to a list of chains. The more buckets there are, the less likely they are to have hash conflicts and the more efficient they are to find.

  • jdk1.8 StringTable default number of barrels is 60013, minimum number of barrels is 1009

    case

    Run parameters: -XX:+PrintStringTableStatistics-XX:+PrintGCDetails-verbose:gc

    public class StringDemo02 {
    
       public static void main(String[] args) throws Exception {
           List<String> list = Stream.iterate(1, i -> i + 1).limit(460000).map(j -> String.valueOf(j)).collect(Collectors.toList());
           long start = System.nanoTime();
           for (String s : list) {
               s.intern();
           }
           System.out.println((System.nanoTime() - start) / 100000);
       }
    }
    

    Run Results

    979
    Heap
    PSYoungGen      total 75776K, used 46359K [0x000000076b580000, 0x0000000770a00000, 0x00000007c0000000)
     eden space 65024K, 71% used [0x000000076b580000,0x000000076e2c5d58,0x000000076f500000)
     from space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000)
     to   space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000)
    ParOldGen       total 173568K, used 0K [0x00000006c2000000, 0x00000006cc980000, 0x000000076b580000)
     object space 173568K, 0% used [0x00000006c2000000,0x00000006c2000000,0x00000006cc980000)
    Metaspace       used 4445K, capacity 4778K, committed 4992K, reserved 1056768K
     class space    used 502K, capacity 571K, committed 640K, reserved 1048576K
    SymbolTable statistics:
    Number of buckets       :     20011 =    160088 bytes, avg   8.000
    Number of entries       :     16555 =    397320 bytes, avg  24.000
    Number of literals      :     16555 =    731512 bytes, avg  44.187
    Total footprint         :           =   1288920 bytes
    Average bucket size     :     0.827
    Variance of bucket size :     0.832
    Std. dev. of bucket size:     0.912
    Maximum bucket size     :         6
    StringTable statistics:
    Number of buckets       :     60013 =    480104 bytes, avg   8.000
    Number of entries       :    462084 =  11090016 bytes, avg  24.000
    Number of literals      :    462084 =  25865720 bytes, avg  55.976
    Total footprint         :           =  37435840 bytes
    Average bucket size     :     7.700
    Variance of bucket size :     4.377
    Std. dev. of bucket size:     2.092
    Maximum bucket size     :        16
    

    The number of buckets is 60013, the total running time is more than 0.9s, no garbage collection triggered, the number of string objects in the string constant pool is 462084

    Run parameter increase -XX:StringTableSize=200000, run result

    727
    Heap
    PSYoungGen      total 75776K, used 46358K [0x000000076b580000, 0x0000000770a00000, 0x00000007c0000000)
     eden space 65024K, 71% used [0x000000076b580000,0x000000076e2c5ac0,0x000000076f500000)
     from space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000)
     to   space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000)
    ParOldGen       total 173568K, used 0K [0x00000006c2000000, 0x00000006cc980000, 0x000000076b580000)
     object space 173568K, 0% used [0x00000006c2000000,0x00000006c2000000,0x00000006cc980000)
    Metaspace       used 4442K, capacity 4778K, committed 4992K, reserved 1056768K
     class space    used 502K, capacity 571K, committed 640K, reserved 1048576K
    SymbolTable statistics:
    Number of buckets       :     20011 =    160088 bytes, avg   8.000
    Number of entries       :     16555 =    397320 bytes, avg  24.000
    Number of literals      :     16555 =    731512 bytes, avg  44.187
    Total footprint         :           =   1288920 bytes
    Average bucket size     :     0.827
    Variance of bucket size :     0.832
    Std. dev. of bucket size:     0.912
    Maximum bucket size     :         6
    StringTable statistics:
    Number of buckets       :    200000 =   1600000 bytes, avg   8.000
    Number of entries       :    462083 =  11089992 bytes, avg  24.000
    Number of literals      :    462083 =  25865672 bytes, avg  55.976
    Total footprint         :           =  38555664 bytes
    Average bucket size     :     2.310
    Variance of bucket size :     3.434
    Std. dev. of bucket size:     1.853
    Maximum bucket size     :        11
    

    The number of buckets is 200000, the total running time is more than 0.7s, no garbage collection triggered, the number of string objects in the string constant pool is 462083

    Run Parameter Adjustment-XX:StringTableSize=1009, Run Result

    40503
    Heap
    PSYoungGen      total 75776K, used 47659K [0x000000076b580000, 0x0000000770a00000, 0x00000007c0000000)
     eden space 65024K, 73% used [0x000000076b580000,0x000000076e40ac60,0x000000076f500000)
     from space 10752K, 0% used [0x000000076ff80000,0x000000076ff80000,0x0000000770a00000)
     to   space 10752K, 0% used [0x000000076f500000,0x000000076f500000,0x000000076ff80000)
    ParOldGen       total 173568K, used 0K [0x00000006c2000000, 0x00000006cc980000, 0x000000076b580000)
     object space 173568K, 0% used [0x00000006c2000000,0x00000006c2000000,0x00000006cc980000)
    Metaspace       used 4934K, capacity 5018K, committed 5248K, reserved 1056768K
     class space    used 559K, capacity 603K, committed 640K, reserved 1048576K
    SymbolTable statistics:
    Number of buckets       :     20011 =    160088 bytes, avg   8.000
    Number of entries       :     19153 =    459672 bytes, avg  24.000
    Number of literals      :     19153 =    819112 bytes, avg  42.767
    Total footprint         :           =   1438872 bytes
    Average bucket size     :     0.957
    Variance of bucket size :     0.957
    Std. dev. of bucket size:     0.978
    Maximum bucket size     :         7
    StringTable statistics:
    Number of buckets       :      1009 =      8072 bytes, avg   8.000
    Number of entries       :    463479 =  11123496 bytes, avg  24.000
    Number of literals      :    463479 =  25967096 bytes, avg  56.026
    Total footprint         :           =  37098664 bytes
    Average bucket size     :   459.345
    Variance of bucket size :    61.796
    Std. dev. of bucket size:     7.861
    Maximum bucket size     :       480
    

    The number of buckets is 200000, the total running time is more than 4s, no garbage collection triggered, the number of string objects in the string constant pool is 463479

    Summary:

  • The greater the number of StringTable buckets, the more efficient the string object lookup in the string constant pool.

  • When there are many string constants, you can adjust the StringTableSize to improve the efficiency of the search.

  1. Consider placing string objects in a pool of string constants

    case

    public class StringDemo02 {
    
       public static void main(String[] args) throws Exception {
           List<Integer> list = Stream.iterate(1, i -> i + 1).limit(10).collect(Collectors.toList());
           List<String> result = new ArrayList<>();
           for (int i = 0; i < 1000000; i++) {
               for (Integer num : list) {
                   result.add(new String(String.valueOf(num)));
               }
           }
           System.out.println("---------End--------");
           System.gc();
           System.in.read();
       }
    }
    

    Query the memory occupied by String objects using jvisualvm - > Sampler - > Memory

    You can see that at this point there are about 10 million instances of String objects

    Adjust case, use intern

    public class StringDemo02 {
    
    public static void main(String[] args) throws Exception {
        List<Integer> list = Stream.iterate(1, i -> i + 1).limit(10).collect(Collectors.toList());
        List<String> result = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            for (Integer num : list) {
                result.add(new String(String.valueOf(num)).intern());
            }
        }
        System.out.println("---------End--------");
        System.gc();
        System.in.read();
    }
    }
    

    Query the memory occupied by String objects using jvisualvm - > Sampler - > Memory

    You can see that there are only 15,000 String object instances at this time

    Reason analysis:

  • With no intern(), more than 10 million String objects created are added to the list collection, and gc is not garbage collected, so there are more than 10 million instances of String objects in memory

  • In the case of intern(), String objects create more than 10 million strings, but they are all duplicates. Using intern() will fetch string objects from the pool of string constants, so only 10 instances of String objects are actually added to the list collection, while other String objects are garbage collected after System.gc().

    Summary:

  • When the system has a large number of duplicate string uses, intern() can be used to avoid a large number of string objects occupying memory, thus saving heap memory.

  • Interna() consumes CPU performance when a large number of string objects enter the string constant pool, but has a higher cost-effectiveness than heap memory usage

  • Interna() Entering a large number of string objects into the string constant pool causes Young GC to slow down, because when Young GC scans the entire string constant pool, too many string objects in the string constant pool can affect the efficiency of the entire scan.

    Reference resources: YGC Prolonged by String.intern() of VM Source Analysis

Welcome to the Public Number, follow-up article update notification, and discuss technical issues together.

 

Thirteen original articles were published. 2. Visits 4217
Private letter follow

Tags: Java jvm less Programming

Posted on Tue, 14 Jan 2020 20:51:50 -0500 by Gho