Knowledge of JVM tuning

JVM object storage: Java objects are divided into basic types and reference types. The basic type stores the object itself, while the reference type stores the reference address. The reference address points to the storage address of the object. The reference address is stored in the stack, and the object itself Stored on the heap, the separation of the stack also makes garbage collection possible.

The stack mainly stores basic types and reference addresses, and the method call is the execution level of the program, which follows the first-in-first-out; the running of the program always runs on the stack, so when passing parameters, there is only the problem of passing basic types and object references. The object itself is not passed directly. In Java, the size of the stack is set by -Xss. When there is a lot of data stored in the stack, you need to increase this value appropriately, otherwise java.lang.StackOverflowError will occur. The common occurrence of this exception is the recursion that cannot return, because the information saved in the stack at this time is the recording point returned by the method.

Heap: The storage unit of jvm, mainly for object storage and recycling; objects in the heap are classified into: new generation and old generation; among them, new generation: Eden, Survivor0 area , Survivor1 area; where objects are transferred in these areas, and in the process of transfer, memory recycling is performed through gc; and garbage collection algorithms include: reference counting, mark-clear, copy, mark -tidy

Reference counting: An older recycling algorithm. The principle is that this object has a reference, which increases a count, and deletes a reference to decrease a count. During garbage collection, only objects with a collection count of 0 are used. The most fatal problem of this algorithm is that it cannot handle the problem of circular reference.

Mark-Sweep: This algorithm executes in two phases. The first stage marks all referenced objects from the reference root node, and the second stage traverses the entire heap to clear unmarked objects. This algorithm needs to suspend the entire application, and at the same time, memory fragmentation will occur.

Copy: This algorithm divides the memory space into two equal areas, using only one of them at a time. During garbage collection, the currently used area is traversed, and the objects in use are copied to another area. This algorithm only processes the objects in use each time, so the cost of copying is relatively small, and at the same time, corresponding memory organization can be performed after copying, and there will be no “fragmentation” problem. Of course, the disadvantage of this algorithm is also obvious, that is, it needs twice the memory space.

Mark-Sweep: This algorithm combines the advantages of both “Mark-Sweep” and “Copy” algorithms. It is also divided into two phases. The first phase marks all referenced objects starting from the root node. The second phase traverses the entire heap, clears unmarked objects and “compresses” surviving objects into one of the heaps, and discharges them in order. This algorithm avoids the fragmentation problem of the “mark-and-sweep” algorithm, and also avoids the space problem of the “copy” algorithm.

Jvm’s generational garbage collection strategy is based on the fact that: different objects have different life cycles. Therefore, objects with different life cycles can be collected in different ways to improve recycling efficiency.

New generation:

All newly created objects are first placed in the young generation. The goal of the young generation is to collect those short-lived objects as quickly as possible. The young generation is divided into three areas. One Eden district, two Survivor districts (generally speaking). Most objects spawn in the Eden area. When the Eden area is full, the surviving objects will be copied to the Survivor area (one of the two). When the Survivor area is full, the surviving objects in this area will be copied to another Survivor area. When it is full, the objects copied from the first Survivor area and still alive at this time will be copied to the “Tenured” area. It should be noted that the two areas of Survivor are symmetrical, and there is no sequence relationship, so there may be objects copied from Eden and objects copied from the previous Survivor in the same area, and only objects copied from the first Survivor are copied to the old area. Objects that Survivor has come and gone. Moreover, one of the Survivor areas is always empty. At the same time, according to the needs of the program, the Survivor area can be configured as multiple (more than two), which can increase the existence time of the object in the young generation and reduce the possibility of being placed in the old generation.

Old Generation:

Objects that survive N garbage collections in the young generation will be placed in the old generation. Therefore, it can be considered that all objects stored in the old generation are objects with a long life cycle.

Permanent Generation:

Used to store static files, such as Java classes, methods, etc. The persistent generation has no significant impact on garbage collection, but some applications may dynamically generate or call some classes, such as Hibernate, etc. At this time, it is necessary to set up a relatively large permanent generation space to store these new classes during operation. The persistent generation size is set by -XX:MaxPermSize=.

When to trigger garbage collection

