当前位置:网站首页>[100 cases of JVM tuning practice] 04 - Method area tuning practice (Part 1)

[100 cases of JVM tuning practice] 04 - Method area tuning practice (Part 1)

2022-07-07 01:00:00 Half old 518

front said
Author's brief introduction : Half old 518, Long distance runner , Determined to persist in writing 10 Blog of the year , Focus on java Back end
Column Introduction : Case driven introduction JVM knowledge , Teach you how to use JVM Troubleshooting 、 Evaluation code 、 Optimize performance
The article brief introduction : Introduce the concept of method area 、 Help you understand constant pool 、String table Adjustment

7. Method area

7.1 Definition

The method area is java Shared area shared by all threads in the virtual machine , It mainly stores information about the structure of classes ( Member variables , Method 、 Constructor code ), Runtime constant pool , Class loader . The method area is created when the virtual machine is started , Logically, it is part of the heap ( Specific manufacturers may not follow the logical division standard when implementing ).

stay jdk1.8 before , The method area is located in jvm Permanent generation of , The string is stored in the constant pool . stay jdk1.8 in the future , The method area is located in the meta space of local memory , No longer occupy JVM Of memory space , The string exists in the heap . Refer to the following figure for details. .

 Insert picture description here

Tip:
Method area is actually a logical concept , Because you can find , stay jdk1.8 in the future , It is even split in physical storage space .

7.2 Method area memory overflow

The following code can demonstrate the method area memory overflow .

public class Demo1_8 extends ClassLoader { //  Binary bytecode that can be used to load classes 
    public static void main(String[] args) {
        int j = 0;
        try {
            Demo1_8 test = new Demo1_8();
            for (int i = 0; i < 10000; i++, j++) {
                // ClassWriter  The function is to generate the binary bytecode of a class 
                ClassWriter cw = new ClassWriter(0);
                //  Parameter meaning : Version number ,  The access level is public,  Class name ,  Package name ,  Parent class ,  Interface 
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                //  return  byte[]
                byte[] code = cw.toByteArray();
                //  Class loading is performed 
                test.defineClass("Class" + i, code, 0, code.length); // Class  object 
            }
        } finally {
            System.out.println(j);
        }
    }
}

stay jdk1.8 Previous versions , Set the permanent generation to 8m: -XX:MaxPermSize=10m, stay jdk1.8 in the future , Set the meta space to 8m: -XX:MaxMetaspaceSize=8m, It will be reported OutOfMemoryError.

In the actual work scenario ,spring、mybatis And other frameworks are used cglib To generate dynamically class, Therefore, improper use of the framework may lead to memory overflow in the method area . But in the jdk1.8 Later, the method area is moved to the meta space , Plenty of space , Garbage collection is also carried out by meta space , The possibility of memory overflow is reduced .

7.3 Constant pool

Here's a helloworld Code for .

//  Binary bytecode ( Class basic information , Constant pool , Class method definition , Contains virtual machine instructions )
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

The computer will eventually convert this code into binary code and execute , This binary code contains the basic information of the class 、 Class method definition ( Contains instructions )、 Constant pool . Let's switch to out The corresponding class File directory , Use the decompile command javap -v xxx.class Turn the contents of binary code into readable code to find out .

Classfile /F:/ Information   Decrypt JVM/ Code /jvm/out/production/jvm/cn/itcast/jvm/t5/helloworld.class
  Last modified 2021 year 9 month 9 Japan ; size 567 bytes
  SHA-256 checksum 37204bf6e654f64ae56660a1e8becfaa98b3ae7592b81b4b6e331de92a460b96
  Compiled from "HelloWorld.java"
public class cn.itcast.jvm.t5.HelloWorld
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #5                          // cn/itcast/jvm/t5/HelloWorld
  super_class: #6                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #23            // hello world
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // cn/itcast/jvm/t5/HelloWorld
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcn/itcast/jvm/t5/HelloWorld;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               HelloWorld.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // java/lang/System
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8               hello world
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               cn/itcast/jvm/t5/HelloWorld
  #27 = Utf8               java/lang/Object
  #28 = Utf8               java/lang/System
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public cn.itcast.jvm.t5.HelloWorld();
    descriptor: ()V
    flags: (0x0001) 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 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/itcast/jvm/t5/HelloWorld;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) 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 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"

Above , The constant pool is a constant table . Notice the method definition part, such as #2 The content such as , This actually corresponds to the constant pool ( surface )Constant pool The constant .

The constant pool is .class In the document , When the class is loaded , The contents of the constant pool will be put into the runtime constant pool , And the symbolic address will become the real address .

7.4 String table

Let's look at an interview question .

String s1 = "a"; 
String s2 = "b";
String s3 = "ab";  
String s4 = s1 + s2; 
String s5 = "a" + "b";

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

Answer that question , It needs to be made clear string table, Let's start with the simplest , Decompile the following code .

public class Demo1_22 {
    public static void main(String[] args) {
        String s1 = "a"; //  Lazy 
        String s2 = "b";
        String s3 = "ab";  
    }
}

Interception part . You can see jvm from #2 take String a It's in storage LocalVariableTable Of Slot1, Other strings are similar .

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=4, 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: return
      LineNumberTable:
        line 11: 0
        line 12: 3
        line 13: 6
        line 21: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  args   [Ljava/lang/String;
            3       7     1    s1   Ljava/lang/String;
            6       4     2    s2   Ljava/lang/String;
            9       1     3    s3   Ljava/lang/String;

stay jvm Startup time , The contents of the constant pool are loaded into the runtime constant pool , But at this time a,b,ab It's just a symbol , Instead of string objects . Only when specific instructions are executed , Such as 0: ldc #2 Will create a string object "a". At the same time ,jvm Still going String table[] Find out if there is "a" This string , If not, add it to String table[]. notes :String table[] It's actually hashtable structure , It can't be expanded .

stay java New in the code s4, And decompile .

String s4 = s1 + s2; 

The result of decompilation is as follows .

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

The above operation is equivalent to .

new StringBuilder().append("a").append("b").toString() 

among toString() The implementation of the method is :new String("ab"). therefore s3 == s4 As the result of the fasle.

System.out.println(s3 == s4); //false

Then we add... To the code s5.

 String s5 = "a" + "b";

The result of decompilation is as follows .

29: ldc           #4                  // String ab
31: astore        5

original ,javac Compile time helps us optimize , It thinks “a”,“b” Is a constant , The result is unlikely to change , So the result is directly determined as ab 了 . also , because "ab" stay String table There is already , So no new string objects will be created .

System.out.println(s3 == s4);  //true

intern() Method can put the string object in the heap into the string , Refer to the following code .

public class Demo1_23 {

    // String table["ab", "a", "b"]
    public static void main(String[] args) {

        String x = "ab"; 
        String s = new String("a") + new String("b");     //  Pile up   new String("a")   new String("b") new String("ab")
        String s2 = s.intern();// Try to put this string object into the string pool , If there is one, it will not put , If not, put it into the string pool , Will return the objects in the string pool 
        System.out.println( s2 == x);  //true,s2 And x Are all objects in the string pool 
        System.out.println( s == x ); //false,s It's objects in the heap , Different from the objects in the string pool 
    }

}

This is the case x2 You can successfully join the string pool , So the result is true.

String x2 = new String("c") + new String("d"); // new String("cd")
x2.intern();
String x1 = "cd";
System.out.println(x1 == x2);  //true

however jdk1.6 Call in intern() Method , Will try to put the string into the string pool , If so, it will not be put in , If not, a copy will be copied and put into the string pool , therefore , The object in the string pool is not the same object as the object in the heap . The same code above again jdk1.6 in x1 == x2 return false.

The characteristics of string pool are summarized as follows .

 Insert picture description here

7.5 String table The location of

stay jdk1.6,string table Put in constant pool , The constant pool is located in the method area of the permanent generation . Permanent generation only full gc Only when triggered will it be recycled , This leads to string table The recovery efficiency of is low .jdk1.7 take string table Moved to the pile .

7.6 String table The garbage collection of

Refer to the following code to configure parameters and run .

/**
 *  demonstration  StringTable  Garbage collection 
 * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
 */
public class Demo1_7 {
    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        try {
            for (int j = 0; j < 100000; j++) { // j=100, j=10000
                String.valueOf(j).intern();
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }

    }
}

The printed information is as follows

[GC (Allocation Failure) [PSYoungGen: 2048K->488K(2560K)] 2048K->875K(9728K), 0.0028226 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2536K->512K(2560K)] 2923K->958K(9728K), 0.0039494 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2560K->512K(2560K)] 3006K->1006K(9728K), 0.0020900 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
...
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :     26231 =    629544 bytes, avg  24.000
Number of literals      :     26231 =   1548152 bytes, avg  59.020
Total footprint         :           =   2657800 bytes
Average bucket size     :     0.437
Variance of bucket size :     0.418
Std. dev. of bucket size:     0.646
Maximum bucket size     :         4

