catalogue
Stringtable (string constant pool)
heap
definition
- Through the new keyword, the storage area of the created object
- It is the largest area of memory managed by the virtual machine to store object instances.
characteristic
- It is an area shared by threads. All objects in the heap need to consider thread safety
- There is a garbage collection mechanism
- It can be implemented as either fixed size or extended. The mainstream is through - Xmx and - Xms(-Xms). The initial size of heap memory is 1 / 64 of physical memory by default, and the maximum size of - Xmx heap memory is 1 / 4 of physical memory by default
)Configure.
Method area
definition
- Like Java heap, it is a memory area shared by each thread, which is used to store type information, constants, static variables, code cache compiled by the real-time compiler and other data that have been loaded by the virtual machine.
-
The method is logically a part of the heap. The specific implementation is different. Before HotSpot virtual machine 1.8, its specific implementation is permanently replaced in the JVM memory. After 1.8 and 1.8, its implementation meta space is in the local memory (in the operating system)
characteristic
- The method area is created at virtual startup
- The method area is logically a part of the heap, but different versions of JVM have different alignment implementations, such as permanent generation (implementation before jdk8, using heap memory) and meta space (implementation of jdk8, using operating system memory)
- When the method area cannot meet the new memory allocation requirements, an OOM exception will be thrown (the above situation will be triggered by dynamically generating JSP files and constantly creating new strings)
Runtime Constant Pool
Definition
The runtime constant pool is a part of the method area. In addition to the Class version, bytes, methods, interfaces and other description information, there is also a constant pool table in the Class file, which is used to store various literal quantities and symbol references generated during compilation. This part will be stored in the runtime constant pool of the method area after the Class is loaded. And change the symbolic address into the real address.
- When running in 1.6, the constant pool table is placed in the permanent generation
- In 1.8, the runtime constant pool was moved into heap memory
Let's first understand the constant pool under binary bytecode
public class HelloWorld { public static void main(String[] args) { System.out.println("hello world"); } }
Execute javap - V HelloWorld Class to get the following bytecode instructions
Classfile /Users/lizhijian/IdeaProjects/Demo/out/production/Demo/jvm/HelloWorld.class Last modified 2021-6-16; size 541 bytes MD5 checksum 2857962a7a60aa95f2f6642d6b02afad Compiled from "HelloWorld.java" public class jvm.HelloWorld minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#20 // java/lang/Object."<init>":()V // (2) Get #21#22 pointing information #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #23 / / hello world (6) points to hello world #4 = Methodref #24.#25 // (9) java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #26 // jvm/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 Ljvm/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 (3) #22 = NameAndType #29:#30 // out:Ljava/io/PrintStream; (4) #23 = Utf8 hello world //(7) #24 = Class #31 // (10)java/io/PrintStream #25 = NameAndType #32:#33 // (11)println:(Ljava/lang/String;)V #26 = Utf8 jvm/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 jvm.HelloWorld(); 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 9: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Ljvm/HelloWorld; 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 // (1) Gets the constant number 2 in the constant pool 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; // (5) Loads the #3 pointed constant into the operand stack 3: ldc #3 // String hello world 5: invokevirtual #4 // (8)Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 11: 0 line 12: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "HelloWorld.java"
You can see that the part below the Constant pool is a Constant pool, which is a table. The virtual machine instruction finds the class name, method name, parameter type, literal and other information to be executed according to this constant table. (1) ~ (11) is the process of loading Constant pool data.
Runtime constant pool: when the class is loaded, the constant pool information of the class will be put into the runtime constant pool, and the symbolic address (#1, #2) in it will be replaced with the real address.
Stringtable (string constant pool)
Decompile the following code
public class Demo1_22 { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; } }
Compiled bytecode:
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 load a from constant pool #2 number 2: astore_1 // Store in slot 1 of LocalVariableTable 3: ldc #3 // String b 5: astore_2 6: ldc #4 // String ab 8: astore_3 9: return LineNumberTable: line 12: 0 line 13: 3 line 14: 6 line 15: 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;
The information in the constant pool is loaded into the runtime constant pool. At this time, a , b , ab symbols have not yet become string objects. Wait until the sentence String s1 = "a" is executed, and then change the a symbol into "a" string. The process is as follows:
ldc #2 will change the a symbol into "a" string (lazy loading), and find out whether there is the same string in the quasi good stringTable []. If not, put it into the string pool.
String variable splicing
Let's take another demo
//StringTable["a", "b", "ab"] public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; String s4 = s1 + s2; //new StringBuilder().append(s1).append(s2).toString(); System.out.println(s3 == s4); }
Compiled bytecode:
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=5, args_size=1 0: ldc #2 // String a // Store a in the local variable table 2: astore_1 3: ldc #3 // String b 5: astore_2 6: ldc #4 // String ab 8: astore_3 // Create a StringBuilder object 9: new #5 // class java/lang/StringBuilder 12: dup // Call the constructor of StringBuilder 13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V // Load element at position 1 of local variable table 16: aload_1 // Call the append method in the local variable table 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_2 // Call the append method in the local variable table 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Call the toString method in the local variable table 24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; // Store the toString result in the local variable table No. 4 27: astore 4 29: return LineNumberTable: line 12: 0 line 13: 3 line 14: 6 line 15: 9 line 16: 29 LocalVariableTable: Start Length Slot Name Signature 0 30 0 args [Ljava/lang/String; 3 27 1 s1 Ljava/lang/String; 6 24 2 s2 Ljava/lang/String; 9 21 3 s3 Ljava/lang/String; 29 1 4 s4 Ljava/lang/String;
You can see that string S4 = S1 + S2; < = = > new StringBuilder().append(s1).append(s2).toString();
The source code of toString() is
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
Create a new string in the heap. To sum up, system out. println(s3 == s4); Output false
String compiler optimization
String s1 = "a"; String s2 = "b"; String s3 = "ab"; String s4 = s1 + s2; String s5 = "a" +"b";
Because String s3 = "ab" has been created in the string pool (S3 goes to the constant pool to load, and after loading, "ab" is added to StringTable []), the jvm thinks that "a" and "b" are constants and will not change, so it makes optimization during compilation to make String s5 = "a" + "b", directly find the "ab" string in the string pool without re creating the object. System out. println(s3 == s5) <==> true
lazy instantiation
public static void main(String[] args) { System.out.println("1"); System.out.println("2"); System.out.println("3"); System.out.println("4"); System.out.println("5"); System.out.println("6"); System.out.println("7"); System.out.println("8"); System.out.println("9"); System.out.println("1"); System.out.println("2"); System.out.println("3"); }
As can be seen from the above figure, with the execution of the code, the number of corresponding strings in StringTable has been increasing until system out. Println ("9") is finished. When the second execution of system out. println("1"); The number of times does not increase because "1" ~ "9" has been generated in the string pool.
intern() method
concept
The function of intern() is to put this string object into the string pool. If it exists, it will not be put into the string pool. If it does not exist, it will be put into the string pool, and the objects in the string pool will be returned (note here that the string pool objects will be returned regardless of whether they exist in the string pool before)
- 1.8 try to put this string object into the string pool. If there is one, it will not be put in. If there is no one, it will be put into the string pool and the objects in the string pool will be returned
- 1.6 try to put the string object into the string pool. If there is one, it will not be put in. If there is no one, it will copy the object and put it into the string pool, and the object in the string pool will be returned
1.8 look at some 🌰:
- demo1
public static void main(String[] args) { // "A" in new String("a") means that constants are put into the string pool and "b" means that constants are put into the string pool // new String("a") generates a new object in the heap String s1 = new String("a") + new String("b"); // Because it is dynamically spliced, "ab" is not put into the string pool String s2 = new String("a") + new String("b"); System.out.println(s1 == "ab"); //false System.out.println(s2.intern() == "ab"); // true }
When new String("a") is executed, "A" will be put into the string pool and new objects will be generated in the heap. The same is true for new String("b"). However, when String s1 = new String("a") + new String("b"); is executed, because it is dynamically spliced, "ab" will not be put into the string pool and objects will only be created in the heap. Therefore, S1 = = "ab" outputs false, while s2.intern() returns string pool objects, so s2.intern() = " AB "output true.
- demo2
public static void main(String[] args) { String s = new String("a") + new String("b");// Heap object String s2 = s.intern(); // "ab" does not exist in the string pool. Put the s object into the string pool and assign the result to s2 String x = "ab"; System.out.println(s2 == "ab"); // true System.out.println(s == x); // true } --output true true
Execute to String s2 = s.intern(); If it is found that there is no "ab" in the string pool, create "ab" in the string pool. Note: it needs to be understood that the s object is put into the string pool!!!!!!!!!!! You can see that s2 gets the address of "ab" in the string pool, so it outputs true, and the "ab" in the string pool itself is the address of S, so s==x(ab) outputs true.
- demo3
If "ab" is created first, the output is as follows.
public static void main(String[] args) { String x = "ab"; // String pool object String s = new String("a") + new String("b");// Heap object String s2 = s.intern(); // "ab" already exists in the string pool, and s.intern() returns the string pool object System.out.println(s2 == x); // true System.out.println(s == x); // false } --output true fasle
You can see that the variable s returns the address in the heap, while s.intern() returns the address of the string pool, that is, s==x outputs false (because "ab" in the string pool does not depend on the variable s when it is initially generated, so s is not directly related to "ab"), and s2==x outputs true.
1.6 lower 🌰:
- demo1
Create string first
public static void main(String[] args) { String x = "ab"; String s = new String("a") + new String("b");// Heap object String s2 = s.intern(); System.out.println(s2 == x); System.out.println(s == x); } --output true false
”AB "create in the string pool first, s2 = s.intern(), and return the string pool address for s2. S itself points to the heap (the string pool already has" ab ", that is, it does not need to rely on s to create it again). Therefore, s2==x outputs true and s = = x outputs false.
- demo2
Create after string
public static void main(String[] args) { String s = new String("a") + new String("b");// Heap object String s2 = s.intern(); // A copy of s.intern() in 1.6 is put into the string pool. It is understood that the creation of "ab" does not directly depend on S String x = "ab"; System.out.println(s2 == x); System.out.println(s == x); } -- output true false
It can be analyzed from the code that s2=s.intern() returns the string pool object, so s2==x outputs true. S.intern() copies a copy of "ab" to the string pool, that is, "ab" does not directly depend on S, so s==x outputs false.
Summary:
- In 1.6 / 1.8, s.intern() returns string pool objects
- Whether the string variable s itself points to "ab" in the string pool depends on whether the creation of "ab" depends on S.
Direct memory
Application scenario
- Common in NIO operations, it is used for data buffer
- Allocation recovery cost is high, but read-write performance is high
- Not managed by JVM memory reclamation
- Traditional file operation
The operation file code is as follows:
static final String FROM = "E:\\xx.mp4"; static final String TO = "E:\\a.mp4"; static final int _1Mb = 1024 * 1024; public static void main(String[] args) { io(); // io time: 1535.586957 1766.963399 1359.240226 directBuffer(); // directBuffer time: 479.295165 702.291454 562.56592 } private static void directBuffer() { long start = System.nanoTime(); try (FileChannel from = new FileInputStream(FROM).getChannel(); FileChannel to = new FileOutputStream(TO).getChannel(); ) { ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb); while (true) { int len = from.read(bb); if (len == -1) { break; } bb.flip(); to.write(bb); bb.clear(); } } catch (IOException e) { e.printStackTrace(); } long end = System.nanoTime(); System.out.println("directBuffer Time:" + (end - start) / 1000_000.0); } private static void io() { long start = System.nanoTime(); try (FileInputStream from = new FileInputStream(FROM); FileOutputStream to = new FileOutputStream(TO); ) { byte[] buf = new byte[_1Mb]; while (true) { int len = from.read(buf); if (len == -1) { break; } to.write(buf, 0, len); } } catch (IOException e) { e.printStackTrace(); } long end = System.nanoTime(); System.out.println("io Time:" + (end - start) / 1000_000.0); } --output io Time: 2921.12 directBuffer Time: 892.55
It can be seen that the performance of direct memory is much higher than that of traditional IO
Principle analysis
- Traditional file operation
The traditional file operation will first load the data from the disk into the system memory, and then load the data from the system memory into the buffer of the heap memory.
- Using direct memory
As shown in the figure above, direct memory is used to open up a space in the disk, so that the system memory and Java heap memory can be accessed directly, which greatly improves the file reading and writing performance.
Reference: understanding the Java virtual machine