Because the objects are processed by generations, the garbage collection area and time are also different. There are two types of GC: Scavenge GC and Full GC.

Scavenge GC

Under normal circumstances, when a new object is generated and fails to apply for space in Eden, Scavenge GC will be triggered to GC the Eden area, clear non-surviving objects, and move the surviving objects to the Survivor area. Then organize the two areas of Survivor. This method of GC is performed on the Eden area of the young generation and will not affect the old generation. Because most objects start from the Eden area, and the Eden area will not be allocated very large, so the GC in the Eden area will be performed frequently. Therefore, it is generally necessary to use a fast and efficient algorithm here, so that Eden can be free as soon as possible.

Full GC

Organize the entire heap, including Young, Tenured, and Perm. Full GC is slower than Scavenge GC because it needs to recycle the entire pair, so the number of Full GC should be reduced as much as possible. In the process of tuning the JVM, a large part of the work is the adjustment of FullGC. The following reasons may lead to Full GC:

 · The old generation (Tenured) is filled
· Perm generation (Perm) is full
· System.gc() is called explicitly
After the last GC, the allocation strategy of Heap's domains changes dynamically

Select the appropriate garbage collection algorithm

Serial processor:

  • Applicable conditions: the amount of data is relatively small (about 100M); applications with a single processor and no requirement for response time.
  • Cons: Can only be used for small applications
  • Can be opened with -XX: + UseSerialGC.

Parallel processors:

  • Applicable conditions: “High requirements for throughput”, medium and large applications with multiple CPUs and no requirements for application response time. Examples: background processing, scientific computing.
  • Disadvantage: Potentially longer application response time during garbage collection
  • Open with -XX: + UseParallelOldGC. Use -XX:ParallelGCThreads= to set the number of threads for parallel garbage collection. This value can be set equal to the number of machine processors.

concurrent collector

Applicable conditions: “high requirements for response time”, medium and large applications with multiple CPUs and high requirements for application response time. For example: Web server/application server, telecommunications exchange, integrated development environment.

Open with -XX: + UseConcMarkSweepGC

Common configuration summary

heap settings
-Xms: initial heap size
-Xmx: maximum heap size
-XX:NewSize=n: Set the young generation size
-XX:NewRatio=n: Set the ratio of the young generation to the old generation. For example, if it is 3, it means that the ratio of the young generation to the old generation is 1:3, and the young generation accounts for 1/4 of the entire young generation and the old generation
-XX:SurvivorRatio=n: The ratio of the Eden area to the two Survivor areas in the young generation. Note that there are two Survivor areas. For example: 3, means Eden: Survivor=3:2, a Survivor area accounts for 1/5 of the entire young generation
-XX:MaxPermSize=n: Set the permanent generation size

collector settings
-XX:+UseSerialGC: set the serial collector
-XX: +UseParallelGC: set parallel collector
-XX: + UseParalledlOldGC: Set parallel old generation collector
-XX: + UseConcMarkSweepGC: set concurrent collector

Garbage Collection Statistics
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

Parallel collector settings
-XX:ParallelGCThreads=n: Set the number of CPUs used by the parallel collector to collect. Number of parallel collection threads.
-XX:MaxGCPauseMillis=n: Set the maximum pause time for parallel collection
-XX:GCTimeRatio=n: Set the percentage of garbage collection time to program running time. The formula is 1/(1 + n)

Concurrent collector settings
-XX: + CMSIncrementalMode: Set to incremental mode. Applicable to single CPU case.
-XX:ParallelGCThreads=n: Set the number of CPUs used when the young generation collection method of the concurrent collector is parallel collection. Number of parallel collection threads.

Tuning summary

young generation size selection

Response time priority application: set it as large as possible until it is close to the minimum response time limit of the system (choose according to the actual situation). In this case, young generation collections occur with minimal frequency. At the same time, reduce the number of objects reaching the old generation.

Applications that prioritize throughput: set it as large as possible, possibly reaching the level of Gbit. Because there is no requirement for response time, garbage collection can be performed in parallel, which is generally suitable for applications with more than 8 CPUs.

Old Generation Size Selection

