Java之WeakReference学习

我是在什么情况下遇到了这部分知识?

在我看Glide源码学习它的缓存机制的时候,遇到类ActiveResources,这个类里面用一个Map<Key, ResourceWeakReference>的方式保存下缓存数据,然后这个ResourceWeakReference是继承自WeakReference,然后在代码里面还开了一个线程,里面有一个死循环一直在监听resourceReferenceQueue这个队列的数据。然后就很纳闷,然后去百度了下。发现这是弱引用的知识,当发生gc的时候,弱引用对象会被保存到resourceReferenceQueue这个队列里面。这个队列是线程阻塞的,只有当有数据的时候才会继续执行。这也是一个知识点。我们就可以利用这一点用来监听发生gc的时候移除的引用对象数据。完美

当一个对象仅仅被weak referencr指向的时候,而没有任何其他强引用指向的时候。如果这个时候触发了GC,那么这个对象就会被回收,不论当前的内存空间是否够用,这个对象都会被回收。

认识WeakReference

   WeakReference继承Reference

1
2
3
4
5
6
7
8
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}

   这里有两个构造函数,我来分别讲下构造函数里面的参数信息:

  • referent:通过对他的翻译我们知道他是:所指对象的意思。就是我们弱引用的对象。(需要区分弱引用对象和被弱引用的对应),弱引用对象指的是WeakReference的实例或者其子类的实例。
  • q:ReferenceQueue:从名字也可以看出这是一个队列。他是在发生GC的时候,弱引用对象会被保存到这个队列里面。多以我们就可以通过这个监听这个队列的数据变化,来获取被gc的弱引用对象值。

案例

使用WeakReference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.brightloong.lab.reference;
public class Apple {

private String name;

public Apple(String name) {
this.name = name;
}
/**
* 覆盖finalize,在回收的时候会执行。
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Apple: " + name + " finalize。");
}

@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
'}' + ", hashCode:" + this.hashCode();
}
}

继承WeakReference的Salad:

1
2
3
4
5
6
7
package io.github.brightloong.lab.reference;
import java.lang.ref.WeakReference;
public class Salad extends WeakReference<Apple> {
public Salad(Apple apple) {
super(apple);
}
}

Client调用和输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package io.github.brightloong.lab.reference;
import java.lang.ref.WeakReference;
public class Client {
public static void main(String[] args) {
Salad salad = new Salad(new Apple("红富士"));
//通过WeakReference的get()方法获取Apple
System.out.println("Apple:" + salad.get());
System.gc();
try {
//休眠一下,在运行的时候加上虚拟机参数-XX:+PrintGCDetails,输出gc信息,确定gc发生了。
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//如果为空,代表被回收了
if (salad.get() == null) {
System.out.println("clear Apple。");
}
}
}
/**
Apple:Apple{name='红富士'}, hashCode:1846274136
[GC (System.gc()) [PSYoungGen: 3328K->496K(38400K)] 3328K->504K(125952K), 0.0035102 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 496K->0K(38400K)] [ParOldGen: 8K->359K(87552K)] 504K->359K(125952K), [Metaspace: 2877K->2877K(1056768K)], 0.0067965 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Apple: 红富士 finalize。
clear Apple。
*/

ReferenceQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package io.github.brightloong.lab.reference;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Client2 {
public static void main(String[] args) {
ReferenceQueue<Apple> appleReferenceQueue = new ReferenceQueue<>();
WeakReference<Apple> appleWeakReference = new WeakReference<Apple>(new Apple("青苹果"), appleReferenceQueue);
WeakReference<Apple> appleWeakReference2 = new WeakReference<Apple>(new Apple("毒苹果"), appleReferenceQueue);

System.out.println("=====gc调用前=====");
Reference<? extends Apple> reference = null;
while ((reference = appleReferenceQueue.poll()) != null ) {
//不会输出,因为没有回收被弱引用的对象,并不会加入队列中
System.out.println(reference);
}
System.out.println(appleWeakReference);
System.out.println(appleWeakReference2);
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());

System.out.println("=====调用gc=====");
System.gc();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("=====gc调用后=====");

//下面两个输出为null,表示对象被回收了
System.out.println(appleWeakReference.get());
System.out.println(appleWeakReference2.get());

//输出结果,并且就是上面的appleWeakReference、appleWeakReference2,再次证明对象被回收了
Reference<? extends Apple> reference2 = null;
while ((reference2 = appleReferenceQueue.poll()) != null ) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("appleReferenceQueue中:" + reference2);
}
}
}
/**
=====gc调用前=====
java.lang.ref.WeakReference@6e0be858
java.lang.ref.WeakReference@61bbe9ba
Apple{name='青苹果'}, hashCode:1627674070
Apple{name='毒苹果'}, hashCode:1360875712
=====调用gc=====
Apple: 毒苹果 finalize。
Apple: 青苹果 finalize。
=====gc调用后=====
null
null
appleReferenceQueue中:java.lang.ref.WeakReference@6e0be858
appleReferenceQueue中:java.lang.ref.WeakReference@61bbe9ba

Process finished with exit code 0
**/

在Android里面的使用案例

可以看Glide的源码里面的ActiveResources类。