The running of a program in Java can not be separated from jvm's strict division of memory regions, each region is responsible for its own duties, in order to ensure the correct operation of the program.
A flowchart of a program from file to run
The memory area in JVM can be divided into five categories. Program counters, virtual machine stacks, stacks, method areas, local method stacks. The roles of each area are explained in turn below.
Program counter
Role: remember the execution address of the next instruction
Feature: Thread Private
Storage location: Registers (very fast)
Is there a memory overflow: No, it is the only area where no memory overflow will occur (as specified by the Java Virtual Machine Specification)
The above figure is a compiled byte code of the program, which is a JVM instruction. The instruction set of each operating system is the same. This also explains that java compiles once and runs everywhere. These instructions are not understood by the computer. An interpreter is needed in the middle to convert these instructions into machine code. After the interpretation, the CPU can run these machine code instructions.
java code - > byte code - > JVM instruction - > machine code - > CPU reads machine code instruction set, runs
VM Stack
Meaning: Memory space required for threads to run
Features: Threads are private, FIFO
Component: One or more stack frames, each method call means opening a stack frame.
Stack frame: Memory required for each method to run (local variable, parameter, return address)
Default size: Linux / Mac OS is 1M. Windows depends on Win's virtual memory
Parameters for setting stack size: -Xss1m
Each thread invokes a method with a stack, and each thread in the method has a local variable that does not interfere with each other.
There are two cases of OOM on a virtual machine stack, one is too many stack frames (recursive) and the other is too large (too many local variables, more memory is occupied than stack memory)
Eg:
package com._12306.SellTickets; public class Test4 { private static int count; public static void main(String[] args) { try { method1(); } catch (Throwable e) { e.printStackTrace(); System.out.println(count); } } private static void method1() { count++; method1(); } }
The demo above does not set a stop condition, and constantly calls the method1 method in the main method, only entering and leaving. By the time of 22204 times, the stack can no longer accommodate so many stack frames, and an error of stack memory overflow will be reported.
Native Method Stack
Role: Local method services used by virtual machines (methods that call other languages through interfaces)
Functionality: Similar to a virtual machine stack, it combines the two in a HotSpot virtual machine
Features: Threads are private, OOM appears
heap
Role: Objects created by the new keyword are stored in the heap
Features: Thread sharing, security issues, garbage collection mechanism to clean up unreferenced objects
Zoning: New Generation, Older Generation, Permanent Generation
VM parameter to set heap size: -Xmx2m
Heap memory overflow is caused by the constant creation of objects that are referenced and cannot be recycled by GC. As objects accumulate, heap memory is exhausted and exceptions are thrown.
Eg
package com._12306.SellTickets; import java.util.ArrayList; import java.util.List; public class Test5 { public static void main(String[] args) { int i = 0; //Counter try { List<String> arraylist = new ArrayList<>(); //String object is referenced by arrayList, GC cannot recycle String a = "Hello"; while (true) { //Infinite loop arraylist.add(a); //Continuously add strings to the collection, twice as often as before a += a; i++; } } catch (Throwable e) { e.printStackTrace(); System.out.println(i); } } }
StringTable
Role: Objects that store strings to improve string utilization.
Bottom implementation: Hash table to ensure that each string is unique.
Convenience: Save memory and read faster (locate string objects quickly based on literal size)
Storage location: 1.6 in permanent generation, 1.7 in heap. (The efficiency of permanent generation recycling is not high.)
Features: Thread sharing, and very secure, once created will not change, there will be OOM
OOM demo, setting VM parameters: -Xmx10m-XX:-UseGCOverheadLimit
Eg:
package com._12306.SellTickets; import java.util.ArrayList; import java.util.List; public class Test14 { public static void main(String[] args) { List<String> list = new ArrayList<>(); int i = 0; try { for (int j = 0; j < 260000; j++) { list.add(String.valueOf(i).intern()); //Place object i in a StringTable string pool i++; } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(i); } } }
Result
StringTable encountered a GC
Eg
package com._12306.SellTickets; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * Demonstrate StringTable garbage collection * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc */ public class Test15 { public static void main(String[] args) throws InterruptedException { int i = 0; try { for (int j = 0; j < 40000; j++) { // j=100, j=40000 String.valueOf(j).intern(); i++; } } catch (Throwable e) { e.printStackTrace(); } finally { System.out.println(i); } } }
Result
The state of GC after recycling, which originally needed to hold more than 60,000 string objects, now only 3,829, the new generation carried out two GC.
Method Area
Purpose: Store structure-related information about classes, member variables, methods, constructors for classes, running constant pools
Storage location: 1.8 was previously called the permanent generation and stored in the heap. 1.8 was called Metaspace and stored in local memory
Features: Thread sharing, not easy OOM
VM parameter to set method area size: -XX:MaxMetaspaceSize=10m
The memory overflow in the method area is due to too many byte code files that generate classes, of which the large use of cglib dynamic proxies is the main cause.
Since 1.8 uses the operating system's memory, my computer has 4G of memory, and it's not easy to fill it up. The example below sets the method area's memory to 10M.
Eg:
package com._12306.SellTickets; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; public class Test7 extends ClassLoader { public static void main(String[] args) { int j = 0; try { Test7 test = new Test7(); for (int i = 0; i < 40000; i++,j++) { //ClassWriter's role is to claim the class's binary byte code ClassWriter cw = new ClassWriter(0); //Version number, public, class name, package name, class, interface cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null); //Return byte[] byte[] code = cw.toByteArray(); //Execute class loading test.defineClass("Class" + i, code, 0, code.length); } } finally { System.out.println(j); } } }
Constant pool (part of method area)
Meaning: is a table from which virtual machine instructions find the class name, method name, parameter type, literal amount to be executed
And so on.
Generation Period: Created when compiled into a byte code file by the compiler
Location: In a byte code file, by command Javap-v byte code file.class Viewable
Hello, World is the first program for beginners, so let's talk about it next
package com._12306.SellTickets; public class Test8 { public static void main(String[] args) { System.out.println("Hello,World!"); } }
Because Idea is smart, the binary byte code file will be recompiled into a java file, and the byte code file will be decompiled using javap
HackerZhao:SellTickets apple$ javap -v Test8.class Classfile /Users/apple/IdeaProjects/aliossdemo/target/classes/com/_12306/SellTickets/Test8.class Last modified 2021-9-9; size 569 bytes MD5 checksum 4298cd01954f8e99de5ef1340de3ace6 Compiled from "Test8.java" public class com._12306.SellTickets.Test8 minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER 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 // com/_12306/SellTickets/Test8 #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 Lcom/_12306/SellTickets/Test8; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 Test8.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 com/_12306/SellTickets/Test8 #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 com._12306.SellTickets.Test8(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/_12306/SellTickets/Test8; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello,World! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 6: 0 line 7: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "Test8.java"
Constant pool is the constant pool of the process, which contains instructions.
Above is the compiled main method, which starts at 0:getstatic #2, finds the instruction #2 from the constant pool table, followed by #21, #22 and #21, and #22 finds the information you want all the time, knowing that this is an output statement: Field java/lang/System.out:Ljava/io/PrintStream;
Runtime Constant Pool
The constant pool is in the *.class file. When the class is loaded, its constant pool information is put into the run-time constant pool and the symbolic address inside becomes the real address.
Direct memory
Not memory management for a java virtual machine, but system memory
Common in nio operations, do buffer memory when nio data is read and written
Cost of allocation and recycling is high, but read and write performance is good
Not managed by JVM memory recycling
Common performance monitoring, troubleshooting tools
Scenario 1: High CPU usage
package com._12306.SellTickets; import java.util.concurrent.TimeUnit; public class Test6 { public static void main(String[] args) { new Thread(null,() ->{ while(true){ } },"thread1").start(); new Thread(null,() ->{ System.out.println("1..."); try { TimeUnit.SECONDS.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } },"thread2").start(); new Thread(null,() ->{ System.out.println("2..."); try { TimeUnit.SECONDS.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } },"thread3").start(); } }
Run the program, thread1 because of the program while(true) infinite cycle, resulting in high CPU usage.
View the process id, cpu information through the top command, as shown below
View process details through the jstack command
Scenario 2: Deadlock, waiting for each other's lock, no one will let go.
package com._12306.SellTickets; import java.util.concurrent.TimeUnit; public class Test9 { static A a = new A(); static B b = new B(); public static void main(String[] args) throws InterruptedException { new Thread(() -> { synchronized (a) { //Thread 1 falls asleep after lock a try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (b) { //Try to acquire lock b after waking up, which is owned by thread 2 System.out.println("I got the lock a and b"); } } }).start(); new Thread(() -> { synchronized (b) { //Thread 2 gets lock b synchronized (a) { //Thread 2 tries to acquire lock a, which is owned by thread 1 System.out.println("I got the lock a and b"); } } }).start(); } } class A { } class B { }
Find process id through jsp command
Get the status of the process through jstack and process id
Scenario 3: Heap memory consumption is too large, how is the troubleshooting process?
Eg
package com._12306.SellTickets; import java.util.concurrent.TimeUnit; public class Test10 { public static void main(String[] args) throws InterruptedException { System.out.println("1..."); //Phase 1 TimeUnit.SECONDS.sleep(30); byte[] bytes = new byte[1024 * 1024 * 10]; // 10MB System.out.println("2..."); //Phase 2, adding up memory to 10M TimeUnit.SECONDS.sleep(20); bytes = null; //Release reference here System.gc(); //Garbage collection System.out.println("3..."); //Phase 3, post-garbage collection status TimeUnit.SECONDS.sleep(100); } }
Open a visualization window using the jconsole command
Scenario 4: After multiple garbage collections, heap memory usage remains high.
Use the jvisualvm command to open the visualization window, unlike jconsole above, where you can dump heap memory information to see which object is consuming a lot of memory.
java source code
package com._12306.SellTickets; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class Test11 { public static void main(String[] args) throws InterruptedException { List<Student> list = new ArrayList<>(); for (int i = 0; i < 200; i++) { list.add(new Student()); } TimeUnit.SECONDS.sleep(200000L); } } class Student{ private byte[] bytes = new byte[1024*1024]; }
String in Constant Pool
String s1 = "a",String s2 = "b"
Is String s3 = s1 + s2 equal to String s4 = ab
This question has puzzled many beginners, including bloggers. But everything works for a reason, and once you know the result, you want to know how it works. Now I'll explain its underlying principle from the byte code perspective.
Eg:
package com._12306.SellTickets; //StringTable is a public class Test12 { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; } }
Javap-v decompile Test12.class
Classfile /Users/apple/IdeaProjects/aliossdemo/target/classes/com/_12306/SellTickets/Test12.class Last modified 2021-9-11; size 516 bytes MD5 checksum 58ed70d6cf6fa5fab5e81a28b5e092a2 Compiled from "Test12.java" public class com._12306.SellTickets.Test12 minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#24 // java/lang/Object."<init>":()V #2 = String #25 // a #3 = String #26 // b #4 = String #27 // ab #5 = Class #28 // com/_12306/SellTickets/Test12 #6 = Class #29 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/_12306/SellTickets/Test12; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 s1 #19 = Utf8 Ljava/lang/String; #20 = Utf8 s2 #21 = Utf8 s3 #22 = Utf8 SourceFile #23 = Utf8 Test12.java #24 = NameAndType #7:#8 // "<init>":()V #25 = Utf8 a #26 = Utf8 b #27 = Utf8 ab #28 = Utf8 com/_12306/SellTickets/Test12 #29 = Utf8 java/lang/Object { public com._12306.SellTickets.Test12(); 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 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/_12306/SellTickets/Test12; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: 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 6: 0 line 7: 3 line 8: 6 line 9: 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; } SourceFile: "Test12.java"
Execute String s1 = "a" in three steps
- Get this literal from the pool of run constants by instruction
- Getting this literal amount object from a StringTable will not encapsulate it into an object and put it into a StringTable
- Place a reference to variable s1 in the member variable table
Stringtable (string pool) jdk1.8 is stored in the heap (shown in the method area), the bottom is a hash table, and the size is fixed and cannot be expanded.
Is Eg1:String s3 = s1 + s2 equal to String s4 = ab
package com._12306.SellTickets; public class Test12 { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; /** * 9: new #5 // class java/lang/StringBuilder Create a new StringBuilder object * 12: dup * 13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V Initialize StringBuilder * 16: aload_1 // Loading local variable variable variable s1 (get reference address) * 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Do stitching sb.append("a") * 20: aload_2 // Load local variable variable s2 (get reference address) * 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Do stitching sb.append("b") * * * 24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; Call StringBuilder's toString() method to create a new String object in the heap * 27: astore 4 // Write back variable s4 to local variable table */ String s4 = s1 + s2; //s4 is an object in the heap and s3 is an object in a StringTable System.out.println(s3 == s4); //false } }
Unequal
String s3 = s1 + s2 did two variable splicing operations with StringBuilder, and created an "ab" object in heap memory.
String s4 = "ab" creates an object in the constant pool
Address references differ between the two
Eg2:String s3 = "a b" is equal to String s4 = "a"+"b"
Equally, when the java compiler compiles, it knows that these two constants are spliced by default, and then goes to the constant pool to find objects.
Simpler, don't show it here
Eg3: Use the Intern() method to put a String in the heap into a StringTable
package com._12306.SellTickets; public class Test13 { public static void main(String[] args) { /** * 0: new #2 // class java/lang/StringBuilder Create StringBuilder Object * 3: dup * 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V Initialize StringBuilder * 7: new #4 // class java/lang/String Create String Object * 10: dup * 11: ldc #5 // String a Load literal a in constant pool, if not created in string pool * 13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V Initialize String * 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Object a in Split String Pool * 19: ldc #8 // String b Load literal b in constant pool, if not created in string pool * 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Split object b in string pool * 24: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; Converts a StringBuilder object to a String (created in the heap) * 27: astore_1 Loading variable s into member variable table */ String s = new String("a") + "b"; /** * 28: aload_1 Loading variables s from the member variable table * 29: invokevirtual #10 // Method java/lang/String.intern:()Ljava/lang/String; Call the intern() method in String * 32: astore_2 Loading variable s2 into member variable table */ String s2 = s.intern(); //Attempts to put this string object into the string pool, but does not if there is one. If not, returns the object in the string pool System.out.println(s2 == s|| s == "ab"); //The string object AB is now placed in the constant pool, and what is taken from the constant pool is also put in (that is, it is not necessary to take the "ab" string from the heap according to the address reference, but from the constant pool when you take it again) } }
The variable s in the example originally refers to the string object "ab" in heap memory and is now placed in the constant pool, where it has been transformed into an element in the constant pool when retrieved again.
Eg4:String s = new String("ab")+"c" created several String objects
Three, ab, c in StringTable, abc in heap;
Some people will say four, and the fourth should refer to the "abc" in StringTable.
I'll prove why there are three
package com._12306.SellTickets; public class Test12 { public static void main(String[] args) { /** * 0: new #2 // class java/lang/StringBuilder Create StringBuilder object in heap * 3: dup * 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V Call StringBuilder initialization method * 7: new #4 // class java/lang/String Create String Object * 10: dup * 11: ldc #5 // String ab Load the string object ab in the constant pool by instructions, or create the ab in the first constant pool if not * 13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V Initialize String Object * 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Call sb's append() method * 19: ldc #8 // String c Load the string object c in the constant pool, or create it if not, c in the second constant pool * 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; Do stitching operation, abc after stitching * 24: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; StringBuilder To String, a new String object abc is created in the heap. * 27: astore_1 * 28: return */ String s = new String("ab") + "c"; // s = new String("abc"); String intern = s.intern(); //If you don't put it in, it means that there was an object "abc" in the string pool, and s == "abc" is false, then I'm wrong. If you put it in, it means that there were no objects "abc" in the string pool before, that is, three objects were generated. System.out.println(s == "abc"); } }
performance tuning
Scenario 1: Read all the data from a varchar field in the table from the database and return to the page.
package com._12306.SellTickets; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; /** * -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000 */ public class Test17 { public static void main(String[] args) throws IOException { List<String> address = new ArrayList<>(); //Simulate database read string System.in.read(); //Click on the space to start reading for (int i = 0; i < 10; i++) { //Read 10 times try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) { String line = null; long start = System.nanoTime(); //Record start time while (true) { line = reader.readLine(); if (line == null) { break; } address.add(line); //Add the words you read to the list collection (10 iterations, 9 iterations of data are duplicated, creating String objects in the heap and consuming memory) } System.out.println("cost:" + (System.nanoTime() - start) / 1000000); //computing time } } System.in.read(); } }
Press the space and start reading the file because the object has references and cannot be reclaimed by GC. Memory will soon be full
Modify the code to add string objects to the string pool
package cn.itcast.jvm.t1.stringtable; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; /** * Demonstrate intern to 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.intern()); //Attempting to add a string to a StringTable } System.out.println("cost:" +(System.nanoTime()-start)/1000000); } } System.in.read(); } }
50% memory usage
String objects in a string pool cannot be duplicated. The rest of the objects that are not joined to the string pool will be recycled by the GC.
Scenario 2: The impact of string pool size on Performance
Set the number of hash buckets for the string pool with VM parameter: -XX:StringTableSize=1009 (minimum 1009)
Eg
package com._12306.SellTickets; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; /** * Demonstrate the impact of string pool size on Performance * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009 */ public class Test16 { 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); } } }
Modify VM parameter settings -XX:StringTableSize=200000 The number of hash buckets to set the string pool is 200,000
The fewer hash buckets there are, the greater the probability of hash collisions. A hash bucket needs to mount multiple nodes. When a new string object is added, it traverses through the hash bucket one by one, consuming time.
The more hashes there are, the more scattered the string objects are, and the hash value makes it easy to know if the element is in the string pool.
However, it consumes memory. There is a trade-off between space and time.