Java development - detailed explanation of JVM optimization that you don't know

foreword

Once the code optimization has reached a certain level, it will be very difficult to improve the performance of the system. At this time, excellent programmers often start with the JVM to optimize the system. But having said that, the optimization of the JVM is also relatively dangerous. It is not meaningful to optimize the JVM only from the test server. Different servers have different access traffic even if the environment is the same. Therefore, the general JVM tuning Optimizing is a real production environment, and it is necessary to bear unpredictable risks. Therefore, JVM tuning does not mean that anyone can do it. It often requires the cooperation of various departments in the early stage to conduct a large-scale discussion before starting to practice, and at the same time do a good job in various Risk assessment and remedial measures, below, the blogger will tell you about JVM tuning from several dimensions.

JVM

Reasons for JVM optimization

As mentioned above, JVM tuning must occur when the system performance cannot be improved through code, otherwise, most developers will never be idle and have nothing to do to such a thankless death. The bottleneck of the system will cause the system to be stuck, the log will not output, and the load will increase abnormally. After various analysis, it cannot be effectively solved from the code. Experienced programmers will know that it must be some parameters of the JVM There is a problem with the setting, but the setting of the parameters does not mean that every company uses exactly the same parameters. It needs to be analyzed according to some conditions of its own system to get a relatively suitable value.

This is very difficult. Maybe most people have been developing for many years, and they don’t know exactly what the JVM parameters are. Bloggers are the same as everyone. Aside from this part of the blog content, who will look at those system parameters with default values? But today, we have to understand these parameters, just because when we have to do it, I will give you a possibly useful tutorial, so that you can optimize your JVM as much as possible.

What are the operating parameters of the JVM

Speaking of the parameters of the JVM, there are too many, which is a bit annoying, but you have to know that not all parameters are suitable for us to tune. Knowing these, you can continue to read patiently.

JVM parameters are roughly divided into three types:

  • standard parameters
  • -X parameters (non-standard parameters)
  • -XX parameters (also considered non-standard parameters, such parameters are used more frequently)

standard parameters

Viewing standard parameters is very simple, we open the command line tool in a java project:

enter:

java -help

