1. System.gc(

  • By default, passSystem.gc()orRuntime.getRuntime().gc()Is explicitly triggeredFull GCThe old and new generations of the colleague heap are recycled to try to free up memory occupied by discarded objects
  • However,System.gc()The invocation comes with a disclaimer that cannot guarantee the invocation to the garbage collector
  • JVM implementers can passSystem.gc()Call to determine the GC behavior of the JVM. In general, garbage collection should be automatic and not manually triggered, otherwise it would be too much trouble. In special cases, such as when we are writing a performance baseline, we can call it before runningSystem.gc()

Code Test 1

public class SystemGCTest {

    public static void main(String[] args) {
        new SystemGCTest();
        /** * reminds the GARBAGE collector of the JVM to perform GC behavior, but there is no guarantee that the GC will execute * so the Finalize () method may not be executed */
        System.gc();
        /** * forces a call to finalize() of objects with lost references */
        System.runFinalization();
    }

    @Override
    protected void finalize(a) throws Throwable {
        super.finalize();
        System.out.println("SystemGCTest overrides Finalize ()"); }}Copy the code

Running code must output

SystemGCTest overrides Finalize ()Copy the code

Code Test 2


public class LocalVarGC {

    public void localVarGC1(a) {
        byte[] buffer = new byte[10 * 1014 * 1024];  // 10MB
        System.gc();
    }

    public void localVarGC2(a) {
        byte[] buffer = new byte[10 * 1014 * 1024];  // 10MB
        buffer = null;
        System.gc();
    }

    public void localVarGC3(a) {{byte[] buffer = new byte[10 * 1014 * 1024];  // 10MB
        }
        System.gc();
    }

    public void localVarGC4(a) {{byte[] buffer = new byte[10 * 1014 * 1024];  // 10MB
        }
        int value = 10;
        System.gc();
    }

    public void localVarGC5(a) {
        localVarGC1();
        System.gc();
    }

    public static void main(String[] args) {
        LocalVarGC localVarGC = newLocalVarGC(); localVarGC.localVarGC1(); }}Copy the code

Execute each method in turn.

  • performlocalVarGC1(), the output GC log is as follows:
[GC (System.gc()) [PSYoungGen: 12761K->10636K(76288K)] 12761K->10644K(251392K), 0.0077479 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 10636K->0K(76288K)] [ParOldGen: 8K->10478K(175104K)] 10644K->10478K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0073883 secs] [Times: user=0.01 sys=0.02, real=0.01 secs] 
Copy the code

If YoungGC is executed first, the 10M byte array cannot be collected, and the new generation memory is reduced to 10636K (about 10MB). The Full GC is then executed to move the byte array to the old age

  • performlocalVarGC2(), the output GC log is as follows:
[GC (System.gc()) [PSYoungGen: 12761K->464K(76288K)] 12761K->472K(251392K), 0.0007113 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 464K->0K(76288K)] [ParOldGen: 8K->338K(175104K)] 472K->338K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0034281 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Copy the code

The visible byte array is collected, the new generation decreases to 464K on YoungGC, and directly decreases to 0 on Full GC

  • performlocalVarGC3(), the output GC log is as follows:
[GC (System.gc()) [PSYoungGen: 12761K->10700K(76288K)] 12761K->10708K(251392K), 0.0074673 secs] [Times: user=0.03 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 10700K->0K(76288K)] [ParOldGen: 8K->10478K(175104K)] 10708K->10478K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0058114 secs] [Times: user=0.01 sys=0.02, real=0.01 secs] 
Copy the code

You can see that the byte array is not collected in the Young GC and is moved to the old age in the Full GC. Because if we look through jclasslib:

The local variable has a maximum slot number of 2, while looking at the local variable table:

It can be seen that only the position with index 0 in the local variable table is this, and the position with actual index 1 is occupied by the buffer variable. This byte array will not be collected when GC is performed

  • performlocalVarGC4()When comparinglocalVarGC3(), output GC log as follows:
[GC (System.gc()) [PSYoungGen: 12761K->480K(76288K)] 12761K->488K(251392K), 0.0008203 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 480K->0K(76288K)] [ParOldGen: 8K->338K(175104K)] 488K->338K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0036231 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Copy the code

The visible byte array is recycled because the buffer variable is outside the scope of the static code block. In the local variable table, the position with index 1 is occupied by value, so it can be recycled (slot reuse).

  • performlocalVarGC5(), the output GC log is as follows:
[GC (System.gc()) [PSYoungGen: 12761K->10636K(76288K)] 12761K->10644K(251392K), 0.0071704 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 10636K->0K(76288K)] [ParOldGen: 8K->10478K(175104K)] 10644K->10478K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0058936 secs] [Times: user=0.01 sys=0.02, real=0.01 secs] 
[GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] 10478K->10478K(251392K), 0.0003457 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] [ParOldGen: 10478K->338K(175104K)] 10478K->338K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0036410 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Copy the code

When GC in localVarGC5() is called within the localVarGC1() method, the byte array object is not recycled, and when GC is performed again after localVarGC1(), the object is recycled

2. Memory overflow or leakage

Memory Overflow (OOM)

  • Memory leaks, though easier to understand than memory leaks, are also one of the main causes of program crashes. Since GC is constantly evolving, it is generally less likely to happen unless the application’s memory footprint is growing so fast that garbage collection can no longer keep pace with memory consumption
  • In most cases, the GC will do garbage collection for various ages, and if it’s too late, an exclusive Full GC will reclaim a large amount of memory for the application to continue using
  • Javadoc forOutOfMemoryErrorThe explanation is:There is no free memory, and the garbage collector cannot provide more memory
  • The first saidNo free memoryThe Java VIRTUAL machine has insufficient heap memory. There are two reasons:
    • The Heap memory setting of the Java virtual machine is insufficientFor example, there may be a memory leak, or the heap size may not be reasonable, such as we are dealing with an objective amount of data, but the JVM heap size is not explicitly specified or the specified value is too small. We can go through the parameters-Xms.-XmxTo adjust the
    • A lot of large objects are created in the code and cannot be collected by the garbage collector for a long time (there are references): for older versions of Oracle JDK, since the size of the permanent generation is limited, and the JVM for permanent generation garbage collection (such as: constant pool recovery, the type of unloading is no longer needed) is not positive, so when we are constantly adding new types, permanent generation OOM is also very rare, especially in large amounts of dynamic type generated at runtime; similarinternString caching takes up too much space and can cause OOM problems. The corresponding exception information is marked as associated with the permanent generation:java.lang.OutOfMemoryError: PermGen space
  • With the introduction of metadata area, the memory of the method area is no longer so poor, so the corresponding OOM has been changed, when OOM appears, the exception message becomes:java.lang.OutOfMemoryError: MetaspaceDirectly out of memory also causes OOM
  • The implication is that before the OOM is thrown, the garbage collector will normally be triggered to clean up as much space as possible
    • For example, in reference mechanism analysis, the JVM is designed to attempt to reclaim objects to which a soft reference points
    • injava.nio.BITs.reserveMemory()In the method, we can clearly see,System.gc()Will be called to clear space
  • Of course, the garbage collector will not be triggered in every case. For example, if we allocate a large object, such as a large array exceeding the maximum size of the heap, the JVM can determine that garbage collection does not solve the problem, so it simply throws OOM

Memory Leak

  • Also known as a storage leak. Strictly speaking, a memory leak is only when objects are no longer used by the program, but the GC cannot reclaim them. In practice, however, many times poor practices or omissions can lead to long object lifetimes or even OOM, which can be called a memory leak in the broad sense
  • Although a memory leak does not immediately cause the program to crash, once a memory leak occurs, the available memory in the program will be gradually eaten up until all memory is exhausted, and finally OOM appears, causing the program to crash
  • Note that the storage is not physical memory, but the virtual memory size, which depends on the size of the disk swap

Memory leakage provides an example

  • Singleton pattern: Singletons have the same lifetime as the application (singletons are static), so if a singleton holds a reference to an external object, that object cannot be recycled, resulting in a memory leak
  • Some providecloseThe resource is not closed, causing a memory leak: Database connection (dataSource.getConnection()), network connection (socket) and IO connections must be closed manually, otherwise they cannot be reclaimed
  • Note: Do not use examples of circular references that exist in reference counting algorithms to explain memory leaks, as reference counting algorithms are not used in the JVM

3. Stop The World

  • Stop-the-world, or STW, refers to application pauses that occur during GC events. A pause occurs when the entire application thread is suspended without any response, a bit like being stuck. This pause is called STW
    • Enumerating root nodes (GC Roots) in the reachability analysis algorithm cause all Java execution threads to pause
      • The analysis must be done in a snapshot that ensures consistency
      • Consistency means that the entire execution system appears to be frozen at a point in time throughout the analysis
      • If the object reference relationship changes constantly during the analysis, the accuracy of the analysis result cannot be guaranteed
  • Application threads that are interrupted by STW will resume after GC, and frequent interruptions will make users feel like a movie stutter due to slow network speed, so we need to reduce STW occurrences
  • The STW event is independent of which GC is used; all GC’s have this event. Even G1 isn’t completely immune to STW, except that garbage collectors are getting better and more efficient at collecting, reducing pause times as much as possible
  • STW is automatically initiated and completed by the JVM in the background. When the user is not visible, all the normal working threads of the user are stopped
  • Do not use it in developmentSystem.gc()Will lead to the occurrence of STW

