String can also do performance optimization, I can only say Niubi!

Keywords: Java JDK Spring jvm

By Lyl Daisy
https://blog.csdn.net/kkkkk0826/article/details/104171355

String string is one of the most commonly used types in the system, which occupies a large amount of memory in the system. Therefore, efficient use of string can improve the performance of the system.

In the process of work and learning, I summarized the following three schemes for string optimization to share:

1, Super large string optimized for construction

Verification environment: jdk1.8

Decompiler: jad

***

2. Verification

First, execute an example 1 Code:

public class test3 {
    public static void main(String[] args) {
        String str="ab"+"cd"+"ef"+"123";
    }
}

After execution, decompile with the decompiler JAD: jad-o-a-s d.java test.class

Decompiled Code:

package example;
public class test
{
    public test()
    {
    //    0    0:aload_0
    //    1    1:invokespecial   #1   <Method void Object()>
    //    2    4:return
    }
    public static void main(String args[])
    {
        String str = "abcdef123";
    //    0    0:ldc1            #2   <String "abcdef123">
    //    1    2:astore_1
    //    2    3:return
    }
}

Case 2:

public class test1 {
    public static void main(String[] args)
    {
        String s = "abc";
        String ss = "ok" + s + "xyz" + 5;
        System.out.println(ss);
    }
}

Use the decompiler JAD to execute jad-o-a-s d.java test1.class for decompilation

package example;

import java.io.PrintStream;

public class test1
{
    public test1()
    {
    //    0    0:aload_0
    //    1    1:invokespecial   #1   <Method void Object()>
    //    2    4:return
    }
    public static void main(String args[])
    {
        String s = "abc";
    //    0    0:ldc1            #2   <String "abc">
    //    1    2:astore_1
        String ss = (new StringBuilder()).append("ok").append(s).append("xyz").append(5).toString();
    //    2    3:new             #3   <Class StringBuilder>
    //    3    6:dup
    //    4    7:invokespecial   #4   <Method void StringBuilder()>
    //    5   10:ldc1            #5   <String "ok">
    //    6   12:invokevirtual   #6   <Method StringBuilder StringBuilder.append(String)>
    //    7   15:aload_1
    //    8   16:invokevirtual   #6   <Method StringBuilder StringBuilder.append(String)>
    //    9   19:ldc1            #7   <String "xyz">
    //   10   21:invokevirtual   #6   <Method StringBuilder StringBuilder.append(String)>
    //   11   24:iconst_5
    //   12   25:invokevirtual   #8   <Method StringBuilder StringBuilder.append(int)>
    //   13   28:invokevirtual   #9   <Method String StringBuilder.toString()>
    //   14   31:astore_2
        System.out.println(ss);
    //   15   32:getstatic       #10  <Field PrintStream System.out>
    //   16   35:aload_2
    //   17   36:invokevirtual   #11  <Method void PrintStream.println(String)>
    //   18   39:return
    }
}

According to the decompilation results, you can see that the internal StringBuilder For string splicing.

Here are the recommendations: java.lang.String What exactly did the + operation of?

Then execute the code of example 3:

public class test2 {
    public static void main(String[] args) {
        String s = "";
        Random rand = new Random();
        for (int i = 0; i < 10; i++) {
            s = s + rand.nextInt(1000) + " ";
        }
        System.out.println(s);
    }
}

After decompiling the jad-o-a-s d.java test2.class with the decompiler JAD, it is found that the inner part of it also passes the StringBuilder To splice:

package example;
import java.io.PrintStream;
import java.util.Random;
public class test2
{
    public test2()
    {
    //    0    0:aload_0
    //    1    1:invokespecial   #1   <Method void Object()>
    //    2    4:return
    }
    public static void main(String args[])
    {
        String s = "";
    //    0    0:ldc1            #2   <String "">
    //    1    2:astore_1
        Random rand = new Random();
    //    2    3:new             #3   <Class Random>
    //    3    6:dup
    //    4    7:invokespecial   #4   <Method void Random()>
    //    5   10:astore_2
        for(int i = 0; i < 10; i++)
    //*   6   11:iconst_0
    //*   7   12:istore_3
    //*   8   13:iload_3
    //*   9   14:bipush          10
    //*  10   16:icmpge          55
            s = (new StringBuilder()).append(s).append(rand.nextInt(1000)).append(" ").toString();
    //   11   19:new             #5   <Class StringBuilder>
    //   12   22:dup
    //   13   23:invokespecial   #6   <Method void StringBuilder()>
    //   14   26:aload_1
    //   15   27:invokevirtual   #7   <Method StringBuilder StringBuilder.append(String)>
    //   16   30:aload_2
    //   17   31:sipush          1000
    //   18   34:invokevirtual   #8   <Method int Random.nextInt(int)>
    //   19   37:invokevirtual   #9   <Method StringBuilder StringBuilder.append(int)>
    //   20   40:ldc1            #10  <String " ">
    //   21   42:invokevirtual   #7   <Method StringBuilder StringBuilder.append(String)>
    //   22   45:invokevirtual   #11  <Method String StringBuilder.toString()>
    //   23   48:astore_1

    //   24   49:iinc            3  1
    //*  25   52:goto            13
        System.out.println(s);
    //   26   55:getstatic       #12  <Field PrintStream System.out>
    //   27   58:aload_1
    //   28   59:invokevirtual   #13  <Method void PrintStream.println(String)>
    //   29   62:return
    }
}

To sum up the case analysis, it is found that when strings are spliced with "+", there are the following situations inside:

***

*** StringBuilder To splice through append;

3. The loop process of case 3 is also a "+" splicing of variable strings. Therefore, during internal compilation, it will also create StringBuilder For splicing.

Compared with the three cases, it is found that in the third case, each time you do a loop, a StringBuilder object will be created, which will increase the memory of the system, which in turn will reduce the system performance.

Therefore, in the case of string splicing, it can be used explicitly in the single thread environment StringBuilder To avoid new one for each cycle StringBuilder Object; in a multithreaded environment, you can use thread safe StringBuffer, but when it comes to lock contention, StringBuffer performance will be better than StringBuilder all but.

In this way, the optimization effect of string splicing is achieved.

2, How to use String.intern Save memory?

Before answering this question, you can test a piece of code:

1. First, set - XX:+PrintGCDetails -Xmx6G -Xmn3G in idea to print GC log information, as shown in the following figure:

2. Execute the following example code:

public class test4 {
    public static void main(String[] args) {
        final int MAX=10000000;
        System.out.println("no need intern: "+notIntern(MAX));
        System.out.println("use intern: "+intern(MAX));
    }
    private static long notIntern(int MAX){
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            int j = i % 100;
            String str = String.valueOf(j);
        }
        return System.currentTimeMillis() - start;
    }

    private static long intern(int MAX){
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX; i++) {
            int j = i % 100;
            String str = String.valueOf(j).intern();
        }
        return System.currentTimeMillis() - start;
    }
}

GC log without intern:

[GC (System.gc()) [PSYoungGen: 377487K->760K(2752512K)] 377487K->768K(2758656K), 0.0009102 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 760K->0K(2752512K)] [ParOldGen: 8K->636K(6144K)] 768K->636K(2758656K), [Metaspace: 3278K->3278K(1056768K)], 0.0051214 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
 PSYoungGen      total 2752512K, used 23593K [0x0000000700000000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2359296K, 1% used [0x0000000700000000,0x000000070170a548,0x0000000790000000)
  from space 393216K, 0% used [0x0000000790000000,0x0000000790000000,0x00000007a8000000)
  to   space 393216K, 0% used [0x00000007a8000000,0x00000007a8000000,0x00000007c0000000)
 ParOldGen       total 6144K, used 636K [0x0000000640000000, 0x0000000640600000, 0x0000000700000000)
  object space 6144K, 10% used [0x0000000640000000,0x000000064009f2f8,0x0000000640600000)
 Metaspace       used 3284K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 359K, capacity 388K, committed 512K, reserved 1048576K

According to the analysis of the printed log: without the use of intern, the execution time is 354ms, and the occupied memory is 24229k. It is recommended to read: 46 ppts understand JVM.

GC logs using intern:

[GC (System.gc()) [PSYoungGen: 613417K->1144K(2752512K)] 613417K->1152K(2758656K), 0.0012530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 1144K->0K(2752512K)] [ParOldGen: 8K->965K(6144K)] 1152K->965K(2758656K), [Metaspace: 3780K->3780K(1056768K)], 0.0079962 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Heap
 PSYoungGen      total 2752512K, used 15729K [0x0000000700000000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 2359296K, 0% used [0x0000000700000000,0x0000000700f5c400,0x0000000790000000)
  from space 393216K, 0% used [0x0000000790000000,0x0000000790000000,0x00000007a8000000)
  to   space 393216K, 0% used [0x00000007a8000000,0x00000007a8000000,0x00000007c0000000)
 ParOldGen       total 6144K, used 965K [0x0000000640000000, 0x0000000640600000, 0x0000000700000000)
  object space 6144K, 15% used [0x0000000640000000,0x00000006400f1740,0x0000000640600000)
 Metaspace       used 3786K, capacity 4540K, committed 4864K, reserved 1056768K
  class space    used 420K, capacity 428K, committed 512K, reserved 1048576K

Log analysis: without the use of intern, the execution time is 1515ms and the occupied memory is 16694k;

To sum up: in the case of using intern, the memory is smaller than in the case of not using intern, but the time complexity is increased while the memory is saved. I've tried to increase MAX=10000000 by another 0. Using intern will take up to 11 seconds to execute. It can be seen that when traversal data is too large, it is not recommended to use intern.

Therefore, the premise of using intern must consider the specific use scenarios.

Here, you can be sure to use String.intern It does save memory.

Next, analyze the differences between different versions of intern in JDK.

In JDK 1.6, the string constant pool is in the method area, which belongs to the permanent generation.

In JDK 1.7, the string constant pool is moved to the heap.

In JDK 1.8, the string constant pool is moved to the meta space, independent of the heap.

Perform the following example in version 1.6, 1.7 and 1.8 respectively:

public class test5 {
    public static void main(String[] args) {

        String s1=new String("ab");
        s.intern();
        String s2="ab";
        System.out.println(s1==s2);

        String s3=new String("ab")+new String("cd");
        s3.intern();
        String s4="abcd";
        System.out.println(s4==s3);
    }
}

Version 1.6

Execution result:

fasle false

analysis:

In the first part:

***

***

*** 5 tough String interview questions! I suggest you look at it.

Focus on WeChat official account: Java technology stack, reply in the background: interview, you can get N dry cargo of Java on my test.

The schematic diagram is as follows:

Implementation part II:

***

***

***

Version 1.7

false true

Execute the first part: this part is basically similar to jdk1.6, except that s1.intern() returns a reference, not a copy.

Implementation part II:

***

2. When executing s3.intern(), the object reference of "abcd" will be put into the string constant pool. It is found that there is no such reference in the constant pool, so it can be put successfully. ***

Version 1.8

false true

Refer to some blogs on the Internet. In version 1.8, when using intern(), the execution principle is as follows:

If the string constant pool contains a string equivalent to the current object, the string in the constant pool will be returned; if it does not exist, the string will be stored in the constant pool and a reference to the string will be returned.

To sum up, it can be seen that among the three versions, when using intern, if there is no corresponding string in the string constant pool, there are the following differences:

For example:

String s1=new String("ab"); s.intern();

***

***

jdk1.8: if the string constant pool contains a string equivalent to the current object, the string in the constant pool will be returned; if not, the string will be stored in the constant pool and a reference to the string will be returned.

3, How to use the string segmentation method?

In simple string segmentation, indexOf can be used instead of split, because the performance of split is not stable enough, so for simple string segmentation, indexOf can be used first;

Recommend to my blog to read more:

1.Java JVM, collection, multithreading, new features series

2.Spring MVC, Spring Boot, Spring Cloud series tutorials

3.Maven, Git, Eclipse, Intellij IDEA series tools tutorial

4.Latest interview questions of Java, backend, architecture, Alibaba and other large factories

Feel good, don't forget to like + forward!

Posted by Bob_PHP_Builder on Tue, 02 Jun 2020 18:00:44 -0700