Before introducing various references, let's briefly introduce garbage collection.
What is garbage collection?
- Garbage Collection (GC), as its name implies, is to release the space occupied by garbage and prevent memory leakage. Effective use of the memory that can be used to clear and recycle objects that have been dead or not used for a long time in the heap.
- Before the Java language came out, everybody was writing C or C++ programs desperately. At this time, there is a big contradiction. C++ and other languages create objects to constantly open up space, and when not used, they need to constantly release controls. They need to write both constructors and destructors, often repeated allocated, and then constantly destructed. So, some people have proposed, can you write a program to achieve this function, each time create, release the control when reusing this code, without repeating the writing?
- In 1960, MIT-based Lisp first proposed the concept of garbage collection, which was used to handle constant destructive operations such as the C language, while Java was not yet born. So in fact, GC is not the patent of Java. The history of GC is far greater than that of Java.
Garbage collection in Java is based on accessibility analysis algorithm to determine whether an object survives or not.
Reachability analysis algorithm
In the mainstream implementations of the mainstream commercial programming languages (Java, C#, even the old Lisp mentioned earlier), it is called Reachability Analysis (Reachability Analysis) to determine whether an object is alive or not. The basic idea of this algorithm is to use a series of objects called "GC Roots" as the starting point and search downward from these nodes. The search path is called Reference Chain. When there is no link between an object and GC Roots (in graph theory, it is not reachable from GC Roots to this object), it proves that this object is not. Available. As shown in Figure 3-1, objects object 5, object 6, and object 7 are related to each other, but they are not reachable to GC Roots, so they will be judged to be recyclable objects.
In the Java language, objects that can be used as GC Roots include the following:
- Objects referenced in virtual machine stacks (local variable tables in stack frames).
- Objects referenced by static attributes of the middle class in the method area.
- Objects referenced by a constant in the method area.
- Objects referenced by JNI (commonly referred to as Native methods) in the local method stack.
Various Quotes
Whether an object survives depends on the reference.
Before JDK 1.2, the definition of reference in Java was very traditional: if the value stored in reference-type data represents the starting address of another piece of memory, it is said that this piece of memory represents a reference. This definition is very simple, but too narrow, an object in this definition is only cited or not quoted in two states. It is impossible to describe how to describe some "tasteless, abandoned" objects.
We want to describe objects that are kept in memory when there is enough memory space; objects that can be discarded if the memory space is still very tight after garbage collection. The caching function of many systems is in line with such application scenarios.
After JDK 1.2, Java extends the concept of reference, dividing it into four categories: Strong Reference, Soft Reference, Weak Reference and Phantom Reference, which gradually weaken in turn.
reference type | GC strategy | brief introduction |
---|---|---|
Strong Reference | Never Recycle (on the premise that GC ROOT can be referenced) | The most basic reference to Object obj=new Object() |
Soft Reference | Recycling before OOM | SoftReference |
Weak Reference | Before the next GC | WeakReference |
Phantom Reference | The unknown is that it can be recycled at any time. | PhantomReference |
Strong citation
Strong reference is the most basic way of reference, Object obj=new Object(), referring to the starting address of another piece of memory. Strongly referenced object recycling is based on "reachability analysis" algorithm, which may be recycled when the object is not reachable.
For example, the new object in the method refers to the local variable assigned to the method (the local variable table stored in the stack frame). When the method is finished, the stack frame is out of the stack, and the object is naturally unreachable, and unreachable may be recovered.
Soft reference
Soft references are used to describe objects that are useful but not necessary. For objects associated with soft references, these objects will be included in the recovery scope for a second recovery before the system will have memory overflow exceptions. If there is not enough memory for this reclamation, a memory overflow exception will be thrown. At JDK
After 1.2, the SoftReference class is provided to implement soft reference.
SoftReference<RefObj> ref = new SoftReference<RefObj>(refObj);
Write an example to test the GC strategy for soft references:
# JVM OPTIONS: -XX:+PrintGCDetails -Xmx5m public class ReferenceTest { private List<RefObj> refObjs = new ArrayList<>(); private SoftReference<RefObj> ref = new SoftReference<RefObj>(createRefObj(4096*256));//1m public void add(){ refObjs.add(createRefObj(4096)); } private RefObj createRefObj(int dataSize){ RefObj refObj = new RefObj(); byte[] data = new byte[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = Byte.MAX_VALUE; } refObj.setData(data); return refObj; } public void validRef(){ System.out.println(ref.get()); } public static void main(String[] args) { ReferenceTest referenceTest = new ReferenceTest(); for (int i = 0; i < 1200; i++) { //Increasing Reactor Size referenceTest.add(); //After adding, check whether objects in SoftReference are recycled referenceTest.validRef(); } } private class RefObj{ private byte[] data; public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } }
A RefObjList and a SoftReference are maintained in Reference Test, adding objects to RefObjList continuously, increasing heap size until memory overflows. To see if the object referenced in SoftReference still exists
Test results:
# Intercept a key section [Full GC (Ergonomics) [PSYoungGen: 1023K->1021K(1536K)] [ParOldGen: 4073K->4073K(4096K)] 5097K->5094K(5632K), [Metaspace: 3534K->3534K(1056768K)], 0.0017581 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] reference.ReferenceTest$RefObj@58372a00 reference.ReferenceTest$RefObj@58372a00 reference.ReferenceTest$RefObj@58372a00 reference.ReferenceTest$RefObj@58372a00 reference.ReferenceTest$RefObj@58372a00 [Full GC (Ergonomics) [PSYoungGen: 1024K->1021K(1536K)] [ParOldGen: 4093K->4093K(4096K)] 5117K->5114K(5632K), [Metaspace: 3534K->3534K(1056768K)], 0.0014771 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 1021K->0K(1536K)] [ParOldGen: 4093K->4072K(4096K)] 5114K->4072K(5632K), [Metaspace: 3534K->3534K(1056768K)], 0.0060554 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] null .....ellipsis [Full GC (Allocation Failure) [PSYoungGen: 1022K->1022K(1536K)] [ParOldGen: 4093K->4093K(4096K)] 5116K->5116K(5632K), [Metaspace: 3534K->3534K(1056768K)], 0.0014051 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Exception in thread "main" [Full GC (Ergonomics) [PSYoungGen: 1024K->0K(1536K)] [ParOldGen: 4094K->981K(4096K)] 5118K->981K(5632K), [Metaspace: 3538K->3538K(1056768K)], 0.0037282 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] java.lang.OutOfMemoryError: Java heap space at reference.ReferenceTest.createRefObj(ReferenceTest.java:21) at reference.ReferenceTest.add(ReferenceTest.java:16) at reference.ReferenceTest.main(ReferenceTest.java:35) Heap PSYoungGen total 1536K, used 39K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000) eden space 1024K, 3% used [0x00000000ffe00000,0x00000000ffe09e10,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 4096K, used 981K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000) object space 4096K, 23% used [0x00000000ffa00000,0x00000000ffaf55f0,0x00000000ffe00000) Metaspace used 3565K, capacity 4564K, committed 4864K, reserved 1056768K class space used 384K, capacity 388K, committed 512K, reserved 1048576K
From the program + GC log, it can be seen that after a GC (before OOM), the object referenced in SoftReference can not be obtained and has been reclaimed by GC.
Weak reference
Weak references are also used to describe non-essential objects, but they are weaker than soft references, and objects associated with weak references can only survive until the next garbage collection occurs. When the garbage collector is working, regardless of whether the current memory is sufficient or not, objects associated with only weak references are reclaimed. At JDK
After 1.2, WeakReference classes are provided to implement weak references.
Weak references and soft references are used in the same way, but the corresponding classes are different and the GC strategy is different.
WeakReference<RefObj> ref = new WeakReference<RefObj>(refObj);
The test code based on the above soft reference is slightly modified to test the GC policy of the weak reference:
public class ReferenceTest { private List<RefObj> refObjs = new ArrayList<>(); private WeakReference<RefObj> ref = new WeakReference<RefObj>(createRefObj(4096*256));//1m public void add(){ refObjs.add(createRefObj(4096)); } private RefObj createRefObj(int dataSize){ RefObj refObj = new RefObj(); byte[] data = new byte[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = Byte.MAX_VALUE; } refObj.setData(data); return refObj; } public void validRef(){ System.out.println(ref.get()); } public static void main(String[] args) { ReferenceTest referenceTest = new ReferenceTest(); referenceTest.validRef(); referenceTest.add(); //Manual GC to see if objects in WeakReference still exist System.gc(); referenceTest.validRef(); } private class RefObj{ private byte[] data; public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } }
Test results:
[GC (Allocation Failure) [PSYoungGen: 1526K->512K(1536K)] 1992K->1266K(5632K), 0.0005932 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] reference.ReferenceTest$RefObj@58372a00 [GC (System.gc()) [PSYoungGen: 706K->512K(1536K)] 2484K->2346K(5632K), 0.0005772 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 512K->0K(1536K)] [ParOldGen: 1834K->972K(4096K)] 2346K->972K(5632K), [Metaspace: 3493K->3493K(1056768K)], 0.0062458 secs] [Times: user=0.16 sys=0.00, real=0.01 secs] null Heap PSYoungGen total 1536K, used 31K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000) eden space 1024K, 3% used [0x00000000ffe00000,0x00000000ffe07cc8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) ParOldGen total 4096K, used 972K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000) object space 4096K, 23% used [0x00000000ffa00000,0x00000000ffaf31d8,0x00000000ffe00000) Metaspace used 3500K, capacity 4500K, committed 4864K, reserved 1056768K class space used 381K, capacity 388K, committed 512K, reserved 1048576K
From the log, after any GC (including manual GC), GC reclaims objects in WeakReference, regardless of whether the current memory is sufficient.
Virtual reference
Virtual reference, also known as ghost reference or phantom reference, is the weakest citation relationship. Whether an object has a virtual reference will not affect its lifetime at all, nor can it obtain an object instance through a virtual reference. The only purpose of setting virtual reference associations for an object is to receive a system notification when the object is reclaimed by the collector. At JDK
After 1.2, the PhantomReference class is provided to implement virtual references.
In other words, virtual referenced objects can be recycled at any time.
The use of virtual references is similar to that of soft references / weak references, except that when constructed, a queue needs to be specified
//Reference queue, when the referenced object is reclaimed, the Reference object itself is added to the reference Queue, which is equivalent to receiving a notification. //Soft reference / weak reference has this construction parameter, but in virtual reference this parameter becomes obligatory. ReferenceQueue<RefObj> referenceQueue = new ReferenceQueue<>(); PhantomReference<RefObj> ref = new PhantomReference<RefObj>(refObj,referenceQueue); SoftReference<RefObj> ref = new SoftReference<RefObj>(refObj,referenceQueue); WeakReference<RefObj> ref = new WeakReference<RefObj>(refObj,referenceQueue);
Or make some changes based on the test code above to test virtual references and recycle queues
public class ReferenceTest { private List<RefObj> refObjs = new ArrayList<>(); private ReferenceQueue<RefObj> referenceQueue = new ReferenceQueue<>(); private PhantomReference<RefObj> ref = new PhantomReference<RefObj>(createRefObj(4096*256),referenceQueue);//1m public void add(){ refObjs.add(createRefObj(4096)); } /** * Start a sub-thread to monitor the recycled reference queue */ public void referenceQueueMonitor(){ new Thread(()->{ try{ Reference<RefObj> weakReference; while ((weakReference = (Reference<RefObj>) referenceQueue.remove())!=null){ System.out.println("collect "+weakReference); } }catch (InterruptedException e){} }).start(); } private RefObj createRefObj(int dataSize){ RefObj refObj = new RefObj(); byte[] data = new byte[dataSize]; for (int i = 0; i < dataSize; i++) { data[i] = Byte.MAX_VALUE; } refObj.setData(data); return refObj; } public void validRef(){ System.out.println(ref.get()); } public static void main(String[] args) { ReferenceTest referenceTest = new ReferenceTest(); referenceTest.referenceQueueMonitor(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } referenceTest.validRef(); referenceTest.add(); System.gc(); referenceTest.validRef(); } private class RefObj{ private byte[] data; public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } }
Test results:
#For the first time: [GC (Allocation Failure) [PSYoungGen: 1525K->512K(1536K)] 3466K->2661K(5632K), 0.0005258 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] null [GC (System.gc()) [PSYoungGen: 567K->512K(1536K)] 2717K->2717K(5632K), 0.0005225 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 512K->0K(1536K)] [ParOldGen: 2205K->2213K(4096K)] 2717K->2213K(5632K), [Metaspace: 4379K->4379K(1056768K)], 0.0078481 secs] [Times: user=0.13 sys=0.00, real=0.01 secs] null #Finally, the recycling log is printed. collect java.lang.ref.PhantomReference@7be2d776 #Times N: [GC (Allocation Failure) [PSYoungGen: 1536K->512K(1536K)] 3440K->2536K(5632K), 0.0005524 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] #Print the recovery log first collect java.lang.ref.PhantomReference@30a3ced6 null [GC (System.gc()) [PSYoungGen: 573K->512K(1536K)] 2597K->2580K(5632K), 0.0005956 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 512K->0K(1536K)] [ParOldGen: 2068K->2252K(4096K)] 2580K->2252K(5632K), [Metaspace: 4387K->4387K(1056768K)], 0.0082860 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] null
From the log, the results are inconsistent many times. Since GC occurs when the program starts, it triggers GC manually in the middle. Considering the characteristics of virtual reference, the object lifetime of virtual reference is uncertain, and the object may be recycled at any time.
Reference scenarios for various references
We want to describe objects that are kept in memory when there is enough memory space; objects that can be discarded if the memory space is still very tight after garbage collection. The caching function of many systems is in line with such application scenarios.
The easiest thing to think about is caching, releasing part of the data when there is insufficient memory, and eliminating strategies like Redis/Ehcache.
The following are the application scenarios of JDK/framework:
- java.util.WeakHashMap - jdk
- java.util.concurrent.ArrayBlockingQueue - jdk
- This cache is heavily used in org. spring framework. util. Concurrent Reference HashMap - spring, including spring-Bean Utils
Reference resources
- Deep Understanding of Java Virtual Machines: JVM Advanced Features and Practical Warfare (2nd Edition) - Zhou Zhiming
- Let's talk about Java garbage collection from beginning to end.