Prior to JDK 1.2, an object created could only be used by a program if it was in a reachaable state, and the garbage collector would recycle the object once it was found to be useless. However, in some cases, we want some objects that do not need to be collected immediately or that do not need to be collected immediately from a global perspective. For example, the cache system is designed so that some temporarily unused objects can still be placed in memory, rather than being reclaimed immediately, if memory is not too tight or to improve performance. So, starting with JDK 1.2, Java designers subdivide references to objects into Strong references, Soft references, Weak references, and Phantom references Reference is divided into four levels, the main difference is reflected in the priority of GC collection: strong Reference -> soft Reference -> weak Reference -> virtual Reference. That is to say, starting with JDK 1.2, when the garbage collector reclaims an object, its reactability analysis needs to take into account the reference strength of the object, which means that now the object’s validity = reactability + reference type. Look at the class hierarchy as follows:

1. Four types of references

1. Strong Reference

Commonly used in code, like Person Person =new Person(); If an object has a strong reference, GC will not reclaim the referenced object under any circumstances. When memory space runs out, the JAVA virtual machine would rather throw an OutOfMemoryError to terminate the application than reclaim objects with strong references.

2. Soft Reference

Indicates that an object is in a useful but not necessary state. If an object has a soft reference, GC will not reclaim the object if there is enough memory; When the memory space is insufficient, the GC recollects the object’s memory (the reclamation occurs before the OutOfMemoryError).

Person person=new Person();
SoftReference sr=new SoftReference(person);Copy the code

A soft reference can be used in conjunction with a ReferenceQueue (ReferenceQueue). If the object referenced by the soft reference is reclaimed by GC, the Java virtual machine will add the soft reference to the associated ReferenceQueue so that the soft reference can be reclaimed at the appropriate time. However, due to the low priority of the GC thread, a manual call to system.gc () usually does not perform GC immediately, so the object referenced by the weak reference is not necessarily reclaimed immediately.

3. Weak References

Used to describe objects that are not required. It is similar to soft references, but weaker than soft references: weak references have shorter lives. During the scanning process, if only the object with weak reference association is found by GC, it will reclaim the object with weak reference association. In other words, objects associated with weak references will be reclaimed by GC regardless of the current memory shortage.

Person person=new Person();
WeakReference wr=new WeakReference(person);Copy the code

Similarly, a weak reference can be used in conjunction with a ReferenceQueue (ReferenceQueue). If the object referenced by the weak reference is reclaimed by GC, the Java virtual machine will add the weak reference to the associated ReferenceQueue so that the weak reference can be reclaimed at the appropriate time.

4. Phantom Reference

A virtual reference is the same as no reference, which means that it can be collected by GC at any time. The purpose of setting a virtual reference is to receive a system notification when the object associated with the virtual reference is collected by the garbage collector. The difference between a virtual reference and a weak reference is that a virtual reference must be used in conjunction with a ReferenceQueue (ReferenceQueue). Its activity during GC collection is as follows:

ReferenceQueue queue=new ReferenceQueue();
PhantomReference pr=new PhantomReference(object.queue);Copy the code

That is, when GC reclaims an object, if it finds that the object has a virtual reference, the virtual reference of the object will be added to the reference queue associated with the object before reclaiming it. A program can determine whether a referenced object has been collected by GC by determining whether a virtual reference has been added to the reference queue.

ReferenceQueue and Reference

1. Meaning and function of ReferenceQueue

Usually we translate the ReferenceQueue as the Reference queue, in other words, the queue that holds the Reference object. When the object referenced by the Reference object is collected by GC, the Reference object is added to the end of the Reference queue (ReferenceQueue).

ReferenceQueue commonly used methods: public Reference poll () : to remove an element from the queue, the queue is empty, return null; Public Reference remove() : remove an element from the queue, or block until an element is available; Public Reference remove(long timeout) : remove an element from the queue. If no element is available, block until it is available or block for more than timeout milliseconds.

See the following code:

ReferenceQueue< Person> rq=new ReferenceQueue();
Person person=new Person();
SoftReference sr=new SoftReference(person,rq);Copy the code

In this code, there are two types of reference to the Person object: a strong reference to Person, and a soft reference to SR. The SR strongly references a SoftReference object, which soft-references a Person object. When the Person is reclaimed, the objects strongly referenced by the SR will be placed at the end of the queue in rQ. Using ReferenceQueue, you can clear a SoftReference that has lost a SoftReference object by doing the following:

