Loading...
avatar

Reference子类源码解析

SoftReference和WeakReference

我们知道这两个类基本功能相似,区别在于当引用对象为空的时候,WeakRefreence对象会在GC回收的时候被回收,而SoftReference则需要更苛刻的条件

SoftReference实现原理

总结:GC回收时计算 SoftReference存活时间与剩余内存换算得出的时间进行大小比较(内存剩余空间*设定系数仅过换算规则计算得出),如果大于测认为需要回收,反之亦然

1
2
3
4
5
6
7
/// hotspot/src/share/vm/memory/referenceProcessor.cpp
if (rt == REF_SOFT) {
// 是否需要清除软引用
if (!_current_soft_ref_policy->should_clear_reference(obj, _soft_ref_timestamp_clock)) {
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// should_clear_reference 实现如下
bool LRUMaxHeapPolicy::should_clear_reference(oop p,
jlong timestamp_clock) {
jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p);
assert(interval >= 0, "Sanity check");

// The interval will be zero if the ref was accessed since the last scavenge/gc.
if(interval <= _max_interval) { // 与_max_interval比较
return false;
}

return true;
}
1
2
3
4
5
6
7
8
9
10
// Capture state (of-the-VM) information needed to evaluate the policy
void LRUMaxHeapPolicy::setup() {
size_t max_heap = MaxHeapSize;
max_heap -= Universe::get_heap_used_at_last_gc();
max_heap /= M;

_max_interval = max_heap * SoftRefLRUPolicyMSPerMB;
// _max_interval 计算
assert(_max_interval >= 0,"Sanity check");
}
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
// SoftReference对象记录两个时间,目的就是为了GC判断是否需要清除
public class SoftReference<T> extends Reference<T> {
// 由JVM负责更新的,记录了上一次GC发生的时间。
static private long clock;

// 每次调用 get 方法都会更新,记录了当前Reference最后一次被访问的时间。
private long timestamp;

public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}

public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}

// 和super.get的逻辑最大的不同,就在于每次调用get都会把上次发生GC的时间,也就是
// clock 更新到 timestamp 中去。
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
}

PhantomReference和Cleaner

  • 不能访问到referent
  • Cleaner继承PhantomReference,它们本质流程都是Reference流程,有GC标记,ReferenceHandler处理
  • Cleaner的clean方法由ReferenceHandler调用,最终到thunk线程进行自定义资源回收处理,以及自己的释放
1
2
3
4
5
6
7
8
9
10
public class PhantomReference<T> extends Reference<T> {
// get方法永远是null,所以无法获得referent
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}

}
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class Cleaner extends PhantomReference<Object>{
// Reference需要Queue,但Cleaner自己管理ref,所以虚构个无用Queue
private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();

// 所有的cleaner都会被加到一个双向链表中去,确保回收前这些Cleaner都是存活的。
static private Cleaner first = null;

private Cleaner
next = null,
prev = null;

// 构造的时候把自己加到双向链表中去
private static synchronized Cleaner add(Cleaner cl) {
if (first != null) {
cl.next = first;
first.prev = cl;
}
first = cl;
return cl;
}

// clean方法会调用remove把当前的cleaner从链表中删除。
private static synchronized boolean remove(Cleaner cl) {
// If already removed, do nothing
if (cl.next == cl)
return false;

// Update list
if (first == cl) {
if (cl.next != null)
first = cl.next;
else
first = cl.prev;
}
if (cl.next != null)
cl.next.prev = cl.prev;
if (cl.prev != null)
cl.prev.next = cl.next;

// Indicate removal by pointing the cleaner to itself
cl.next = cl;
cl.prev = cl;
return true;
}

// 用户自定义的一个Runnable对象,
private final Runnable thunk;

// 私有有构造函数,保证了用户无法单独地使用new来创建Cleaner。
private Cleaner(Object referent, Runnable thunk) {
super(referent, dummyQueue);
this.thunk = thunk;
}

/**
* 所有的Cleaner都必须通过create方法进行创建。
*/
public static Cleaner create(Object ob, Runnable thunk) {
if (thunk == null)
return null;
return add(new Cleaner(ob, thunk));
}

/**
* Reference Handler线程调用,来清理资源。
*/
public void clean() {
if (!remove(this))
return;
try {
thunk.run();
} catch (final Throwable x) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (System.err != null)
new Error("Cleaner terminated abnormally", x)
.printStackTrace();
System.exit(1);
return null;
}});
}
}
}

FinalReference和Finalizer

  • 它本质流程依然与Reference类似
  • Finalizer的构造是通过标志由JVM构造(JVM构造的时候会判断finalize方法是否非空,最终会调用到register进行构造)
  • Finalizer加入的queue会被Finalizer线程进行处理
  • finalize方法只会调用一次,通过hasBeenFinalized这个tag保证
  • ⚠️ finalize可以通过获取referent复活对象,其中存在很多安全隐患
  • ⚠️ FinalizerThread为守护线程,优先级很低,很有可能抢占不到资源而导致资源无法回收
Author: Afree
Link: https://afree8909.github.io/blog/2019/03/10/Reference%E5%AD%90%E7%B1%BB%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment