In the past, when learning strong weak virtual reference, I just looked at the blog and didn't write my own code to practice and prove it. As a result, I forgot it after reading it every time. Later, I made up my mind to knock on the code by myself, so that I could be more impressed. The ancients said that I had to feel shallow on the paper and never knew that I had to do it.
Four references in Java
There are four reference types in Java: strong reference, soft reference, weak reference and virtual reference.
Why Java should design these four kinds of references
Java's memory allocation and memory recycling do not need the responsibility of the programmer. They are all the responsibility of the great JVM. Whether an object can be recycled depends on whether there is a reference to the object. The professional point is called reachability analysis.
Java designs these four references for two main purposes:
- The programmer can determine the life cycle of an object by means of code;
- There is recycling.
Strong citation
Strong reference is the most common kind of reference. 99.9999% of the code we write is strong reference:
Object o = new Object();
This is a strong reference, is it everywhere in the code, the most cordial.
As long as an object has a strong reference associated with it, the object will never be recycled. Even if there is insufficient memory, the JVM would rather throw an OOM than recycle it.
So when can it be recycled? When the association between a strong reference and an object is broken, it can be recycled.
We can manually associate the interrupt, and the method is particularly simple:
o = null;
We can call GC manually to see whether resources will be recycled if the association between strong references and objects is interrupted. In order to observe the recycling more conveniently and clearly, we need to write a new class, and then rewrite the finalize method. Let's do this experiment:
public class Student { @Override protected void finalize() throws Throwable { System.out.println("Student It's been recycled."); } }
public static void main(String[] args) { Student student = new Student(); student = null; System.gc(); }
Operation result:
Student is recycled
It is clear that the resources have been recycled.
Of course, in actual development, never rewrite the finalize method
In the actual development, we can see that some objects are manually assigned to NULL, which is probably to "specially remind" the JVM that this resource can be garbage collected.
Soft reference
Let's first see how to create a soft reference:
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());
Soft reference is to wrap objects with soft reference. When we need to get wrapped objects from soft reference objects, we just need to get them:
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student()); Student student = studentSoftReference.get(); System.out.println(student);
What are the characteristics of soft citation
When there is insufficient memory, the JVM's GC will be triggered. If there is still insufficient memory after GC, the wrapped object of the soft reference will be killed. That is to say, the JVM will recycle the object only when there is insufficient memory.
It's still the same. We have to do experiments to deepen our impression:
SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]); System.out.println(softReference.get()); System.gc(); System.out.println(softReference.get()); byte[] bytes = new byte[1024 * 1024 * 10]; System.out.println(softReference.get());
I defined a soft reference object, in which byte [] is wrapped, and byte [] occupies 10M, and then 10Mbyte [] is created.
To run the program, you need to bring a parameter:
-Xmx20M
Represents the maximum heap memory of 20 m.
Operation result:
[B@11d7fff [B@11d7fff null
It is clear that the byte [] wrapped by the soft reference object is still alive after GC is completed manually. However, when we create a 10M byte [], the maximum heap memory is not enough, so we kill the byte [] wrapped by the soft reference object. If we don't, we will throw OOM.
What is the use of soft reference? It is more suitable for caching. When the memory is enough, you can get the cache normally. When the memory is not enough, you will kill the cache first and not throw OOM immediately.
Weak reference
The use of weak reference is similar to that of soft reference, except that the keyword becomes WeakReference:
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]); System.out.println(weakReference.get());
The feature of soft reference is that whenever GC occurs, it will be recycled no matter whether the memory is enough or not:
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]); System.out.println(weakReference.get()); System.gc(); System.out.println(weakReference.get());
Operation result:
[B@11d7fff null
It is clear that there is enough memory, but when GC is triggered, the resource is recycled.
Weak references are useful in many places, such as ThreadLocal and WeakHashMap.
Virtual reference
Virtual reference is also called phantom reference. Let's see its use:
ReferenceQueue queue = new ReferenceQueue(); PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue); System.out.println(reference.get());
The use of virtual reference is quite different from the soft reference and weak reference mentioned above. We can run ReferenceQueue directly no matter what it is
null
Unexpectedly, null is printed out. Let's see the source code of get method:
public T get() { return null; }
This means that null is returned directly.
This is one of the characteristics of virtual reference: virtual reference cannot be used to obtain the real reference to an object.
What is the meaning of the existence of virtual reference? This is going back to the code above us. We need to copy the code to prevent you from turning up again:
ReferenceQueue queue = new ReferenceQueue(); PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue); System.out.println(reference.get());
To create a virtual reference object, in addition to passing in the wrapped object, we also passed in a ReferenceQueue, which can be seen from its name as a queue.
The second feature of virtual reference is that virtual reference must be used with ReferenceQueue. When GC is ready to recycle an object, if it finds that it has virtual reference, it will add the virtual reference to the ReferenceQueue associated with it before recycling.
Let's practice with code:
ReferenceQueue queue = new ReferenceQueue(); List<byte[]> bytes = new ArrayList<>(); PhantomReference<Student> reference = new PhantomReference<Student>(new Student(),queue); new Thread(() -> { for (int i = 0; i < 100;i++ ) { bytes.add(new byte[1024 * 1024]); } }).start(); new Thread(() -> { while (true) { Reference poll = queue.poll(); if (poll != null) { System.out.println("Virtual references are recycled:" + poll); } } }).start(); Scanner scanner = new Scanner(System.in); scanner.hasNext(); }
Operation result:
Student is recycled Virtual reference is recycled: java.lang.ref.PhantomReference@1ade6f1
We simply analyze the following code:
The first thread plugs data into the collection. With more and more data, GC will happen.
The second thread is in a dead cycle. Take the data from the queue. If the data is not null, print it.
From the running results, we can see that when GC occurs, virtual references will be recycled, and the recycled notifications will be put into ReferenceQueue.
What's the use of virtual references? In NIO, virtual reference is used to manage out of heap memory.
That's all about this blog.