Code demo

package com.nasuf.jvm;

import java.util.ArrayList;
import java.util.List;

public class StopTheWorldDemo {
    public static class WorkThread extends Thread {
        List<byte[]> list = new ArrayList<>();

        @Override
        public void run(a) {
            try {
                while (true) {
                    for (int i = 0; i < 1000; i++) {
                        byte[] buffer = new byte[1024];
                        list.add(buffer);
                    }
                    if (list.size() > 10000) {
                        list.clear();
                        // Full GC is fired, and STW events occurSystem.gc(); }}}catch(Exception ex) { ex.printStackTrace(); }}}public static class PrintThread extends Thread {
        public final long startTime = System.currentTimeMillis();

        @Override
        public void run(a) {
            try {
                while (true) {
                    // Prints time information every second
                    long t = System.currentTimeMillis() - startTime;
                    System.out.println(t / 1000 + "." + t % 1000);
                    Thread.sleep(1000); }}catch(Exception ex) { ex.printStackTrace(); }}}public static void main(String[] args) {
        WorkThread w = new WorkThread();
        PrintThread p = newPrintThread(); w.start(); p.start(); }}Copy the code

The output is as follows:

0.0
1.2
2.7
3.10
4.12
5.14
6.17
7.20
8.26
9.31
10.35
11.36
12.40
13.47
14.49
Copy the code

See that the PrintThread thread does not output strictly every second because of the delay caused by STW events

4. Parallelism and concurrency of garbage collection

Concurrent

  • In an operating system, a period of time in which several programs are running on the same processor
  • Concurrency is not a true sense of the word “simultaneously”, only the CPU to a time period is divided into several segments of time (time interval), and then switch back and forth between the several time interval, due to the CPU speed is very fast, as long as the time interval handled well, can make the user feel is more than one application at the same time

Parallel

  • When the system has more than one CPU, when one CPU executes a process, the other CPU can execute another process. The two processes do not preempt resources but can run simultaneously, which is called Parallel.
  • In fact, the factor that determines parallelism is not the number of cpus, but the number of CPU cores. For example, a CPU can have multiple cores in parallel
  • Suitable for scientific calculation, background processing and other weak interactive scenes

The two contrast

  • Concurrency, which is multiple things happening at the same time in the same period of time; Parallelism means multiple things happening at the same time
  • Concurrent tasks seize resources from each other; Parallel tasks do not grab resources from each other
  • Parallelism occurs only if there are multiple cpus or one CPU has multiple cores. Otherwise, everything that seems to happen at the same time is actually executed concurrently

Concurrent and parallel garbage collection

Concurrency and parallelism, in the context of talking about the garbage collector, can be explained as follows:

  • Parallel:Refers to multiple garbage collection threads working in parallel, but the user thread is still in the wait state
    • Be the ParNew, Parallel Insane, Parallel Old
  • Serial
    • In contrast to the concept of parallelism, a single thread executes
    • If memory is insufficient, the program is paused and the JVM garbage collector is started for garbage collection. Recycle, and then start the program thread

  • Concurrent: A user thread executes concurrently (but not necessarily in parallel and may execute alternately) with the garbage collector thread without pausing the user program
    • The user program continues to run while the garbage collector thread runs on another CPU. For example, CMS and G1

5. Safety points and areas

Safepoint

  • Program execution does not always pause to begin GC, but only at certain points, called safe points
  • The choice of safe points is important, too few can lead to long GC waits, and too frequent can lead to runtime performance issues. The execution time of most instructions is very short and is usually based on the characteristics that make the program run for a long time. For example, select some instructions that take a long time to execute as safe points, such as method calls, loop jumps, exception jumps, etc
  • How do you check that all threads come to a standstill at the nearest safe point during GC?
    • Preemptive interrupts (no virtual machine currently uses them) : First interrupts all threads, and if any thread is not safe, restores the thread to the safe point
    • Active interrupt: set an interrupt flag, each thread running to a safe point when the active rotation of this flag, if the terminal flag is true, will suspend itself interrupt

Safe Region

  • The safe-point mechanism ensures that the program will run into an accessible GC within a short period of time. But what about when the program “doesn’t execute”? For example, a thread is insleepThe state orblockedState, when the JVM cannot respond to interrupt requests, “walks” to a safe point to interrupt suspend, and the JVM is less likely to wait for the thread to wake up. In this case, safety zones are needed
  • A safe zone is a code snippet where the reference relationship of the object does not change and it is safe to start GC anywhere in the zone. We can also think of a security zone as an extended security point
  • Actual execution:
    • When a thread runs into code that is in a safe zone, it first signals that it has entered the safe zone. If GC occurs during this time, the JVM ignores threads that are identified as being in the safe zone state (that is, stops the user thread to perform GC).
    • When a thread is about to leave the safe zone, it checks to see if the JVM has completed GC, and if so, continues running, otherwise the thread must wait until it receives a signal that it is safe to leave the safe zone

6. Back to quotes

We want to describe objects that can remain in memory when there is enough space; If memory space is tight after garbage collection, these objects can be discarded

After JDK1.2, Java has expanded the concept of Reference, including Strong Reference, Soft Reference, Weak Reference and Phantom Reference. The intensity of these four citations gradually decreases in turn. With the exception of strong references, the other three can be found in the java.lang.ref package

In the Reference subclass, only the FinalReference is visible within the package. The other three Reference types are public and can be used directly in applications

  • Strong ReferenceThe most traditional definition of reference refers to the reference assignment that is common in program codeObject obj = new Object()This reference relationship.In any case, the garbage collector will never reclaim the referenced object as long as the strong reference relationship exists
  • Soft references: Objects are collected for a second collection before the system runs out of memory. An out-of-memory exception is thrown if there is not enough memory after this collection
  • Weak Reference: Objects associated with Weak references only survive until the next garbage collection. When the garbage collector works, objects associated with weak references are reclaimed regardless of whether there is enough memory
  • Phantom Reference: The existence of a Phantom Reference does not affect the lifetime of an object, nor can it be used to obtain an instance of an object. The only purpose of setting up a virtual reference association for an object is to receive a system notification when the object is reclaimed

6.1 Strong Reference – No reclamation

  • In Java programs, the most common type of reference is strong reference (more than 99% of all strong references in normal systems), which is our most common plain object reference and the default reference type. When used in the Java languagenewWhen the operator creates a new object and assigns it to a variable, the variable becomes a strong reference to the object.
  • Strongly referenced objects are touchable, and the garbage collector will never reclaim the referenced objects. For an ordinary object, if no other reference relationship exists, the corresponding (strong) reference is explicitly assigned to as long as the scope of the reference is exceedednullThat is, it can be collected as garbage, but the exact timing of the collection depends on the garbage collection policy
  • In contrast, soft reference, weak reference and virtual reference objects are soft touch, weak touch and virtual touch, under certain conditions, can be reclaimed. So strong references are one of the main causes of memory leaks

The test code

/** * -XX:+PrintGCDetails */
public class StrongReferenceTest {
    public static void main(String[] args) {
        StringBuffer str = new StringBuffer("Hello");
        StringBuffer str1 = str;

        str = null;
        System.gc();

        try {
            // Sleep 3s to ensure that GC occurs
            Thread.sleep(3000);
        } catch(InterruptedException e) { e.printStackTrace(); } System.out.println(str1); }}Copy the code

The output is as follows:

[GC (System.gc()) [PSYoungGen: 2621K->528K(76288K)] 2621K->536K(251392K), 0.0007413 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 528K->0K(76288K)] [ParOldGen: 8K->338K(175104K)] 536K->338K(251392K), [Metaspace: 2651K->2651K(1056768K)], 0.0032001 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Hello
Heap
 PSYoungGen      total 76288K, used 5243K [0x000000076ab00000.0x0000000770000000.0x00000007c0000000)
  eden space 65536K, 8% used [0x000000076ab00000.0x000000076b01ef10.0x000000076eb00000)
  from space 10752K, 0% used [0x000000076eb00000.0x000000076eb00000.0x000000076f580000)
  to   space 10752K, 0% used [0x000000076f580000.0x000000076f580000.0x0000000770000000)
 ParOldGen       total 175104K, used 338K [0x00000006c0000000.0x00000006cab00000.0x000000076ab00000)
  object space 175104K, 0% used [0x00000006c0000000.0x00000006c0054b38.0x00000006cab00000)
 Metaspace       used 3166K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 349K.capacity 386K.committed 512K.reserved 1048576K
Copy the code

The local variable STR refers to the heap space where the StringBuffer instance resides, and STR is a strong reference to the StringBuffer instance if the instance can be manipulated by STR. The two references in this example are strong references. Strong references have the following characteristics:

  • Strong references allow direct access to the target object
  • The object pointed to by a strong reference will not be reclaimed by the system at any time. The VM would rather throw an OOM exception than reclaim the object pointed to by a strong reference
  • Strong references are more likely to cause memory leaks

6.2 Soft Reference – If the memory is insufficient, it will be reclaimed

  • Soft references are used to describe objects that are useful but not necessary. Objects that are associated only with soft references are listed in the recycle scope for a second collection before the system is about to run out of memory. An out of memory exception is thrown if there is not enough memory for this collection (note: the out of memory is not because of the soft reference, because the soft reference has already been reclaimed)
  • Soft references are often used to implement memory sensitive caching. For example, caching is useful for soft references. If you have free memory, you can keep the cache for a while and clean it up when you run out of memory. This ensures that you don’t run out of memory while using the cache
  • When the garbage collector decides to reclaim soft reachable objects at some point, it cleans up soft references and optionally stores them in a Reference Queue.
  • Similar to weak references, except that the Java virtual machine keeps soft references alive as long as possible and cleans them up when necessary

The test code

import java.lang.ref.SoftReference;

/** * -Xmx10m -Xms10m */
public class SoftReferenceTest {

    public static class User {

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int id;
        public String name;

        @Override
        public String toString(a) {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\' ' +
                    '} '; }}public static void main(String[] args) {
        // Create an object and create a soft reference
        /** * User user = new User(1, "nasuf"); * SoftReference
      
        userSoftReference = new SoftReference<>(user); * user = null; // Cancel strong references * * the following line of code is equivalent to the above three lines */
      
        SoftReference<User> userSoftReference = new SoftReference<>(new User(1."nasuf"));
        /** * Retrieves strongly referenced objects from soft references * due to sufficient heap space memory, soft-referenced reachable objects are not reclaimed */
        System.out.println(userSoftReference.get());

        System.gc();
        System.out.println("After GC:");
        System.out.println(userSoftReference.get());

        try {
            // Make the system think memory resources are tight
            byte[] b = new byte[1024 * 1024 * 7];
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            /** * Retrieves data from the soft reference again * Before throwing OOM, the garbage collector will reclaim the soft reference's reachable object */System.out.println(userSoftReference.get()); }}}Copy the code

The output is as follows:

User{id=1, name='nasuf'}
After GC:
User{id=1, name='nasuf'}
null
java.lang.OutOfMemoryError: Java heap space
	at com.nasuf.jvm.SoftReferenceTest.main(SoftReferenceTest.java:51)

Process finished with exit code 0
Copy the code

6.3 Weak Reference – Weak Reference is reclaimed

  • Weak references are also used to describe non-essential objects, and only objects associated with weak references will survive until the next garbage collection occurs. During system GC, whenever a weak reference is found, objects associated only with weak references are reclaimed, regardless of whether the system heap space is fully used.
  • However, because garbage collector threads are usually of low priority, objects holding weak references are not always found quickly. In this case, weak reference objects can exist for a long time
  • Like soft references, when constructing weak references, you can specify a reference queue. When a weak reference object is reclaimed, it is added to the specified reference queue. By this queue, you can track the reclaim status of the object
  • Both soft and weak references are great for storing cache data that is not essential. If you do this, when the system runs out of memory, the cached data will be reclaimed without running out of memory. When memory resources are sufficient, these cached data can exist for quite a long time, thus speeding up the system
  • The biggest difference between a weak-reference object and a soft-reference object is that when the GC is collecting, the algorithm checks whether the soft-reference object is being collected. For weakly referenced objects, GC always collects. Weakly referenced objects are easier and faster to collect by GC

WeakReference source

public class WeakReference<T> extends Reference<T> {

    /**
     * Creates a new weak reference that refers to the given object.  The new
     * reference is not registered with any queue.
     *
     * @param referent object the new weak reference will refer to
     */
    public WeakReference(T referent) {
        super(referent);
    }

    /**
     * Creates a new weak reference that refers to the given object and is
     * registered with the given queue.
     *
     * @param referent object the new weak reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q); }}Copy the code

The test code

import java.lang.ref.WeakReference;

public class WeakReferenceTest {
    public static class User {

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int id;
        public String name;

        @Override
        public String toString(a) {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\' ' +
                    '} '; }}public static void main(String[] args) {
        // Construct a weak reference
        WeakReference<User> weakReference = new WeakReference<>(new User(1."nasuf"));
        // Retrieve the object from the weak reference
        System.out.println(weakReference.get());
        System.gc();
        // Reclaim its memory regardless of whether you currently have enough memory
        System.out.println("After GC:");
        // Try again to get the object from the weak referenceSystem.out.println(weakReference.get()); }}Copy the code

The output is as follows:

User{id=1, name='nasuf'}
After GC:
null
Copy the code

6.4 Phantom Reference – Object reclamation trace

  • Virtual references, also known as “ghost references” or “phantom references,” are the weakest of all reference types
  • The existence of virtual references to an object does not determine its life cycle at all. If an object holds only virtual references, it is almost as good as no references and can be collected by the garbage collector at any time
  • Virtual references cannot be used alone, nor can they be used to obtain the referenced object. When attempting to pass a virtual referenceget()Method to get an object, alwaysnull
  • The sole purpose of setting a virtual reference association for an object is to track the garbage collection process. For example, you can receive a system notification when the object is reclaimed by the collector
  • Virtual references must be used with reference queues. A virtual reference must be supplied as a parameter to a reference queue when it is created. When the garbage collector is about to reclaim an object and finds that it has a virtual reference, it adds the virtual reference to the reference queue after the object is reclaimed to inform the application of the object’s recycling status
  • Because a virtual reference can track the reclaim time of an object, it is also possible to place some resource release operations in a virtual reference for execution and recording

PhantomReference source

public class PhantomReference<T> extends Reference<T> {

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get(a) {
        return null;
    }

    /**
     * Creates a new phantom reference that refers to the given object and
     * is registered with the given queue.
     *
     * <p> It is possible to create a phantom reference with a <tt>null</tt>
     * queue, but such a reference is completely useless: Its <tt>get</tt>
     * method will always return null and, since it does not have a queue, it
     * will never be enqueued.
     *
     * @param referent the object the new phantom reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q); }}Copy the code

The test code

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceTest {
    public static PhantomReferenceTest obj; // The declaration of the current class object
    static ReferenceQueue<PhantomReferenceTest> phantomQueue = null; // Reference queue

    public static class CheckRefQueue extends Thread {
        @Override
        public void run(a) {
            while (true) {
                if(phantomQueue ! =null) {
                    PhantomReference<PhantomReferenceTest> objt = null;
                    try {
                        objt = (PhantomReference<PhantomReferenceTest>)phantomQueue.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(objt ! =null) {
                        System.out.println("Trace garbage collection process: PhantomReferenceTest instance was GC");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize(a) throws Throwable {
        super.finalize();
        System.out.println("Call the Finalize () method of the current class");
        obj = this;
    }

    public static void main(String[] args) {
        Thread t = new CheckRefQueue();
        t.setDaemon(true); Daemons are terminated when there are no non-daemons left in the program
        t.start();

        phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
        obj = new PhantomReferenceTest();
        PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<>(obj, phantomQueue);

        // The object in the virtual reference cannot be obtained
        System.out.println("Get the object in PhantomReference:" + phantomRef.get());

        try {
            // Remove the strong reference
            obj = null;
            // The first time GC is performed, it cannot reclaim the object because it can be resurrected
            System.out.println("First GC");
            System.gc();
            // Pause for 1s to ensure that GC executes and finalize method is called
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("Obj is null");
            } else {
                System.out.println(Obj "available");
            }
            System.out.println("Second GC");
            obj = null;
            // Once the obj object is reclaimed, the virtual reference object is placed in the reference queue
            System.gc();
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("Obj is null");
            } else {
                System.out.println(Obj "available"); }}catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The output is as follows:

Get the object in PhantomReference:nullThe first GC calls the finalize() method obj of the current class so that the second GC can track the garbage collection process: PhantomReferenceTest instance is GC obj isnull

Process finished with exit code 0
Copy the code

6.5 Final Reference

  • It is used to implement objectsfinalize()Method can also be a finalizer reference
  • No manual encoding is required, and it is used internally with reference queues
  • During GC, finalizer references are enqueued byFinalizerThe thread finds the referenced object through a finalizer reference and calls itsfinalize()Method to reclaim the referenced object on the second GC

FinalReference source

/** * Final references, used to implement finalization */
class FinalReference<T> extends Reference<T> {

    public FinalReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q); }}Copy the code