Learning java virtual machine - (2) Garbage collection

garbage collection

1. Any judgment object can be recycled

1.1 Reference counting method

Disadvantages: When a circular reference occurs, the counts of both objects are 1, resulting in the failure of both objects to be released

1.2 Accessibility Analysis Algorithm

First of all, it is necessary to determine a series of root objects * (objects that must not be treated as garbage collection). Before garbage collection, all objects in the heap memory will be scanned once to determine whether each object is directly or indirectly by the root object . If it is the reference of this object, it cannot be recycled. On the contrary, if an object is not directly or indirectly referenced by the root object, then this object can be directly regarded as garbage and can be recycled in the future. *

The garbage collector in the Java virtual machine uses reachability analysis to explore all surviving objects

  • The garbage collector in the Java virtual machine uses reachability analysis to explore all surviving objects
  • Scan the objects in the heap to see if the object can be found along the reference chain starting from the GC Root object. If it cannot be found, it means that it can be recycled
  • Which objects can be used as GC Root?
    • Objects referenced in the virtual machine stack (local variable table in the stack frame).
    • Objects referenced by class static properties in the method area
    • Objects referenced by constants in the method area
    • The object referenced by JNI (that is, the Native method in general) in the local method stack
public static void main(String[] args) throws IOException {
    
    

        ArrayList<Object> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add(1);
        System.out.println(1);
        System.in.read();

        list = null;
        System.out.println(2);
        System.in.read();
        System.out.println("end");
    }

For the above code, you can use the following command to dump the heap memory information into a file, and then use the Eclipse Memory Analyzer tool for analysis.

first step:

Use the jps command to view the process of the program

Step two:

Use the jmap -dump:format=b,live,file=1.bin 16104 command to dump the file

dump: dump file

format=b: binary file

file: file name

16104: The id of the process

Step 3: Open Eclipse Memory Analyzer (MAT) to analyze the 1.bin file.

The analyzed gc root finds the ArrayList object, then sets the list to null and dumps it again, then the list object will be recycled.

1.3 Four kinds of references

  1. strong reference

Only when all GC Roots objects do not refer to the object through [strong reference], the object can be garbage collected

  1. Soft Reference (SoftReference)

When only soft references refer to the object, after garbage collection, when the memory is still insufficient, garbage collection will be triggered again to recycle the soft reference object

Can cooperate with the reference queue to release the soft reference itself

  1. Weak reference (WeakReference)

When only weak references refer to the object, during garbage collection, the weak reference object will be recycled regardless of whether the memory is sufficient

You can use the reference queue to release the weak reference itself

  1. Phantom Reference (PhantomReference)

Must be used with the reference queue , mainly used with ByteBuffer. When the referenced object is recycled, the phantom reference will be enqueued, and the Reference Handler thread will call the phantom reference related method (unsafe class method) to release the direct memory

  1. Finalizer reference (FinalReference)

There is no need for manual coding, but it is used internally with the reference queue. During garbage collection, the finalizer reference is enqueued (the referenced object has not been recycled temporarily), and then the **Finalizer thread** finds the referenced object through the finalizer reference and calls Its finalize method, the referenced object can only be recovered during the second GC.

1.3.1 example01: soft reference

