一、finalize
相当于析构函数,主要用于销毁某些资源。
与GC的关系:在gc发现对象mutator不可达,并且存在非平凡的finalize方法时,会将改方法放入finalize队列,并重新以该队列作为trace root来标记堆中的对象。
实现上,对象拥有一个finalize标志位,当gc发现mutator不可达且拥有该标志位时,会将该对象放入ReferenceQueue,(实际上是挂到Reference的discover上,由守护线程轮询入队),同时去除该标记位(让改对象不能再次入队,保证一个对象的finalize方法只能被执行一次),同时finalizer守护线程(FinalizerThread)会不停的拉取这个队列的对象,并执行其finalize方法。
具体实现参见,省略了与主流程无关的内容
final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
same package as the Reference
class */
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
private static Finalizer unfinalized = null;
private static final Object lock = new Object();
/* Invoked by VM */
static void register(Object finalizee) {
new Finalizer(finalizee);
}
private void runFinalizer(JavaLangAccess jla) {
synchronized (this) {
if (hasBeenFinalized()) return;
remove();
}
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(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();
}
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and 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();
}
}
通过源码我们可以看到
- 对于finalize的异常,是会被忽略的
- FinalizerThread看上去是单线程的,这会影响并发执行的效率
- finalize是无序的!!!即使存在依赖关系
- 通过finalize暴露的this,会导致这个对象重新复活,但是这跟finalize的实现理念是相悖的,因为finalize用来清理关联的资源,在finalize清理完资源后,重新复活这个对象意味着,这个对象可能在一个不满足条件的环境下运行(资源被清理了)
finalize拥有以上的不足,于是引入了以下概念
二、各类型引用
- weak:弱引用
- soft:软引用,内存敏感
- phantom:幽灵引用(翻译成虚引用感觉不太贴切,幽灵表示飘渺游荡的指针),可以理解为一个对应引用的占位符。
与GC的关系,CMS的yong gc为例(其他GC同理)
- 根扫描,扫描并复制所有的强引用,并标记出所有的weak、soft、phantom引用(到这些引用就返回,不继续trace)
- 若复制过程中出现内存不够,置空所有的soft(注意是所有)并入队。如果内存足够,以soft为根进行trace跟复制。事实上,soft的处理可能比这个复杂,vm会选择入队并置空的时机!!!但一般来说,只有对象被回收了,才会入队,可能不仅在内存不够时还可能出现在长时间未get的引用
- 如果weak引用的对象没有被复制(需要被回收),则将weak置空,并加入Reference queue(加入的方式等同于finalize)
- 如果尚未被复制的对象包含了finalize标记,则走第一节所述内容(加入队列,并回到【1】)
- 如果幽灵引用的目标对象未被复制,入队并trace(保证对象的完整性,虽然get方法返回的是null)。注意,该引用需要手动清理,调用clear方法
finalize的处理实际上zao
三、一个基于spring的refreshscope实现
意图:通过查看源码发现apollo与spring Configuration的绑定是相当耗时的,时间复杂度正比于配置项的平方,并且不能自动刷新,因此简单重新实现了绑定流程,采用了自定义的scope,但是自定义scope需要自己管理bean的生命周期。这个demo不合理,但是能表现意图,主要还是Reference是调用是不保证顺序的
package com.yupaopao.platform.audit.gateway.framework;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
/**
* 避免每次都去创建,如果没刷新,直接拿
* 区别于proptotype,每次都要创建
* <p>
* 提示:自定义scope需要自己管理bean的生命周期,如destory等,但是这里不是特别好实现,因为如果这个bean被pin住,bean就不能销毁。这里交给了GC
* 但是bean的依赖关系可能会导致销毁问题,主要是reference queue的非无序性,强制把生命周期拉到了gc阶段,不合理
*
* @author hanweiwei
*/
@Slf4j
public class RefreshScope extends Thread implements Scope {
public static final String scopeName = "apolloRefresh";
private ConcurrentHashMap<String, BeanLifecycleWrapper> wrapperCache = new ConcurrentHashMap<>();
private ConcurrentHashMap<WeakReference<Object>, WeakReference<Runnable>> referenceCache = new ConcurrentHashMap<>();
private ThreadLocal<BeanLifecycleWrapper> beanLifecycleLocal = new ThreadLocal<>();
private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
beanLifecycleLocal.remove();
try {
BeanLifecycleWrapper beanLifecycleWrapper = wrapperCache.computeIfAbsent(name, key -> new BeanLifecycleWrapper(key, objectFactory));
return beanLifecycleWrapper.getBean();
} finally {
beanLifecycleLocal.remove();
}
}
@Override
public Object remove(String name) {
BeanLifecycleWrapper wrapper = wrapperCache.remove(name);
if (wrapper != null && wrapper.callback != null) {
referenceCache.put(new WeakReference<>(wrapper.bean, referenceQueue), new WeakReference<>(wrapper.callback));
}
//如果这直接返回bean,会导致这个bean还在使用,却被提前销毁
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
beanLifecycleLocal.get().setDestroyCallback(callback);
}
/**
* 不支持,可以用表达式替换
*
* @param key
* @return
*/
@Override
public Object resolveContextualObject(String key) {
return wrapperCache.get(key);
}
/**
* 无绑定id,只与当前环境有关
*
* @return
*/
@Override
public String getConversationId() {
return null;
}
/**
* 占位
*/
private class BeanLifecycleWrapper {
private final String name;
private final ObjectFactory<?> objectFactory;
private Object bean;
private Runnable callback;
BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory) {
this.name = name;
this.objectFactory = objectFactory;
}
public String getName() {
return this.name;
}
public void setDestroyCallback(Runnable callback) {
this.callback = callback;
}
public Object getBean() {
if (this.bean == null) {
synchronized (this) {
if (this.bean == null) {
this.bean = this.objectFactory.getObject();
if (this.bean != null) {
beanLifecycleLocal.set(this);
}
}
}
}
return this.bean;
}
public void destroy() {
if (this.callback == null) {
return;
}
Runnable callback = this.callback;
callback.run();
this.callback = null;
this.bean = null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj;
if (this.name == null) {
return other.name == null;
} else return this.name.equals(other.name);
}
}
@Override
public void run() {
for (; ; ) {
try {
Reference<?> r = referenceQueue.remove();
if (r != null) {
WeakReference<Runnable> runnableWeakReference = referenceCache.remove(r);
Runnable runnable = runnableWeakReference.get();
if (runnable != null) {
runnable.run();
}
r.clear();
}
} catch (Exception e) {
log.warn("", e);
}
}
}
}