catalogue
StringTable garbage collection
Distribution release principle
Overall architecture diagram
Program Counter Register
effect
Remember the execution address of the next JVM instruction (corresponding to the following number)
When the JVM executes 0, it puts address 3 into the program counter
The program counter is implemented using the registers of the operating system
The CPU cannot directly execute these binary bytecodes. These binary bytecodes need to be converted into machine codes through the interpreter before they can be executed by the CPU
0: getstatic #20 // PrintStream out = System.out; 3: astore_1 // -- 4: aload_1 // out.println(1); 5: iconst_1 // -- 6: invokevirtual #26 // -- 9: aload_1 // out.println(2); 10: iconst_2 // -- 11: invokevirtual #26 // -- 14: aload_1 // out.println(3); 15: iconst_3 // -- 16: invokevirtual #26 // -- 19: aload_1 // out.println(4); 20: iconst_4 // -- 21: invokevirtual #26 // -- 24: aload_1 // out.println(5); 25: iconst_5 // -- 26: invokevirtual #26 // -- 29: return
characteristic
-
Thread private
-
There will be no memory overflow
Java virtual machine stacks
definition
-
The memory required for each thread to run is called the virtual machine stack
-
Each stack consists of multiple stack frames, corresponding to the memory occupied by each method call
-
Each thread can only have one active stack frame, corresponding to the method currently executing
Problem Formulation
-
Does garbage collection involve stack memory?
No, there is no garbage collection in the virtual machine stack.
-
Is the larger the stack memory allocation, the better?
No, it's better to use the default. The larger the stack memory allocation, it can only increase the number of recursive calls of this method (that is, increase the number of stack frames of this stack), which does not improve the execution efficiency of the method, but will reduce the number of multithreads.
-
Are local variables in methods thread safe?
If the local variable in the method does not escape the scope of the method, it is thread safe;
If a local variable references an object and escapes the scope of a method, thread safety needs to be considered.
Stack memory overflow
- Excessive stack frames lead to stack memory overflow (recursion, using third-party class libraries)
public class Demo01 { private static int count; public static void main(String[] args) { method01(); } private static void method01() { count++; method01(); } }
result:
two Stack memory overflow caused by too large stack frame
Thread run diagnostics
Case 1: excessive CPU usage
location
-
top command to see which process is occupying too much CPU
-
ps H -eo pid, tid (thread id),% cpu | grep the process number just found through top. Use the ps command to further check which thread occupies too much CPU
-
Jstack process id is located by comparing the nid of the thread in the process with the tid just seen through the ps command. Note that the thread id found by jstack is hexadecimal and needs to be converted.
Native Method Stack
definition
Some methods with native keywords require java to call local C or C + + methods. Sometimes Java cannot directly interact with the underlying operating system, so it needs to use the local method stack to serve the methods with native keywords.
Heap
definition
Using the new keyword, heap memory is used when creating objects
characteristic
-
Thread sharing and heap objects need to consider thread safety
-
There is a garbage collection mechanism
Heap memory overflow
You can use - Xmx8m to specify the size of heap memory
give an example:
public class Demo01 { public static void main(String[] args) { int i = 0; List<String> list = new ArrayList<>(); String str = "hello"; while (true) { list.add(str); str = str + str; } } }
result:
Heap memory diagnostics
jps tools
Check which java processes are in the current system
jmap tool
View heap memory usage jmap - heap process id
jconsole tool
Graphical interface, multi-functional monitoring tool, which can monitor continuously
Visual JVM tools
Method area
definition
The Java virtual machine has a method area that is shared among all Java virtual machine threads. The method area is similar to the storage area of compiled code for traditional languages, or similar to the "text" section in the operating system process. It stores the structure of each class, such as runtime constant pool, field and method data, as well as the code of methods and constructors, including special methods for class and instance initialization and interface initialization. The method area is created when the virtual machine is started. Although the method area is logically part of the heap, a simple implementation may not choose to garbage collect or compress it. This specification does not enforce the location of the method area or the policy used to manage compiled code. The method area can have a fixed size, or can be expanded according to the needs of calculation, and can be shrunk if a larger method area is not required. The memory of the method area does not need to be continuous!
form
Method area memory overflow
When the program loads too many classes during running, it may lead to memory overflow in the method area
-
Permanent generation memory overflow before 1.8 (java.lang.OutOfMemoryError: PermGen space)
-
Use - XX:MaxPermSize=8m to specify the permanent generation memory size
-
-
Meta space memory overflow will occur after 1.8 (java.lang.OutOfMemoryError: Metaspace)
-
Use - XX:MaxMetaspaceSize=8m to specify the Metaspace size
-
Runtime Constant Pool
Binary bytecode includes: basic class information, constant pool, class method definition, and virtual machine instructions
Before learning about the runtime constant pool, let's learn about the constant pool through an example
public class Demo01 { public static void main(String[] args) { System.out.println("Hello World!"); } }
After the above program is compiled, use the javap -v Demo01.class command on the console to decompile and view the bytecode file:
public class jvm.metaspace.Demo01 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 // jvm/metaspace/Demo01 #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/metaspace/Demo01; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 Demo01.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 jvm/metaspace/Demo01 #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.metaspace.Demo01(); 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 Ljvm/metaspace/Demo01; 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 5: 0 line 6: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "Demo01.java"
Constant pool: it is a table from which the virtual machine instruction finds the class name, method name, parameter type and literal information to be executed
Runtime constant pool: the constant pool is in the *. Class file. After the class is loaded, its constant pool information will be put into the runtime constant pool, and the symbolic address in it will be changed into the real address
StringTable
Program code
public class Demo01 { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "c"; } }
Part of the compiled binary bytecode file
Constant pool
Bytecode
Local variable table
analysis
The information in the constant pool will be loaded into the runtime constant pool. At this time, a, B and C are symbols in the constant pool and have not been changed into java string objects
ldc # two // Load it from the value "a" at position 2 in the constant pool. At this time, the symbol a becomes the string object "a" (put it into the string pool, do not put it if there is a string pool, and the string objects in the string pool are unique)
astore_ one // Place "a" in the local variable table where the subscript is 1
ps: lazy loading is adopted, that is, this string object will be created only when this line of bytecode is run
Topic 1
public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; String str = s1 + s2; System.out.println(s3 == str); // String s = "a" + "b" / / compile time optimization < = > "ab" }
code analysis
- The string s3 is stored in the string pool.
- (S1 + S2) < = > (New stringbuilder(). Apend ("a"). Apend ("B"). Tostring(), this tostring() < = > new string ("ab")), so this str exists in the heap.
- Normally, new String() will first create a string object in the heap, and then go to the string pool to see if there is a string with the same content. If there is one, you don't need to create it. If not, a copy will also be stored in the string pool. However, the toString() method of StringBuilder is a little special. It will only be created in the heap, not in the string pool.
Conclusion: the answer is: false, because one is in the string pool and the other is in the heap. It must be different.
Bytecode analysis
StringTable attribute
-
The string in the constant pool is only a symbol and becomes an object when it is used for the first time
-
The string pool mechanism is used to avoid repeated creation of string objects
-
The principle of string variable splicing is StringBuilder (1.8)
-
The principle of string constant splicing is compile time optimization
-
You can use the intern method to actively put string objects that are not in the string pool into the string pool
public class Demo01 { public static void main(String[] args) { String s1 = new String("a") + new String("b"); String s2 = s1.intern(); System.out.println(s1 == s2); // JDK 1.7: true JDK 1.6: false } } analysis: new String("a") + new String("b") A total of 6 objects are created as follows: (1) Create a in the heap StringBuilder object (2) Creates a string object in the heap"a" (3) Create a string object in the string pool"a" (4) call append Method will "a" Splice to StringBuilder in (5) Creates a string object in the heap"b" (6) Create a string object in the string pool"b" (7) call append Method will "a" Splice to StringBuilder in (8) call toString Methods the spliced "ab" Create in heap(new String("ab")) here s1 Points to the heap"ab"Address of(There are no strings in the pool"ab"String object),s1.intern()Is to put the"ab"Create a copy in the string pool. As above, if it exists, the reference address will be returned. If it does not exist, create a copy, and then return the reference address. It should be noted that in JDK 1.6 The string pool is still in the constant pool of the permanent generation s1.intern() Is to create objects directly in the string pool s1,And in JDK 1.7 After that, the string pool is placed in the heap, so s1.intern() Is stored in the heap in the string pool s1 Without creating an object.
StringTable location
In JDK 1.6, the string pool is in the constant pool of the permanent generation, while after JDK 1.7, the string pool is placed in the heap.
Reason: the recovery efficiency of the permanent generation is very low, which can only be triggered in FULLGC, while StringTable is used frequently. If the recovery efficiency is low, it is easy to lead to insufficient memory of the permanent generation.
StringTable garbage collection
-Xmx10m specifies the heap memory size
-20: + printstringtablestatistics print string constant pool information
-XX:+PrintGCDetails
-Verbose: the number of times gc prints, time-consuming and other information
StringTable tuning
-
Adjust - XX:StringTableSize = number of barrels
If a large number of strings need to be stored in the program, the number of buckets (string pool is stored in hash table) can be modified according to the actual situation. Increasing the number of buckets can reduce hash conflict and improve string storage efficiency
-
Consider pooling string objects
If a large number of strings need to be stored in the program, and many of these strings are repeated, these strings can be stored in the pool to reduce the occupation of memory
Direct memory
definition
Direct Memory
-
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
benefit
Before using direct memory:
analysis:
When the program reads the disk file information, it needs to divide a system cache in the system memory for storage, while the Java program cannot directly read this system cache, so it needs to divide a Java buffer (byte []) in the Java heap memory to read the information of the system cache. Only then can the Java program read the disk file information.
In this way, it is necessary to divide two memory areas (system buffer and Java buffer), resulting in unnecessary memory replication and low efficiency
After using direct memory, it is as follows:
analysis:
Direct memory is an area that can be accessed by both the operating system and Java code. There is no need to copy the code from the system memory to the Java heap memory, which improves the efficiency.
Direct memory overflow
public class Demo01 { public static int _100Mb = 1024 * 1024 * 100; public static void main(String[] args) { List<ByteBuffer> list = new ArrayList<>(); int i = 0; try { while (true) { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb); list.add(byteBuffer); i++; } } finally { System.out.println(i); } } }
Operation results:
Distribution release principle
The allocation and release of direct memory is based on unsafe objects, not garbage collection
public class Demo01 { public static int _1GB = 1024 * 1024 * 1024; public static void main(String[] args) throws Exception { Unsafe unsafe = getUnsafe(); // Allocate memory long base = unsafe.allocateMemory(_1GB); unsafe.setMemory(base, _1GB, (byte) 0); System.in.read(); // Free memory unsafe.freeMemory(base); System.in.read(); } /** * Get unsafe objects through reflection */ public static Unsafe getUnsafe() throws Exception { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } }
summary
-
The Unsafe class is used to complete the allocation and recycling of direct memory, and the freeMemory method needs to be actively called for recycling
-
The implementation of ByteBuffer internally uses Cleaner (Virtual Reference) to detect ByteBuffer. Once ByteBuffer is garbage collected, ReferenceHandler (daemon thread) will call the clean method of Cleaner and call freeMemory to free memory
-20: + disableexplicitgc / / disabled GC, that is, System.gc() is invalid
System.gc() executes full gc and takes a long time to recycle, so you can release direct memory by calling freeMemory through the unsafe object