/**
 * 软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class Code_08_SoftReferenceTest {
    
    

    public static int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
    
    
        method2();
    }

    // 设置 -Xmx20m , 演示堆内存不足,
    public static void method1() throws IOException {
    
    
        ArrayList<byte[]> list = new ArrayList<>();

        for(int i = 0; i < 5; i++) {
    
    
            list.add(new byte[_4MB]);
        }
        System.in.read();
    }

    // 演示 软引用
// List -> SoftReference -> byte[]
    public static void method2() throws IOException {
    
    
        ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
        for(int i = 0; i < 5; i++) {
    
    
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }
        System.out.println("循环结束:" + list.size());
        for(SoftReference<byte[]> ref : list) {
    
    
            System.out.println(ref.get());
        }
    }
}

Soft reference objects are stored in the list collection. When the memory is insufficient, full gc will be triggered to recycle the soft reference objects. The details are shown in the figure:

In the above code, when the object referenced by the soft reference is recycled, but the soft reference still exists, so generally soft references need to be used together with a reference queue.

Modify method2 as follows:

// 演示 软引用 搭配引用队列
    public static void method3() throws IOException {
    
    
        ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for(int i = 0; i < 5; i++) {
    
    
            // 关联了引用队列,当软引用所关联的 byte[] 被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while(poll != null) {
    
    
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("=====================");
        for(SoftReference<byte[]> ref : list) {
    
    
            System.out.println(ref.get());
        }
    }

1.3.2 example02: weak quotation

public class Code_09_WeakReferenceTest {
    
    

    public static void main(String[] args) {
    
    
//        method1();
        method2();
    }

    public static int _4MB = 4 * 1024 *1024;

    // 演示 弱引用
    public static void method1() {
    
    
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for(int i = 0; i < 10; i++) {
    
    
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB]);
            list.add(weakReference);

            for(WeakReference<byte[]> wake : list) {
    
    
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
    }

    // 演示 弱引用搭配 引用队列
    public static void method2() {
    
    
        List<WeakReference<byte[]>> list = new ArrayList<>();
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for(int i = 0; i < 9; i++) {
    
    
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB], queue);
            list.add(weakReference);
            for(WeakReference<byte[]> wake : list) {
    
    
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
        System.out.println("===========================================");
        Reference<? extends byte[]> poll = queue.poll();
        while (poll != null) {
    
    
            list.remove(poll);
            poll = queue.poll();
        }
        for(WeakReference<byte[]> wake : list) {
    
    
            System.out.print(wake.get() + ",");
        }
    }

}

2. Garbage collection algorithm

2.1 Mark removal

Definition: Mark Sweep

  • faster
  • memory fragmentation

2.2 Marking and finishing

Definition: Mark Compact

  • slow
  • no memory fragmentation

2.3 Copy

Definition: Copy

  • no memory fragmentation
  • Requires twice the memory space

2.4 Generational Garbage Collection

  • Objects are first allocated in the Eden area
  • When the space in the new generation is insufficient, trigger Minor GC​​, Eden and fromSurviving objects are copied to toin using the copy algorithm, and the age of the surviving objects is increased by 1 fromand exchangedto
  • Minor GCIt will cause stop the world, suspend the threads of other users, and wait for the garbage collection to end before the user threads resume running
  • When the life of the object exceeds the threshold, it will be promoted to the old age, and the maximum life is 15 (4bit)
  • When there is not enough space in the old generation, it will try to trigger first Minor GC. If there is still not enough space, then trigger Full GC , and the STW time will be longer

3. Generational garbage collection

3.1 Related VM parameters

meaning parameter
heap initial size -Xms
heap max size Xmx or -XX:MaxHeapSize=size
Cenozoic size -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
Survival area ratio (dynamic) -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
Ratio of Survival Zone -XX:SurvivorRatio=account
Promotion Threshold -XX:MaxTenuringThreshold=threshold
Promotion Details -XX:+PrintTenuringDistribution
GC details -XX:+PrintGCDetails -verbose:gc
Before FullGC MinorGC -XX:+ScavengeBeforeFullGC

3.2 GC analysis

public class Code_10_GCTest {
    
    

    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;

    // -Xms20m -Xmx20m -Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
    public static void main(String[] args) {
    
    
        List<byte[]> list = new ArrayList<>();
        list.add(new byte[_6MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_6MB]);
        list.add(new byte[_512KB]);
        list.add(new byte[_6MB]);
    }

}

Through the above code, allocate memory to the list to observe the situation of the new generation and the old generation, when to trigger Minor GC, when to trigger Full GC, etc., you need to set jvm parameters before use.

4. Garbage collector

Related concepts

  • Parallel collection: refers to multiple garbage collection threads working in parallel, but at this time the user thread is still in a waiting state.
  • Concurrent collection: Refers to the simultaneous work of user threads and garbage collection threads (not necessarily in parallel, may be executed alternately). User programs continue to run while the garbage collector runs on another CPU
  • Throughput: That is, the ratio of the time the CPU spends on running user code to the total CPU consumption time (throughput = running user code time/(running user code time + garbage collection time)), that is. For example: if the virtual machine runs for 100 minutes and the garbage collector spends 1 minute, then the throughput is 99%.

4.1 Serial

  • single thread
  • Less heap memory, suitable for personal computers

-XX:+UseSerialGC=serial + serialOld

Safe point : Let other threads stop at this point, so as not to move the object address during garbage collection, so that other threads cannot find the moved object

Because it is serial, there is only one garbage collection thread. And when the thread performs recovery work, other threads enter the blocked state

  1. Serial collector

The Serial collector is the most basic and oldest collector

Features: Single-threaded, simple and efficient (compared to other single-threaded collectors), using a copy algorithm . For an environment limited to a single CPU, since the Serial collector has no thread interaction overhead, concentrating on garbage collection can naturally obtain the highest single-thread collection efficiency. When the collector performs garbage collection, it must suspend all other worker threads until it ends (Stop The World)!

ParNew collector

  1. ParNew collector

The ParNew collector is actually a multi-threaded version of the Serial collector

**Features:** Multi-threading, the number of collection threads enabled by default by the ParNew collector is the same as the number of CPUs. In an environment with a lot of CPUs, you can use the -XX:ParallelGCThreads parameter to limit the number of threads for garbage collection. Same Stop The World problem as the Serial collector

Serial Old Collector

  1. Serial Old Collector

Serial Old is the old version of the Serial collector

Features: It is also a single-threaded collector, using a mark-sort algorithm

4.2 Throughput priority

  • Multithreading
  • Large heap memory, multi-core CPU
  • Let the time of S, TW be the shortest per unit time
  • The garbage collector used by JDK1.8 by default

  1. Parallel Scavenge Collector

It is closely related to throughput, so it is also called throughput priority collector

Features: The collector that belongs to the new generation is also a collector that uses the copy algorithm (the survival area of ​​the new generation is used), and it is a parallel multi-threaded collector (similar to the ParNew collector)

The goal of the collector is to achieve a manageable throughput. There is another point worthy of attention: GC adaptive adjustment strategy (the most important difference from the ParNew collector)

GC adaptive adjustment policy: The Parallel Scavenge collector can set the -XX:+UseAdptiveSizePolicy parameter. When the switch is turned on, there is no need to manually specify the size of the new generation (-Xmn), the ratio of Eden to the Survivor area (-XX:SurvivorRation), the age of objects promoted to the old generation (-XX:PretenureSizeThreshold), etc., the virtual machine runs according to the system Status collects performance monitoring information, and dynamically sets these parameters to provide optimal pause time and highest throughput. This adjustment method is called GC's adaptive adjustment strategy.

XX:MaxGCPauseMillis controls the maximum garbage collection pause time

  • XX:MaxGCPauseMillis controls the maximum garbage collection pause time
  • XX:GCRatio directly sets the size of the throughput

4.3 Response time priority

  • Multithreading
  • Large heap memory, multi-core CPU
  • Make the time of a single STW as short as possible

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ Serialold
-XX:ParallelGCThreads=n //Number of parallel threads~ -XX:ConcGCThreads=threads //Number of concurrent threads
-XX:CMSInitiatingOccupancyFraction=percent //Memory during garbage collection Proportion
-XX:+CMSScavengeBeforeRemark

CMS collector

Concurrent Mark Sweep, an old generation collector that aims to obtain the shortest recovery pause time

Features: Implemented based on the mark-sweep algorithm. Concurrent collection, low pause, but memory fragmentation

Application Scenario: Applicable to the scenarios where the response speed of the service is emphasized, the system pause time is expected to be the shortest, and a better experience is brought to the user. Such as web program, b/s service

The operation process of the CMS collector is divided into the following four steps:

  1. Initial mark: mark the objects that GC Roots can directly reach. Fast but still has the Stop The World problem.
  2. Concurrent marking: the process of GC Roots Tracing to find surviving objects and user threads can execute concurrently.
  3. Re-marking: In order to correct the mark record of the part of the object whose mark changes due to the continued operation of the user program during the concurrent mark. Still having Stop The World issues
  4. Concurrent clearing: Clear and recycle the marked objects. During the clearing process, there may still be new garbage generated. These garbage are called floating garbage. If the user needs to store a large object, the new generation cannot store it. Going on, the old generation will degenerate into a serial Old collector due to too much floating garbage, and mark-organize the old generation garbage. Of course, this is also very time-consuming!

The memory recovery process of the CMS collector is executed concurrently with user threads, and can be used with the ParNew collector (multi-threaded, new generation, copy algorithm) and the Serial Old collector (single thread, old generation, mark-sort algorithm).

4.4 G1

Definition: Garbage First

Applicable scene:

  • Focus on both throughput and low latency (response time), the default pause target is 200 ms
  • Super large heap memory (large memory), will divide the heap memory into multiple areas of equal size
  • Overall yes 标记-整理算法, between the two areas is复制算法

Related parameters:

JDK8 is not enabled by default, the required parameters are enabled

-XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time

4.4.1 G1 Garbage Collection Phase

1) Young Collection
  • will STW

Generation is divided according to the life cycle of the object, and partition is to divide the heap space into several different consecutive small areas. Each small area is recycled independently. You can control how many small areas are recycled at a time, and it is convenient to control the pause time generated by GC!

Garden of Eden

Survival area

old generation

2) Young Collection + CM
  • GC Root will be initially marked during Young GC
  • When the proportion of heap space occupied by the old generation reaches the threshold, concurrent marking (without STW) is performed, which is determined by the following JVM parameters

-XX:InitiatingHeapOccupancyPercent=percent (默认45%)

3) Mixed Collection

E, S, O will be fully garbage collected

  • The final mark (Remark) will be STW
  • Copy survival (Evacuation) will STW

-XX:MaxGCPauseMillis=ms

Why are some old generations copied and some not?

Because the maximum pause time is specified, it may take too long to collect all the old generations. In order to ensure that the time does not exceed the set pause time, the most valuable old age will be recycled (after recycling, more memory can be obtained)

4.4.2 Full GC

SerialGC

  • Garbage collection that occurs due to insufficient memory in the new generation - minor gc
  • Garbage collection that occurs due to insufficient memory in the old generation - full gc

ParallelGC

  • Garbage collection that occurs due to insufficient memory in the new generation - minor gc
  • Garbage collection that occurs due to insufficient memory in the old generation - full gc

CMS

  • Garbage collection that occurs due to insufficient memory in the new generation - minor gc
  • Insufficient memory in the old generation (CMS will degenerate to -full gc when concurrency fails)

G1

  • Garbage collection that occurs due to insufficient memory in the new generation - minor gc
  • Out of memory in the old generation
    • If the garbage generation speed is slower than the garbage collection speed, Full GC will not be triggered, or it will be cleaned up concurrently
    • If the garbage generation speed is faster than the garbage collection speed, Full GC will be triggered, and then it will degenerate into a serial collection of the serial Old collector, which will cause a long pause.

4.4.3 Young Collection Intergenerational References

Cross-generational references in the new generation recycling (the old generation refers to the new generation) problem

  • Card List and Remembered Set

    • Remembered Set exists in E and is used to save the dirty card corresponding to the new generation object
      • Dirty card: O is divided into multiple areas (one area 512K), if the area refers to the new generation object, the area is called a dirty card
  • Pass post-write barrier + dirty card queue when reference changes

  • concurrent refinement threads 更新 Remembered Set

4.4.4 Remark

pre-write barrier + satb_mark_queue

Black: has been processed and needs to be kept

Gray: in progress

White: unprocessed

But in the process of concurrent marking, it is possible that A does not refer to C after being processed, but the processing process is not over yet, and A references C before the processing process ends, and remark will be used at this time.

The process is as follows

  • C was not referenced before, when A references C, a write barrier will be added to C, the write barrier instruction will be executed, C will be put into a queue, and C will be changed to the processing state
  • After the concurrent marking phase is over, the re-marking phase will be STW, and then the object placed in the queue will be reprocessed, and if there is a strong reference to it, it will be processed, from gray to black.

4.4.5 JDK 8u20 string deduplication

Pros: save a lot of memory

Disadvantages: Slightly more CPU time is taken up, and the recycling time of the new generation is slightly increased

-XX:+UseStringDeduplication

String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
String s2 = new String("hello"); // char[]{'h','e','l','l','o'}
  • Put all newly allocated strings into a queue
  • When the new generation is reclaimed, G1 concurrently checks whether there are duplicate strings
  • If they have the same value, let them refer to the same char[]
  • String.intern() focuses on string objects
    • String.intern() focuses on string objects
    • The focus of string deduplication is char[]
    • Inside the JVM, a different string table is used

4.4.6 JDK 8u40 Concurrent Marker Class Offload

After the concurrent marking phase ends, it is possible to know which classes are no longer used. If all classes of a class loader are not in use, unload all classes loaded by it

-XX:+ClassUnloadingWithConcurrentMark is enabled by default

4.4.7 JDK 8u60 Recycling Huge Objects

  • When an object is larger than half of the region, it is called a giant object
  • G1 does not copy huge objects
  • priority for recycling
  • G1 will track all incoming references in the old generation. If the incoming references in the old generation are 0, the giant objects can be disposed of during garbage collection in the new generation.

4.4.8 Adjustment of JDK 9 Concurrent Marking Start Time

  • Concurrent marking must be completed before the heap space is full, otherwise it will degenerate into FulGC
  • Required before JDK 9-XX:InitiatingHeapOccupancyPercent
  • \-XX:InitiatingHeapOccupancyPercentused to set the initial value
    • \-XX:InitiatingHeapOccupancyPercentused to set the initial value
    • Data sampling and dynamic adjustment
    • Always add a safe empty space

5. Garbage collection tuning

View virtual machine parameters command

 java  -XX:+PrintFlagsFinal -version | findstr "GC"

Specific information can be queried according to parameters

5.1 Tuning areas

  • Memory
  • lock contention
  • CPU usage
  • Io
  • gc

5.2 Defining goals

Low latency/high throughput? Choose the right GC

  • CMS G1 ZGC
  • ParallelGC
  • Zing

5.3 Fastest GC

First exclude the reduction of memory problems caused by the code written by itself

  • To view the memory usage before and after Full GC, consider the following questions
    • Is there too much data?
      • resultSet = statement.executeQuery(“select * from 大表 limit n”)
    • Is the data representation too bloated?
      • object graph
      • object size 16 Integer 24 int 4
    • Is there a memory leak
      • static Map map …
      • soft
      • weak
      • Third-party cache implementation

5.4 New Generation Tuning

  • Characteristics of the new generation

    • All new operations allocate memory very cheaply
      • TLAB thread-lcoal allocation buffer
    • Dead objects are recycled with zero cost
    • Most objects die when used
    • Minor GC takes much less time than Full GC
  • Is the larger the new generation memory, the better?

    • no
      • The memory of the new generation is too small: Minor GC is triggered frequently, which will cause STW and reduce throughput
      • Too much memory in the new generation: The proportion of memory in the old generation has decreased, and Full GC will be triggered more frequently. And when Minor GC is triggered, it will take longer to clean up the new generation
    • It is advisable to set the memory of the new generation to accommodate [concurrency * (request-response)] data
  • The surviving area needs to be able to save the current active object + the object that needs to be promoted

  • The promotion threshold is properly configured, so that long-lived objects can be promoted as soon as possible

-XX:MaxTenuringThreshold=threshold
-XX:+PrintTenuringDistrubution

5.5 Old Age Tuning

Take CMS as an example:

  • The bigger the memory in the old generation of CMS, the better
  • Try not to do tuning first, if there is no Full GC, then try to tune the new generation first.
  • Observe that the memory usage of the old generation during Full GC is increased by 1/4 ~ 1/3 by default in the old generation

-XX:CMSInitiatingOccupancyFraction=percent

5.6 Case

Case 1: Frequent Full GC and Minor GC

Case 2: Full GC occurs during the peak request period, and the single pause time is extremely long (CMS)

Case 3: Full GC occurs when the old age is abundant (jdk1.7)

Guess you like

Origin blog.csdn.net/qq_51110841/article/details/126828221