java中使用Reference对象来描述所有的引用对象
referent表示被引用的对象。一个Reference可能有4种状态:Active、Pending、Enqueued、Inactive
在构造Reference时可以决定是否指定ReferenceQueue
,会有不同的状态变更,另外一旦状态变成Inactive,状态就不会再做任何变更
当GC发生时,被回收的对象会添加到Pending列表中,通过Reference的next字段来构建Pending链表。在Reference的静态代码块,则会启动RefenrenceHandler
static {
...
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
...
}
并设置为最大的优先级,它负责将pending中的元素添加到ReferenceQueue,
private static class ReferenceHandler extends Thread {
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null : rn;
r.next = r;
} else {
try {
lock.wait();
} catch (InterruptedException x) { }
continue;
}
}
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
//存在ReferenceQueue时,将pending中的元素入队列
ReferenceQueue q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
}
}
}
ReferenceQueue提供对列的功能,出队和入队,当ReferenceQueue作为参数被提供时,这意味着用户一旦从ReferenceQueue中获取到元素,也就可以知道,这个对象要被回收了
,以此达到一种通知的效果
new
生成的对象,这类可确保不会被GC回收掉软引用不仅考虑内存,还会考虑referent的使用情况和创建时间来决定是否该回收。Hotspot会读取当前堆剩余的内存,以及配置参数XX:SoftRefLRUPolicyMSPerMB
(每M数据应该存活的毫秒数)
void LRUMaxHeapPolicy::setup() {
size_t max_heap = MaxHeapSize;
max_heap -= Universe::get_heap_used_at_last_gc();
max_heap /= M;
//剩余空间能够存的以M为单位的数据应该存活的时间
_max_interval = max_heap * SoftRefLRUPolicyMSPerMB;
assert(_max_interval >= 0,"Sanity check");
}
// The oop passed in is the SoftReference object, and not
// the object the SoftReference points to.
bool LRUCurrentHeapPolicy::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) {
return false;
}
return true;
}
软引用自身携带timestamp和clock,其中clock由GC更新,timestamp每次get的时候,如果和clock不一致则更新
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
如果再上一次GC之后,有过访问记录,那么当前的GC肯定不会回收软引用,这也就意味着,软引用如果一直没有回收,升级到老年代,在OOM之前,有可能出现频繁的Full GC
weakHashMap在 get/put/remove/resize等方法中均使用了expungeStaleEntries
,去掉多余的信息
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
...
private void expungeStaleEntries() {
//从注册的队列中拿到了值
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
Entry<K,V> e = (Entry<K,V>) x;
...
if (p == e) {
e.value = null; // Help GC
size--;
...
}
}
}
}
如果从注册的队列中拿到了对应的元素,那么就自动删掉,这里就是利用了ReferenceQueue承担通知的角色,以及弱引用的GC就回收性质
在ReferenceHandler中注意到,如果pending中的Reference是一个Cleaner,则直接执行clean
public void clean() {
if (!remove(this))
return;
...
//之前没有执行过要clean的,现在执行
thunk.run();
...
}
}
以DirectByteBuffer为例,在使用时,就会创建一个Cleaner
DirectByteBuffer(int cap) {
...
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
private static class Deallocator
implements Runnable
{
public void run() {
if (address == 0) {
// Paranoia
return;
}
unsafe.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
}
java中通过ByteBuffer.allocateDirect
就可以创建,如果DirectByteBuffer被回收,此时唯一引用DirectByteBuffer的是一个虚引用,由于垃圾回收的作用,DirectByteBuffer会处于pending状态,触发Native内存的回收释放
参考直接内存
延伸一点网络读写过程非直接内存转换成直接内存的行为,javaNio中写数据IOUtil.write
实现中可以看到
static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
NativeDispatcher nd){
...
if (!(buf instanceof DirectBuffer)) {
//分配直接内存
ByteBuffer shadow = Util.getTemporaryDirectBuffer(rem);
shadow.put(buf);
shadow.flip();
vec.setShadow(iov_len, shadow);
buf.position(pos); // temporarily restore position in user buffer
buf = shadow;
pos = shadow.position();
}
...
}
会发现如果要将一个byte数组对象传给native,会先转换成直接内存再操作,这是因为native代码访问数组必须保证访问的时候,byte[]对象不能移动,也就是被"pin"钉住,此时要么是暂停GC(GC算法有可能要移动对象),要么是假设换成native的消耗是可接受的,而且I/O操作都很慢,这里就选择了后者
Finalizer自身会启动一个线程,它自己的工作就是一直从ReferenceQueue中拉取对应的元素并执行它的runFinalizer方法
private static class FinalizerThread extends Thread {
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer();
} catch (InterruptedException x) {
continue;
}
}
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
值得注意的是,Finalizer它本身的构造函数是private,只能通过虚拟机自身来执行register操作,具体的时机根据RegisterFinalizersAtInit
参数来决定,如果值为true,那么在构造函数返回之前调用注册
//vmSymbols.hpp
...
template(object_initializer_name, "<init>")
...
do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \
//c1_GraphBuilder.cpp
void GraphBuilder::method_return(Value x) {
//RegisterFinalizersAtInit为true
if (RegisterFinalizersAtInit &&
method()->intrinsic_id() == vmIntrinsics::_Object_init) {
call_register_finalizer();
}
instanceOop instanceKlass::allocate_instance(TRAPS) {
assert(!oop_is_instanceMirror(), "wrong allocation path");
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
int size = size_helper(); // Query before forming handle.
KlassHandle h_k(THREAD, as_klassOop());
instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
if (has_finalizer_flag && !RegisterFinalizersAtInit) {
//RegisterFinalizersAtInit为false
i = register_finalizer(i, CHECK_NULL);
}
return i;
}
注册执行如下
instanceOop instanceKlass::register_finalizer(instanceOop i, TRAPS) {
if (TraceFinalizerRegistration) {
tty->print("Registered ");
i->print_value_on(tty);
tty->print_cr(" (" INTPTR_FORMAT ") as finalizable", (address)i);
}
instanceHandle h_i(THREAD, i);
// Pass the handle as argument, JavaCalls::call expects oop as jobjects
JavaValue result(T_VOID);
JavaCallArguments args(h_i);
//finalizer_register_method即通过Finalizer找到的register
methodHandle mh (THREAD, Universe::finalizer_register_method());
JavaCalls::call(&result, mh, &args, CHECK_NULL);
return h_i();
}
runFinalizer执行如下
private void runFinalizer() {
synchronized (this) {
if (hasBeenFinalized()) return;
remove();
}
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
invokeFinalizeMethod(finalizee);
/* Clear stack slot containing this variable, to decrease
the chances of false retention with a conservative GC */
finalizee = null;
}
} catch (Throwable x) { }
super.clear();
}
则是先看是不是已经执行过,执行过就返回,这里可以得到如下三点信息
finalize()方法只会执行一次
。unfinalized
对象构建的强引用,第一次GC执行,只是在等待runFinalizer的执行,如果执行了,并且之前没有执行过才会从 unfinalized
列表中进行删掉,从而不可达,再第二次GC的时候回收了Finalizer本身执行finalize()方法具体细节如下
JNIEXPORT void JNICALL
Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz,
jobject ob)
{
jclass cls;
jmethodID mid;
cls = (*env)->GetObjectClass(env, ob);
if (cls == NULL) return;
mid = (*env)->GetMethodID(env, cls, "finalize", "()V");
//没有finalize什么都不做
if (mid == NULL) return;
//执行
(*env)->CallVoidMethod(env, ob, mid);
}
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有