An overview of
This paper will be based on the definition of weak references, and then step by step through the use of a case of deep source analysis of its principle, so as to make readers deeply understand what is a weak reference, how to use weak references, what scenarios will use a weak reference, weak references can solve the problem of what, and what is its source code to achieve, which involve memory, Principles of Garbage Collection
Function:
JDK (JDK)
Weak references are mainly used in mapping that does not prevent its key or value from being reclaimed. Just post it in English, Weak references are for implementing canonicalizing mappings that do not prevent their keys (or values) from Being reclaimed)
Personal Understanding:
Weak references exist for garbage collection services. It refers to an object, but does not prevent it from being recycled. If a strong reference is used, the referenced object cannot be reclaimed as long as the reference exists. Weak references do not have this problem. When the garbage collector runs, if all references to an object are weak references, the object is collected
In-depth case analysis:
Ideally, we want to be able to recycle an object as soon as gc occurs when we are no longer using it. But sometimes, because of our carelessness, memory can overflow in the worst case. This is especially true when a map with a long lifetime holds many objects with a short lifetime. Take a look at the following example
public class StrongRefenceDemo {
static Map<String, String> map;
public static void main(String[] args) throws Exception {
StrongRefenceDemo demo = new StrongRefenceDemo();
demo.strongTest();
System.out.println("Before GC occurs:" + map.size());
System.out.println("Start notifying GC");
// Note that the garbage collection is only done through the garbage collector and does not necessarily take place immediately
System.gc();
Thread.sleep(1000 * 5);
System.out.println("After GC occurs:" + map.size());
}
/** * strong reference test */
public void strongTest(a) {
map = new HashMap<>();
String key = new String("key");
String value = new String("value");
map.put(key, value);
key = null;
value = null; }}Copy the code
Output result after running:
Before gc occurs: 1 Notifying GC after GC occurs: 1Copy the code
As you can see from the output, even if we tell the JVM that we are no longer using the object by setting the key and value to null, the objects in the map are not collected by GC because the key and value are pointed to by a strongly referenced map. The garbage collector cannot recycle key and value objects. Map is defined as a static variable of Statis and is an object with a long usage life. There is a local key and value variable in the strongTest() method that ends its lifetime with the execution of the method, but the crude programmer forgot to remove it and the garbage collector can’t reclaim it. If there are many of these relatively short-lived objects, you can end up consuming all memory in the JVM.
But I’m curious here. If the key and value point to an object that is no longer needed after executing strongTest(), I might not be so good at calling remove to remove it. Do you want the garbage collector to decide if it’s ok to recycle? In fact, the answer is yes, this time is weak reference play, see the following program
public class WeakRefenceDemo {
static Map<WeakReference<String>, String> map;
public static void main(String[] args) throws Exception {
WeakRefenceDemo demo = new WeakRefenceDemo();
demo.weakTest();
System.out.println("Before GC occurs:" + map.size());
System.out.println("Start notifying GC");
// Note that the garbage collection is only done through the garbage collector and does not necessarily take place immediately
System.gc();
Thread.sleep(1000 * 5);
System.out.println("After GC occurs:" + map.size());
}
/** ** if reference test */
public void weakTest(a) {
map = new WeakHashMap<>();
String key = new String("key");
String value = new String("value");
map.put(new WeakReference<>(key), value);
key = null;
value = null; }}Copy the code
Run the code above to print the result
Before gc occurs: 1 Notifying GC after GC occurs: 0Copy the code
From the output 0, we can determine that the garbage collection was successful. what? In the whole process, we just changed HashMap to WeakHashMap and key from String to WeakReference. Because strings have only weak references to them, they can be garbage collected. Isn’t it easy? It would be a waste if you stopped studying weak references at this point
WeakHashMap depth resolution
In the above program fragment, in fact, only key is set as a WeakReference new WeakReference<>(key), then normally only the corresponding memory of this key is reclaimed, because there is no call to remove, the value and entry inside will not be reclaimed. So why is the output size 0? Good question, we dig into WeakHashMap source code, we found a magic method expungeStaleEntries(). Before looking at the source code first parse the concept of reference queue: when weak references are recycled, the object will be put in the reference queue, which means that the object from the reference queue is recycled object, first explained here, enough to meet our source code analysis, next will do detailed analysis
/** * Expunges stale entries from the table. */
private void expungeStaleEntries(a) {
// Retrieve a reclaimed object from the reference queue
for(Object x; (x = queue.poll()) ! =null;) {synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
// Set the value to NULL by iterating through the list to tell the garbage collector to recycle
// Note that WeakHashMap and HashMap data structures are made up of arrays + linked lists
while(p ! =null) {
Entry<K,V> next = p.next;
// When equal, it means that this is the object to be reclaimed
if (p == e) {
// Make the reclaimed object no longer referenced
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
// This tells the garbage collection by setting value to null
e.value = null; // Help GC
size--;
break; } prev = p; p = next; }}}}Copy the code
From the above code snippet, the general meaning is to retrieve the recovered object from the reference queue, and then look for the object in WeakHashMap. After finding the object, the corresponding value is also set to NULL, and the corresponding entry is set to NULL, to tell GC to reclaim it. It can be seen from the source that expungeStaleEntries() is a method that is called when executing any WeakHashMap method
/** * Returns the table after first expunging stale entries. */
private Entry<K,V>[] getTable() {
/ / is invoked
expungeStaleEntries();
return table;
}
/** * Returns the number of key-value mappings in this map. * This result is a snapshot, and may not reflect unprocessed * entries that will be removed before next attempted access * because they are no longer referenced. */
public int size(a) {
if (size == 0)
return 0;
/ / is invoked
expungeStaleEntries();
return size;
}
Copy the code
Now it’s clear why value is not set to a weak reference and can be reclaimed by calling the remove method without being explicit, right
Reference queue
From the above source code, we probably know the use of reference queue, so why use reference queue? If there were no reference queues, the above example would have required us to go through all the elements one by one. If there were a small number of elements, that would be fine. If there were a large number of elements, there would certainly be some performance issues. This problem can be easily solved with reference queues. We can see from WeakReference source code that there are two constructors, the second one is needed to pass in the reference queue
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
Copy the code
Reference queue Hello Word
Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference weakReference1 = new WeakReference<>(referent);
WeakReference weakReference2 = new WeakReference<>(referent, referenceQueue);
Copy the code
Note the details in use: Since weakly referenced objects can be reclaimed during GC, we need to check whether they are null before using them to avoid null pointer exceptions
Object referent3 = weakReference2.get();
if(referent3 ! =null) {
// GC hasn't removed the instance yet
} else {
// GC has cleared the instance
}
Copy the code
conclusion
- Weak references are there for garbage collection
- An object can be reclaimed if it has only weak references to it
- Weak references are collected as soon as GC occurs, regardless of whether there is sufficient memory at the time
- If you specify a reference queue when creating weak references, the weak reference object will be placed in the reference queue when it is reclaimed
- For safe use, check whether the object is null each time to determine whether the object has been reclaimed to avoid null pointer exceptions
After watching two things
If you find this article inspiring, I’d like to invite you to do me two small favors:
- Like, so that more people can see this content (collection does not like, is a rogue -_-)
- Pay attention to the public number “interview BAT”, do not regularly share the original knowledge, the original is not easy, please support more (it also provides brush small program oh).
Jdk8 official documents resolve weak references