Applications that prioritize response time: The old generation uses concurrent collectors, so its size needs to be set carefully. Generally, some parameters such as the concurrent session rate and session duration must be considered. If the heap setting is small, it may cause memory fragmentation, high recycling frequency, and application suspension, so the traditional mark-and-sweep method is used; if the heap is large, it will take a long time to collect. The optimal solution generally needs to be obtained by referring to the following data:

  1. Concurrent Garbage Collection Information
  2. Persistent generation concurrent collection times
  3. Legacy GC information
  4. Ratio of time spent on young and old generation collections
    Reducing the time spent on the young generation and the old generation generally improves the efficiency of the application

Applications where throughput is a priority

Generally, throughput-first applications have a large young generation and a small old generation. The reason is that in this way, most short-term objects can be recycled as much as possible, and medium-term objects can be reduced, while the old generation only stores long-term surviving objects.

Fragmentation issues caused by smaller heaps

Because the concurrent collector of the old generation uses a mark-and-sweep algorithm, it does not compact the heap. When the collector reclaims, it will merge adjacent spaces so that they can be allocated to larger objects. However, when the heap space is small, “fragmentation” will appear after running for a period of time. If the concurrent collector cannot find enough space, the concurrent collector will stop, and then use the traditional mark and clear method to recycle. If “fragmentation” occurs, the following configuration may be required:
1. -XX: + UseCMSCompactAtFullCollection: When using the concurrent collector, enable the compression of the old generation.
2. -XX:CMSFullGCsBeforeCompaction=0: When the above configuration is enabled, how many times of Full GC are set here to compress the old generation

Exception: java.lang.OutOfMemoryError: Java heap space

This is the most typical way of memory leaks. Simply put, all the heap space is occupied by garbage objects that cannot be recycled, and the virtual machine can no longer allocate new space.

Solution:

This method is also relatively easy to solve. Generally, it is based on the comparison before and after garbage collection, and at the same time, based on the analysis of object references (common collection object references), the leak point can basically be found.

The permanent generation is full

Exception: java.lang.OutOfMemoryError: PermGen space

Description:

Perm space is full. Exception thrown when storage cannot be allocated for the new class. This exception did not exist before, but it is more common today when Java reflection is widely used. The main reason is that a large number of classes generated by dynamic reflection are continuously loaded, which eventually leads to the fullness of the Perm area.

What’s even more frightening is that even if different classLoaders use the same class, they will all load it, which is equivalent to the same thing. If there are N classLoaders, it will be loaded N times. Therefore, in some cases, this problem is basically regarded as unsolvable. Of course, there are not many cases where there are a large number of classLoaders and a large number of reflection classes.
solve:

  1. -XX:MaxPermSize=16m
  2. Switch to JDK. Such as JRocket.

stack overflow

Exception: java.lang.StackOverflowError

Explanation: I won’t say much about this, it’s usually caused by recursion not returning, or circular calls

thread stack full

Exception: Fatal: Stack size too small

Description: The space size of a thread in java is limited. After JDK5.0, this value is 1M. Data related to this thread will be saved in it. But when the thread space is full, the above exception will occur.

Solution: Increase the thread stack size. -Xss2m. But this configuration cannot solve the fundamental problem, and it depends on whether there is a part of the code that causes the leak.

System memory is full

Exception: java.lang.OutOfMemoryError: unable to create new native thread

Description:

This exception is caused by the operating system not having enough resources to spawn this thread. When the system creates a thread, in addition to allocating memory in the Java heap, the operating system itself also needs to allocate resources to create threads. Therefore, when the number of threads reaches a certain level, there may still be space in the heap, but the operating system cannot allocate resources, and this exception occurs.

The more memory allocated to the Java virtual machine, the less resources left in the system. Therefore, when the system memory is fixed, the more memory allocated to the Java virtual machine, the fewer threads the system can generate in total. are inversely proportional to each other. At the same time, the space allocated to a single thread can be reduced by modifying -Xss, and the number of threads generated in the system can also be increased.

Solution:

  1. Redesign the system to reduce the number of threads.
  2. If the number of threads cannot be reduced, use -Xss to reduce the size of a single thread. In order to be able to produce more threads.