SoftReference ref=null; while((ref=(Person)rq.poll())! =null){ }Copy the code

2. Reference class

Reference is SoftReference, WeakReference, PhantomReference class parent, its internal through a next field to build a list of Reference types of one-way, and corresponding Reference queue queue field contains the Reference object, If not specified in the Reference subclass constructor, a ReferenceQueue.NULL queue is associated by default.

3. Usage scenarios of the four reference types

Strong reference types are ubiquitous in code, and there’s no need to explain too much about soft and weak references: both can implement caching, but soft reference implementations are often used on the server, and are more memory hungry and garbage collector sensitive on mobile devices. Therefore, caching in Android is often implemented with weak references (such as LruCache). There is a scenario in development: user information query. Without considering the change of user information, there are usually two ways to achieve it: 1. During each query, the database is connected to obtain information. The disadvantages are frequent IO reads and long average response time. The advantage is low memory usage. 2. During the first query, the user information is stored in the memory after the database is read. After each query, the user information is read from the memory. Now we use the second solution, based on the cache design, the code is as follows:

User.java

public class User {

    private String id;
    private String name;
    private int age;
    public User(String id) {
        super();
        this.id = id;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    }
Copy the code

UserCache.java

public class UserCache { private static UserCache cache; private Hashtable userRefs; private ReferenceQueue q; private class UserRef extends SoftReference{ private String key=""; public UserRef(User user,ReferenceQueue q){ super(user,q); key=user.getId(); } } private UserCache(){ userRefs=new Hashtable<>(); q=new ReferenceQueue<>(); } public static UserCache getInstance(){ synchronized (UserCache.class) { if(cache==null){ synchronized (UserCache.class) { cache=new UserCache(); } } } return cache; } /** * Cache user data * @param user */ private void cacheObject(user user){cleanCache(); UserRef ref = new UserRef(user, q); userRefs.put(user.getId(), ref); @param id * @return */ public User getObject(String id){User User =null; if(userRefs.containsKey(id)){ System.err.println("get data from cache"); UserRef ref = userRefs.get(id); user=ref.get(); } if(user==null){ System.err.println("get data from db"); user=new User(id); user.setName("dong"+new Random().nextInt(10)); cacheObject(user); } return user; } private void cleanCache() { UserRef ref=null; while((ref=(UserRef) q.poll())! =null){ userRefs.remove(ref.key); } } public void clearAll(){ cleanCache(); userRefs.clear(); System.gc(); System.runFinalization(); }}Copy the code

UserCacheTest.java

public class UserCacheTest { public static void main(String[] args) { UserCache cache = UserCache.getInstance(); for(int i=0; i<2; i++){ System.out.println(cache.getObject("123").toString()); } cache.clearAll(); System.out.println(cache.getObject("123").toString()); }}Copy the code

Simple caching is done, and the output is as follows:

get data from db

get data from cache

User [id=123, name=dong3, age=0]

User [id=123, name=dong3, age=0]

get data from db

User [id=123, name=dong2, age=0]

4. What reference type is it?

If an object has multiple reference types, how does garbage collection determine the reachability of the object? The principles are as follows:

The reachability of a single reference chain is determined by the weakest reference type. The reachability of multiple reference chains is determined by the strongest reference type.

An example is shown in the figure below:



After analyzing Object 2, the path 1-1 — >2-1 takes the weakest reference, soft reference; In path 1-2 — >2-2, take the weakest reference and virtual reference; Take the strongest reference, the soft reference, from the two paths. So Object 2 is the final reference type is soft reference.

The sample code

We have a piece of code to help us understand these four:

private void test_gc1(){ String a=new String("wohenhao"); SoftReference< ? > softReference=new SoftReference(a); WeakReference< ? > weakReference=new WeakReference(a); System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); a=null; System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); softReference.clear(); System.gc(); System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); } private void test_gc2(){ String a=new String("wohenhao"); SoftReference< ? > softReference=new SoftReference(a); WeakReference< ? > weakReference=new WeakReference(a); System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); a=null; System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); System.gc(); System. The out. Println (" strong reference: "+ a +" \ n soft references "+ softReference. The get () +" \ n a weak reference "+ weakReference. The get () +" \ n "); }Copy the code