string table The bottom is hashtable, Data is stored in the form of array and linked list , You can see that the number of buckets for storing strings is 60013 individual , The number of strings is 26231 individual . The number of strings we actually create is 10 0000 individual , Why does the printed quantity not match ? According to the printed information , It's because it triggered GC operation .

7.7 String table tuning

string table The essence is hashtable, and hashtable The performance of is closely related to the number of barrels , about string table Tuning is actually about hashtable Adjust the number of barrels .

/**
 *  Demonstrate the impact of string pool size on performance 
 *  -XX:+PrintStringTableStatistics
 */
public class Demo1_24 {

    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern();
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
        }


    }
}

Configure and run the above code , The printed information is as follows .

cost:439
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     13697 =    328728 bytes, avg  24.000
Number of literals      :     13697 =    609024 bytes, avg  44.464
Total footprint         :           =   1097840 bytes
Average bucket size     :     0.684
Variance of bucket size :     0.684
Std. dev. of bucket size:     0.827
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :     60013 =    480104 bytes, avg   8.000
Number of entries       :    481494 =  11555856 bytes, avg  24.000
Number of literals      :    481494 =  29750344 bytes, avg  61.788
Total footprint         :           =  41786304 bytes
Average bucket size     :     8.023
Variance of bucket size :     8.084
Std. dev. of bucket size:     2.843
Maximum bucket size     :        23

Configuration parameters -XX:StringTableSize=200000, The printed information is as follows .

cost:393
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     13697 =    328728 bytes, avg  24.000
Number of literals      :     13697 =    609024 bytes, avg  44.464
Total footprint         :           =   1097840 bytes
Average bucket size     :     0.684
Variance of bucket size :     0.684
Std. dev. of bucket size:     0.827
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :    200000 =   1600000 bytes, avg   8.000
Number of entries       :    481494 =  11555856 bytes, avg  24.000
Number of literals      :    481494 =  29750344 bytes, avg  61.788
Total footprint         :           =  42906200 bytes
Average bucket size     :     2.407
Variance of bucket size :     2.420
Std. dev. of bucket size:     1.556
Maximum bucket size     :        12

Configuration parameters -XX:StringTableSize=1009, The printed information is as follows .

cost:4870
SymbolTable statistics:
Number of buckets       :     20011 =    160088 bytes, avg   8.000
Number of entries       :     16327 =    391848 bytes, avg  24.000
Number of literals      :     16327 =    698456 bytes, avg  42.779
Total footprint         :           =   1250392 bytes
Average bucket size     :     0.816
Variance of bucket size :     0.811
Std. dev. of bucket size:     0.901
Maximum bucket size     :         6
StringTable statistics:
Number of buckets       :      1009 =      8072 bytes, avg   8.000
Number of entries       :    482764 =  11586336 bytes, avg  24.000
Number of literals      :    482764 =  29845512 bytes, avg  61.822
Total footprint         :           =  41439920 bytes
Average bucket size     :   478.458
Variance of bucket size :   432.042
Std. dev. of bucket size:    20.786
Maximum bucket size     :       547

You can find , When there are more barrels , The likelihood of hash conflicts will be reduced , This will take less time to enter the pool .

Why use string table To store strings ? Because it saves space , Avoid duplicate creation of string objects . Spread on the Internet twitter The user information stored in the contains address items , If not used string table Storage , Need about 30G Memory , However, these addresses may contain a large number of duplicate addresses , Maybe many users are from Zhongguancun, Beijing , therefore twitter Pool address information , from string table Create storage , Reduce this memory space to tens of M.

Feel this process through the following examples .

/**
 *  demonstration  intern  Reduce memory usage 
 * -XX:StringTableSize=200000 -XX:+PrintStringTableStatistics
 * -Xsx500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000
 */
public class Demo1_25 {

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

        List<String> address = new ArrayList<>();
        System.in.read();
        for (int i = 0; i < 10; i++) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if(line == null) {
                        break;
                    }
                    address.add(line);
                }
                System.out.println("cost:" +(System.nanoTime()-start)/1000000);
            }
        }
        System.in.read();


    }
}

Before typing on the keyboard , There is no data read operation , After typing, the data is read and stored in address in . At these two time nodes jvisualvm in sampler Take samples . give the result as follows .

 Insert picture description here

 Insert picture description here

Modify the code address.add()

address.add(line.intern());

 Insert picture description here

 Insert picture description here

原网站

版权声明
本文为[Half old 518]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207061710314409.html