Codeliu@bogon cache % java -help 
    usage: java [-options] class [args...] 
    (execution class) 
    or java [-options] -jar jarfile [args...] 
    (execution jar file) 
    where options include: 
    -d32 use 32-bit data model (if available) 
    -d64 use 64-bit data model (if available) 
    -server Select "server" VM 
    The default VM is server 
    because you are running on a server class computer. 


    -cp <class search path for directories and zip/jar files> 
    -classpath <class search path for directories and zip/jar files> 
    Use: separated 
    list of directories, JAR archives and ZIP archives to search for class files. 
    -D <name>=<value> 
              set system property 
                      -verbose:[class|gc|jni] 
    enable verbose output 
    -version output product version and exit 
    -version:<value>
              WARNING: This feature is deprecated and will be 
                      removed in a future release. 
                      The specified version is required to run 
                      -showversion output product version and continue 
                      -jre-restrict-search | -no-jre-restrict-search 
                      WARNING: This feature is deprecated and will be 
                      removed in a future release. 
                      Include/exclude user-specific JREs in version search 
                      -? -help output this help message 
                      -X output help for non-standard options 
                      -ea[:<packagename>...|:<classname>] 
    -enableassertions[:<packagename>. ..|:<classname>] 
    enable assertions at specified granularity 
    -da[:<packagename>...|:<classname>] 
    -disableassertions[:<packagename>...|:<classname>
    -esa | -enablesystemassertions 
    -dsa | -disablesystemassertions 
    disable system assertions 
    -agentlib:<libname>[=<options>] 
    Load native agent library <libname>, eg -agentlib:hprof 
    See also -agentlib:jdwp=help and - agentlib:hprof=help 
    -agentpath:<pathname>[=<option>] 
    load native agent library by full pathname 
    -javaagent:<jarpath>[=<option>] 
    load Java programming language agent, see java.lang. instrument 
    -splash:<imagepath> 
              Display the splash screen using the specified image 
                      For more information, see http://www.oracle.com/technetwork/java/javase/documentation/index.html.

Click output, let's try these output commands now, for example:

java -version

output: 

We can even set system property parameters through -D. Although the blogger thinks that it is not very useful in practice, I still want to mention it to everyone.

First we create a simple class:

package com.codingfire.cache;

import java.util.Arrays;

public class JVMTest {
    public static void main(String[] args) {
        String str = System.getProperty("str");
        if (str == null) {
            System.out.println("CodingFire");
        } else {
            System.out.println(str);
        }
    }
}

Let's do it:

Compile:

javac JVMTest.java 

implement:

java com.codingfire.cache.JVMTest

Change parameters:

java -Dstr=libai com.codingfire.cache.JVMTest

There are a few points to note here. I don’t know about windows, but to compile a java file under mac, you need to enter the directory where the file is located. When running the main class file, you need to be in the java directory. The running path must include the package name, which has been circled in the figure Come out, avoid the pit for everyone.

These are some small usages of JVM standard parameters. According to the experience of bloggers, they are not very useful. You just need to know them. If you really need them, you can check them in time. 

Oh, by the way, there are two slightly more important parameters that need to be mentioned at the end: -server and -client

The initial heap space of the server VM is large, and the parallel garbage collector is used by default, which is slow to start and fast to run

The initial heap space of the client VM is small, and the walk-through garbage collector is used by default, which starts quickly and runs slowly

However, under the 64-bit system, only the server VM can be selected, and the client VM is not supported, so bloggers under the 32-bit system will not talk about it, which is meaningless. How can there be 32-bit now? Therefore, there is no choice, just mention it and know what's going on.

-X parameter

Codeliu@bogon java % java -X 
    -Xmixed mixed mode execution (default) 
    -Xint interpreted mode execution only 
    -Xbootclasspath:<directories and zip/jar files separated by :> 
    set search path for boot classes and resources 
    -Xbootclasspath/a: <directories and zip/jar files separated by:> 
    are appended to the end of the boot classpath 
    -Xbootclasspath/p: <directories and zip/jar files separated by:> 
    are placed before the boot classpath 
    -Xdiag show additional diagnostic messages 
    -Xnoclassgc disable class garbage collection 
    -Xincgc enable incremental garbage collection 
    -Xloggc:<file> log GC status in a file (with timestamp) 
    -Xbatch disable background compilation 
    -Xms<size> set initial Java heap size 
    -Xmx<size> set maximum Java heap size 
    -Xss<size> Set Java thread stack size  
    -Xprof output cpu profile data
    -Xfuture Enable strictest checking, expected future default
    -Xrs reduce Java/VM usage of OS signals (see documentation) 
    -Xcheck:jni perform additional checks on JNI functions 
    -Xshare:off don't try to use shared class data 
    -Xshare:auto use shared class data where possible (default) 
    -Xshare:on Requires use of shared class data, otherwise fails. 
    -XshowSettings show all settings and continue 
    -XshowSettings:all 
    show all settings and continue 
    -XshowSettings:vm show all vm related settings and continue 
    -XshowSettings:properties 
    show all property settings and continue 
    -XshowSettings:locale 
    show all locale related settings set and continue 

    -X options are non-standard options and are subject to change without notice. 


    The following options are Mac OS X specific: 
    -XstartOnFirstThread 
    Run the main() method on the first (AppKit) thread  
    -Xdock:name=<application name>"
    Override the default application name shown in the dock 
    -Xdock:icon=<path to icon file>
                  Override the default icon shown in the dock

It comes out below java -X, but different jvms have different parameter paths.

There are two more important parameters:

-Xms and -Xmx are to set the initial memory and maximum memory of jvm heap memory respectively, such as -Xms512m or -Xmx2048m.

You can also use -X to set parameters for the running program:

Codeliu@bogon java % java -Xms512m -Xmx2048m  com.codingfire.cache.JVMTest
CodingFire

Proper adjustment can make good use of system resources and improve efficiency. 

-XX parameters

-XX is a non-standard parameter for JVM tuning and debugging operations. There are two ways to use it, one is boolean type, and the other is non-boolean type:

  • boolean type
    • Format: -XX:[+-]<name> means enable or disable <name> attribute
    • For example: -XX:+DisableExplicitGC means to disable manual gc operation, that is to say, calling System.gc() is invalid
  • Non-boolean type
    • Format: -XX:<name>=<value> indicates that the value of the attribute <name> is <value>
    • For example: -XX:NewRatio=1 indicates the ratio of the new generation to the old generation

for example:

To view the -XX parameter, you need to print the parameter when running the java command, and add the -XX:+PrintFlagsFinal parameter. There are many parameters, and the computer starts to get stuck, and more than half of them are deleted. Just take a look, mainly because you know that there are basically all It is boolean type and numeric type:

Codeliu@bogon java % java -XX:+PrintFlagsFinal -version
[Global flags]
     intx ActiveProcessorCount                      = -1                                  {product}
    uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
    uintx AdaptiveSizePausePolicy                   = 0                                   {product}
    uintx AdaptiveSizePolicyCollectionCostMargin    = 50                                  {product}
    uintx AdaptiveSizePolicyInitializingSteps       = 20                                  {product}
    uintx AdaptiveSizePolicyOutputInterval          = 0                                   {product}
    uintx AdaptiveSizePolicyWeight                  = 10                                  {product}
    uintx AdaptiveSizeThroughPutPolicy              = 0                                   {product}
    uintx AdaptiveTimeWeight                        = 25                                  {product}
     bool AdjustConcurrency                         = false                               {product}
     bool AggressiveHeap                            = false                               {product}
     bool AggressiveOpts                            = false                               {product}
     intx AliasLevel                                = 3                                   {C2 product}
     bool AlignVector                               = false                               {C2 product}
     intx AllocateInstancePrefetchLines             = 1                                   {product}
     intx AllocatePrefetchDistance                  = 192                                 {product}
     intx AllocatePrefetchInstr                     = 0                                   {product}
     intx AllocatePrefetchLines                     = 4                                   {product}
     intx AllocatePrefetchStepSize                  = 64                                  {product}
     intx AllocatePrefetchStyle                     = 1                                   {product}
     bool AllowJNIEnvProxy                          = false                               {product}
     bool AllowNonVirtualCalls                      = false                               {product}
     bool AllowParallelDefineClass                  = false                               {product}
     bool AllowUserSignalHandlers                   = false                               {product}
     bool AlwaysActAsServerClassMachine             = false                               {product}
     bool AlwaysCompileLoopMethods                  = false                               {product}
     bool AlwaysLockClassLoader                     = false                               {product}
     bool AlwaysPreTouch                            = false                               {product}
     bool AlwaysRestoreFPU                          = false                               {product}
     bool AlwaysTenure                              = false                               {product}
     bool AssertOnSuspendWaitFailure                = false                               {product}
     bool AssumeMP                                  = false                                                               {product}
     bool UseSSE42Intrinsics                        = true                                {product}
     bool UseSerialGC                               = false                               {product}
     bool UseSharedSpaces                           = false                               {product}
     bool UseSignalChaining                         = true                                {product}
     bool UseSquareToLenIntrinsic                   = true                                {C2 product}
     bool UseStoreImmI16                            = false                               {ARCH product}
     bool UseStringDeduplication                    = false                               {product}
     bool UseSuperWord                              = true                                {C2 product}
     bool UseTLAB                                   = true                                {pd product}
     bool UseThreadPriorities                       = true                                {pd product}
     bool UseTypeProfile                            = true                                {product}
     bool UseTypeSpeculation                        = true                                {C2 product}
     bool UseUnalignedLoadStores                    = true                                {ARCH product}
     bool UseVMInterruptibleIO                      = false                               {product}
     bool UseXMMForArrayCopy                        = true                                {product}
     bool UseXmmI2D                                 = false                               {ARCH product}
     bool UseXmmI2F                                 = false                               {ARCH product}
     bool UseXmmLoadAndClearUpper                   = true                                {ARCH product}
     bool UseXmmRegToRegMoveAll                     = true                                {ARCH product}
     bool VMThreadHintNoPreempt                     = false                               {product}
     intx VMThreadPriority                          = -1                                  {product}
     intx VMThreadStackSize                         = 1024                                {pd product}
     intx ValueMapInitialSize                       = 11                                  {C1 product}
     intx ValueMapMaxLoopSize                       = 8                                   {C1 product}
     intx ValueSearchLimit                          = 1000                                {C2 product}
     bool VerifyMergedCPBytecodes                   = true                                {product}
     bool VerifySharedSpaces                        = false                               {product}
     intx WorkAroundNPTLTimedWaitHang               = 1                                   {product}
    uintx YoungGenerationSizeIncrement              = 20                                  {product}
    uintx YoungGenerationSizeSupplement             = 80                                  {product}
    uintx YoungGenerationSizeSupplementDecay        = 8                                   {product}
    uintx YoungPLABSize                             = 4096                                {product}
     bool ZeroTLAB                                  = false                               {product}
     intx hashCode                                  = 5                                   {product}
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

Also, = represents the default value, := represents the modified value, which has not been modified here, so there is no, but everyone should know.

To view the running JVM parameters, first run a project, and then in the command line tool:

    #View the java process via jps or jps -l 
    Codeliu@bogon java % jps 
    53700 RemoteMavenServer36 
    62645 Jps 
    51542 
    62634 WorkStealingPoolDemo 
    Codeliu@bogon java % jinfo -flags 62634 
    Attaching to process ID 62634, please wait ... 
    Debugger attached successfully. 
    Server compiler detected .JVM 
    version is 25.101-b13 
    Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=715653120 -XX:MinHeapDeltaBytes=52 4288 -XX:NewSize=44564480 - XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
    Command line:  -Dfile.encoding=UTF-8
    #Check the value of a parameter, usage: jinfo -flag <parameter name> <process id> 
    Codeliu@bogon java % jinfo -flag MaxHeapSize 62634 
    -XX:MaxHeapSize=2147483648

 Here we are looking at the process id of the project just started:

Bloggers in this piece of content have no way to tell you exactly how much to change. You have to consider it according to your own system. The blogger mainly guides everyone to understand these parameters and how to operate them. 

JVM memory model

For the memory model of the JVM, we will explain it for JDK7 and JDK8.

I'm too lazy to draw by myself, so I borrowed two pictures. Basically, everyone draws such a structure, and the internal explanation of the knowledge is slightly different.

Virtual machine stack: This is private to the thread, and has the same life cycle as the thread. It saves local variables when executing methods, dynamic connection information (actually calling other methods), method return information, and so on. When the method starts to execute, it will be pushed into the stack, and when the method is executed, it will be popped out of the stack without GC.

Native method stack: Native method stacks (Native Method Stacks) are very similar to virtual machine stacks. The difference is that the virtual machine stack serves the virtual machine to execute Java methods (that is, bytecodes), while the native method stack It serves the local (Native) method used by the virtual machine.

Program Counter: This is the line number of the bytecode that is private to the thread and is stored internally. If the thread is suspended and then continues to execute, this is a useful means of finding the execution position.

Local memory/direct memory: Local memory is also called off-heap memory . It is an area shared by threads and is not controlled by the JVM, so GC will not occur. It is a piece of physical memory dedicated to the JVM and IO devices. The bottom layer of Java Use the API of C language to call the operating system to interact with the IO device. Therefore, the execution efficiency of the entire java is greatly improved.

Heap: An area shared by threads, mainly used to save object instances, arrays, etc. If there is no memory space in the heap to allocate to instances and cannot be expanded, an OOM exception will be thrown.

Young generation: It is divided into three parts, Eden area and two Survivor areas of the same size. At the same time, only one of them is used, and the other is reserved for copying objects during garbage collection. This is the copy algorithm. When the Eden area is full, the GC will move the surviving objects to the empty Survivor area. When it is full for the second time, it will transfer the surviving objects in Eden and Survivor to another Survivor area, and then clear the first one. The next Survivor area, and so on, and every time you enter a Uurvivor area, the age of the object will be +1, and when you are 15 years old, you will enter the old age. If the object is very large when it is created, it goes directly to the old generation.

Old generation: It mainly saves objects with a long life cycle, generally some old objects and some objects promoted from the young generation.

Permanent belt: used to save class information, static variables, constants, and compiled code . In java7, the method area on the heap will be managed by GC. It has a size limit. If a large number of dynamically generated classes will be placed To the permanent generation, but it is easy to cause OOM. So in Java8, this area is placed in the local memory, called the metaspace, and the memory of the metaspace is large, so OOM can be avoided.

Finally, let me talk about why the permanent belt is abolished:

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

Removing the permanent generation is an effort to integrate HotSpot JVM and JRockit VM, because JRockit has no permanent generation and does not need to configure the permanent generation.

One is that the HotSpot JVM and JRockit VM are combined into one, and the other is that the memory on the heap is still limited to prevent memory overflow. 

JVM memory analysis method

View class loading

Codeliu@bogon java % jps
51542 
63079 Jps
62634 WorkStealingPoolDemo
Codeliu@bogon java % jstat -class 62634
Loaded  Bytes  Unloaded  Bytes     Time   
   548  1114.5        0     0.0       0.13

Parameter Description: 

  • Loaded: the number of loaded classes
  • Bytes: the size of the occupied space
  • Unloaded: Unloaded quantity
  • Bytes: space occupied by unloaded
  • Time: time taken to load

Check the compilation status

Codeliu@bogon java % jstat -compiler 62634
Compiled Failed Invalid   Time   FailedType FailedMethod
      87      0       0     0.05          0   

Parameter Description:

  • Compiled: The number of compilations.
  • Failed: the number of failures
  • Invalid: Unavailable quantity
  • Time: time
  • FailedType: failure type
  • FailedMethod: the failed method

Check the gc situation

Codeliu@bogon java % jstat -gc 62634      
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
5120.0 5120.0  0.0    0.0   33280.0   6785.1   87552.0      0.0     4480.0 774.0  384.0   75.8       0    0.000   0      0.000    0.000

Parameter Description:

  • S0C: the size of the first Survivor area (KB)
  • S1C: the size of the second Survivor area (KB)
  • S0U: The usage size of the first Survivor area (KB)
  • S1U: the used size of the second Survivor area (KB)
  • EC: Size of Eden area (KB)
  • EU: the size used in the Eden area (KB)
  • OC: Old area size (KB)
  • OU: Old use size (KB)
  • MC: method area (metaspace) size (KB)
  • MU: method area used size (KB)
  • CCSC: Compressed class space size (KB)
  • CCSU: Compressed Class Space Usage Size (KB)
  • YGC: Number of young generation garbage collections
  • YGCT: young generation garbage collection consumption time
  • FGC: the number of garbage collections in the old age
  • FGCT: Old generation garbage collection time consumption
  • GCT: Garbage Collection Consumed Total Time

You can also specify the print interval and number of prints:

#1s一次,共打印两次
Codeliu@bogon java % jstat -gc 62634 1000 2
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
5120.0 5120.0  0.0    0.0   33280.0   6785.1   87552.0      0.0     4480.0 774.0  384.0   75.8       0    0.000   0      0.000    0.000
5120.0 5120.0  0.0    0.0   33280.0   6785.1   87552.0      0.0     4480.0 774.0  384.0   75.8       0    0.000   0      0.000    0.000

View memory usage

Codeliu@bogon java % jmap -heap 62634
Attaching to process ID 62634, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.101-b13

using thread-local object allocation.
Parallel GC with 4 thread(s)
#堆内存配置信息
Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 715653120 (682.5MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 34078720 (32.5MB)
   used     = 6947952 (6.6260833740234375MB)
   free     = 27130768 (25.873916625976562MB)
   20.38794884314904% used
From Space:
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
To Space:
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
PS Old Generation
   capacity = 89653248 (85.5MB)
   used     = 0 (0.0MB)
   free     = 89653248 (85.5MB)
   0.0% used

2318 interned Strings occupying 163192 bytes.

View object count and size

#查看所有对象,包括活跃以及非活跃的
jmap -histo <pid> | more

#查看活跃对象
jmap -histo:live <pid> | more
Codeliu@bogon java % jmap -histo:live 62634 | more

 num     #instances         #bytes  class name
----------------------------------------------
   1:          3576         312072  [C
   2:           440         129528  [B
   3:          3555          85320  java.lang.String
   4:           629          71960  java.lang.Class
   5:           595          36688  [Ljava.lang.Object;
   6:             1          32784  [Ljava.util.concurrent.ForkJoinTask;
   7:           180          11520  java.net.URL
   8:           355          11360  java.util.HashMap$Node
   9:           124           7160  [I
  10:           124           6592  [Ljava.lang.String;
  11:           127           5080  java.util.LinkedHashMap$Entry
  12:            19           4976  [Ljava.util.HashMap$Node;
  13:           106           4240  java.lang.ref.SoftReference
  14:           256           4096  java.lang.Integer
  15:           120           3840  java.util.Hashtable$Entry
  16:           127           3048  java.io.ExpiringCache$Entry
  17:            91           2912  java.util.concurrent.ConcurrentHashMap$Node
  18:             7           2632  java.lang.Thread
  19:            64           2560  java.lang.ref.Finalizer
:

Object description:

  • B  byte
  • C  char
  • D  double
  • F  float
  • I  int
  • J  long
  • Z  boolean
  • [Array, such as [I means int[]
  • [L+ class name other objects 

Dump the memory usage to a file

Codeliu@bogon java % jmap -dump:format=b,file=/Users/Codeliu/Desktop/dump.dat 62634
Dumping heap to /Users/Codeliu/Desktop/dump.dat ...
Heap dump file created

View desktop: 

 The file has been saved to the specified path, let's analyze this file below.

Analyze the dump file through jhat

Codeliu@bogon java % jhat -port 10000 /Users/Codeliu/Desktop/dump.dat      
Reading from /Users/Codeliu/Desktop/dump.dat...
Dump file created Fri May 26 19:31:04 CST 2023
Snapshot read, resolving...
Resolving 11498 objects...
Chasing references, expect 2 dots..
Eliminating duplicate references..
Snapshot resolved.
Started HTTP server on port 10000
Server is ready.

At this time, we open an address: localhost:10000 

Click on the last one:

Execute Object Query Language (OQL) query

Enter in the input box:

#Query the string whose length is greater than 1000

select s from java.lang.String s where s.value.length>1000

Click the button Execute: 

If you want to find something, check it yourself. To put it bluntly, it is a visual interface, which contains all the content we have checked above.

There is also a tool called VisualVM, which is a software under jdk/bin. It is easier to use and can also check thread deadlocks.

garbage collection

Talking about garbage collection

If one or more objects no longer have any references pointing to it, then that object is now garbage. It is a matter of common sense that garbage must be recycled. But the question is by whom? As a developer, do you want to recycle? I think none of the 100 developers wants to deal with complex garbage collection, and that can only be handed over to the system for recycling.

This is the automatic garbage collection mechanism. It is called GC in Java. Except for Java, other languages ​​have their own garbage collection mechanism. Bloggers have also been in touch with mobile terminals in the past. iOS garbage collection relies on reference counting, and Java used to use it before. Through this similar method, but due to some reasons, the accessibility analysis algorithm has been changed. As for the reasons, we will talk about them later.

With the garbage collection mechanism, developers can focus on business development, greatly speeding up development efficiency. But sometimes, some memory leaks are unavoidable, which requires the accumulated experience of developers to avoid. Next, let's take a look at the relevant content of garbage collection and some configurations of garbage collection that we can do.

Ways of Garbage Collection

reference count

The reference count is easy to understand. When new is used or the object is referenced, the reference count is +1, and the default is 0.

We can think that the object is like a lamp in the classroom. When there is no one, it is not on. The first person enters, the lamp is on, and the reference count is +1. When the second person comes, the lamp is still on, and the reference count is + 1, for 2, a person walks, the light is on, the reference count is -1, it is 1, and another person walks, the reference count is -1, it is 0, there is no one at this time, the light is off, it means that the object is destroyed and the memory is reclaimed.

The reason why reference counting is abandoned is because reference counting cannot solve the problem of circular references. The so-called circular reference is that multiple objects refer to each other, resulting in memory that cannot be recycled. As shown below:

In addition, Java designers believe that when an object is referenced, it is necessary to update the counter, which has time overhead and wastes CPU resources , because even if the memory is sufficient, the counter statistics are still performed at runtime. Therefore, the reference counting method is abandoned.

iOS is still using the reference counting method so far, and it is generally recognized in the industry that Apple's garbage collection mechanism is the best. I don't know why Java deprecated it. There may be differences between the two reference counting methods. The blogger has not studied it deeply, no Do too many reviews.

Reachability Analysis Algorithm

First look at this picture:

​The reachability analysis algorithm will have a root node [GC Roots], which points to the next node, and then the next node starts to find the nodes below it, and so on until all nodes are traversed. At this time, objects that are not under the link with the node are unreachable objects, and objects that need to be recycled.

But instead of reclaiming the object immediately, give them a chance to prove that they are still reachable, and execute the finalize method of the object, which can only be executed once. If not executed, execute this method, otherwise directly recycle.

If after executing the finalize method, it can be proved that the object is reachable and associated with the root node, it will not be recycled, but if it is determined to be unreachable for the second time, the finalize method will not be executed again, and the object will be directly recycled .

Garbage Collection Algorithm

mark-and-sweep algorithm

The mark-and-sweep algorithm divides garbage collection into two phases, marking and clearing:

  • Garbage mark unreachable objects according to the reachability analysis algorithm
  • Garbage collect these marked unreachable objects

 

The clear mark algorithm needs to traverse all objects when marking and clearing, and when GC occurs, it will stop the world (STW), that is, the application program will stop completely. You can make up for this consequence by yourself. In addition, the memory after the algorithm is clearly marked is discontinuous, that is, fragmented, which is very unfavorable to memory allocation. 

copy algorithm

Obviously, the disadvantage of the copy algorithm is that only half of the memory can be used each time, which is not a very high utilization rate of memory, but it does not mean that it is useless, and this algorithm is used by the young generation. Although only half of the memory can be used at a time, there is no fragmentation, which is also an advantage.

Mark Compression Algorithm

  

The mark compression algorithm is an optimized and improved algorithm based on the mark clear algorithm. Like the mark-and-sweep algorithm, it starts from the root node and marks the references of objects. In the clean-up phase, it does not simply clean up recyclable objects directly, but moves all surviving objects to the other end of the memory, and then cleans up the objects outside the boundary. Garbage, thus solving the problem of fragmentation. However, the mark compression algorithm has one more step to move the memory location, which also has a certain impact on efficiency. 

Generational Collection Algorithm

The generational collection algorithm actually refers to the young generation + old generation, and then divides into E area, S0, and S1 area. We have briefly mentioned their working methods above, and here is a summary.

  • When an object is created, the object will be allocated in the Eden area of ​​the new generation. When the Eden area is about to be full, YoungGC is triggered
  • After YoungGC, the surviving objects in the Eden area are moved to the S0 area, and the age of the current object will be increased by 1 , and the Eden area will be cleared
  • When YoungGC is triggered again, the surviving objects in the Eden area and the objects in S0 will be moved to the S1 area, and the age of these objects will be increased by 1, and the Eden area and the S0 area will be cleared.
  • When YoungGC is triggered again, the surviving objects in the Eden area and the objects in S1 will be moved to the S0 area, the age of these objects will be increased by 1, and the Eden area and the S1 area will be cleared.
  • Then, again and again

When the age of the object reaches a certain limit value ( 15 years old by default , 6 years old by default in CMS), the object will enter the old age. If the object is too large, it will directly enter the old age. Some places say that if in The sum of all the sizes of objects of the same age in the Survivor area exceeds half of the Survivor space, and objects whose age is greater than or equal to this age can directly enter the old age. Bloggers are not sure about the correctness of this point and will verify it.

When the old generation is full, FullGC is triggered . FullGC recycles the new generation and the old generation at the same time . Only one thread of FullGC executes, and all other threads are suspended.

In addition, the size ratio of the three areas in the young generation is: Eden area, S0 area, S1 area [8:1:1]

The ratio of the young generation to the old generation is: [1:2]

When GC is generated for the young generation: MinorGC [young GC]

When GC is generated for the old generation: FullGC [OldGC]

The above is under Java8.

Make complaints

These pictures are really difficult to deal with. They are all marked by bloggers with tables one by one:

In order to facilitate dragging and dropping, ppt is also used:

It's too difficult for me. I have used several drawing software, but none of them are very ideal. If you have a good drawing software, please recommend it.

What are the garbage collectors?

Garbage collection requires a machine collector. The garbage collector is the carrier of the garbage collection algorithm. The garbage collectors are divided into the following four categories:

  • Serial Garbage Collector Serial
  • Parallel Garbage Collector Parallel
  • CMS garbage collector
  • G1 garbage collector

Next, the blogger will talk about their specific functions. The following is a diagram of the collocation of the new generation and the old generation:

 

Serial Garbage Collector Serial

The serial garbage collector uses a single thread for garbage collection. During garbage collection, only one thread is working. At this time, other threads in the java application must be suspended and wait for the completion of garbage collection. This phenomenon is called STW (Stop-The-World) as mentioned earlier. SerialNew is applied to the young generation, and SerialOld is applied to the old generation.

Due to its single-threaded nature, the performance is too poor. So this garbage collector is not very popular in actual combat.

Add parameters to the program running parameters:

  • -XX:+UseSerialGC
    • Specifies that both the young and old generations use the serial garbage collector
  • -XX:+PrintGCDetails
    • print garbage collection details
-XX:+UseSerialGC -XX:+PrintGCDetails

The settings are here:

 

  

Parallel Garbage Collector Parallel

​The parallel garbage collector is improved on the basis of the serial garbage collector, and the single thread is changed to multi-threaded for garbage collection, which shortens the time of garbage collection.

However, the parallel garbage collector will still pause the application during the collection process. This is the same as the serial garbage collector, except that the parallel execution speed is faster and the pause time is shorter.

The ParNew garbage collector acts on the young generation, and its settings are as follows:

-XX:+UseParNewGC -XX:+PrintGCDetails

After setting, the young generation uses the ParNew collector, and the old generation uses the serial collector. Whether to print GC information can be set according to the needs.

Then talk about the Parallel garbage collector, don't confuse it with ParNew. Java8 uses this garbage collector by default. But on this basis, two new parameters related to system throughput are added, making it more flexible and efficient to use:

  • -XX:+UseParallelGC
    • The young generation uses the ParalleScavenge garbage collector, and the old generation uses the serial collector
  • -XX:+UseParallelOldGC
    • The young generation uses the ParallelScavenge garbage collector, and the old generation uses the ParallelOldGC garbage collector. ParallelOldGC is the old generation version of the ParallelScavenge collector. Why is this the case? You should go to the above to see the cooperative use of the picture drawn by the blogger
  • -XX:MaxGCPauseMillis (to avoid long stm time, the memory will be automatically adjusted appropriately)
    • Set the maximum pause time during garbage collection, in milliseconds
    • It should be noted that in order to achieve the set pause time, ParallelGC may adjust the heap size or other parameters. If the heap size is set smaller, the GC work will become very frequent, which may affect performance instead. Therefore Use this parameter with caution.
  • -XX:UseAdaptiveSizePolicy
    • This is an adaptive GC. The garbage collector will automatically adjust parameters such as the young generation and the old generation to achieve a balance between throughput, heap size, and pause time. It is used in scenarios where it is difficult to manually adjust parameters, and let the collector automatically adjust.

The settings are as follows:

-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails

CMS garbage collector

​The full name of CMS is Concurrent Mark Sweep. It is a concurrent garbage collector that uses the mark-sweep algorithm .

​Its biggest feature is that the application can still run normally during garbage collection. Mainly, when recycling, STW still exists during the garbage marking process. The process is as follows:

  • Initialization mark (CMS-initial-mark), will cause stw;
  • Concurrent mark (CMS-concurrent-mark), start concurrent mark and start marking;
  • Precleaning (CMS-concurrent-preclean), start preprocessing and preprocessing;
  • Remark (CMS-final-remark), the final mark, will cause stw;
  • Concurrent sweep (CMS-concurrent-sweep), start concurrent sweep and clean up;
  • Concurrent reset (CMS-concurrent-reset) resets the CMS state and waits for the next CMS trigger;

These steps can be seen in the log, and after adding the configuration, the output can be output by running the project.

-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails

The premise is that you have to set the initial size of the heap and the maximum memory to be smaller so that they can trigger GC quickly. 

I saw a good article about CMS, and I recommend it to everyone: In-depth understanding of CMS GC - Brief Book

G1 garbage collector

After Java9, the G1 garbage collector is used by default. It is suitable for large heap memory and is also suitable for computers with large memory. It can give full play to the advantages of large memory. It can set the maximum pause time, and its purpose is to replace CMS. It is very convenient to use G1. You only need to set the use of G1, set the maximum heap memory and maximum pause time, and you can leave everything to G1.

The memory model of G1 is quite special. The young generation and the old generation still exist, and a large memory area is added:

It seems that the description in this picture is more appropriate. This is the memory model of G1. E, S, O, and H all exist in multiples and form a continuous memory space. Let us guess what cleaning algorithm it uses ? Does it smell like a copy algorithm? That's right, with so much space, what's the use of not copying and clearing? Moreover, the replication algorithm is also highly efficient, and the memory fragmentation is also small. Are you a little excited?

G1 provides three garbage collection methods: young GC, mixed GC, and full GC.

The way young gc is promoted to mixed GC is the same as before. If there are large objects, they will also directly enter the old area.

When the old area is full, mixed gc will be triggered. From the name, it is a mixed gc. That is to say, in addition to recycling the entire young area, a part of the old area will also be recycled, which is part of the old generation, not all . The default threshold for triggering GC in the old generation in CMS is 80%, and the default threshold in G1 is 45%. You can manually set: XX:InitiatingHeapOccupancyPercent.

The cleaning process is as follows:

  • initial mark: Initial mark, will STW, using reachability analysis algorithm
  • concurrent marking: Concurrent marking, executed together with application threads, collects information about surviving objects in each area
  • remark: final mark, will STW, marks missing objects in concurrent marks
  • clean up: Garbage removal, copy the surviving objects to the free area in the same area, and then clear the original area

When objects are allocated quickly and Mixed GC is too busy, full gc is triggered. This is a disaster, because full gc is a serial old gc executed by a single thread, which will cause long-term STW. Avoid full gc as much as possible.

Set G1 parameters:

-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails

There are generally two ways to tune the G1 garbage collector in the industry:

  • Do not set the size of the new generation and the next generation, let the system adjust itself
  • Set XX:MaxGCPauseMillis=xxxx to set the application pause time. When G1 is running, it will select CSet according to this parameter to meet the setting of response time. Generally, it is between 100 and 200ms. According to its own system, it will continue to optimize the accuracy

other garbage collectors

  • The Epsilon collector was introduced in Java11 and is a  no-op (no operation) collector. It does not do any actual memory reclamation, only responsible for managing memory allocation
  • The Shenandoah collector, introduced in JDK12, is a CPU-intensive garbage collector. It performs memory compaction, immediately deletes useless objects and frees up space in the operating system
  • The ZGC collector is designed for low latency needs and heavy heap space usage, allowing Java applications to continue running while the garbage collector is running. Introduced in JDK11, improved in JDK12, and removed from the experimental phase in JDK15 along with Shenandoah

Commonly used GC analysis tools

There are various GC tools, what do you use? Finally, let's do a statistical survey, and put it at the end.

epilogue

So far, the JVM-related content has been shared with everyone. Every time I talk about JVM, I think of the people of Kirielo in Tiga, so easy to talk about, hahaha! ! ! This part of the content still needs everyone to explore more. There is no upper limit for optimization. Let us work hard together.

Guess you like

Origin blog.csdn.net/CodingFire/article/details/130887792