从上一篇文章中在读取jar的资源文件时,对于资源的清理工作时利用JDK提供的Cleaner工具,当Jar文件资源没有引用后需要关闭native资源。\
ZipFile的字段和构造函数
ZipFile的重要字段,其中一个重要字段就是CleanableResource,它表示Zip文件使用资源在使用完之后需要被清除的以下三类资源.
- 需要关闭的inputStream.
- 缓存的解压的对象。
- zip文件native资源。
public class ZipFile implements ZipConstants, Closeable {
private final String name; // zip file name
private volatile boolean closeRequested;
// The "resource" used by this zip file that needs to be
// cleaned after use.
// a) the input streams that need to be closed
// b) the list of cached Inflater objects
// c) the "native" source of this zip file.
private final @Stable CleanableResource res;
}
ZipFile的构造函数中主要是对res变量是创建CleanableResource对象
public ZipFile(File file, int mode, Charset charset) throws IOException
{
String name = file.getPath();
file = new File(name);
this.name = name;
this.res = new CleanableResource(this, ZipCoder.get(charset), file, mode);
}
CleanableResource构造函数
CleanableResource的构造函数如下: 主要创建四个个变量
- 创建Cleaner并调用register方法将zipFile对象和this注册到Cleaner中.
- 创建一个空的istreams的WeakHashSet集合赋值给istreams便令
- 创建双端数组ArrayDeque赋值给inflaterCache变量.
- 创建Source对象
CleanableResource(ZipFile zf, ZipCoder zc, File file, int mode) throws IOException {
this.cleanable = CleanerFactory.cleaner().register(zf, this);
this.istreams = Collections.newSetFromMap(new WeakHashMap<>());
this.inflaterCache = new ArrayDeque<>();
this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0, zc);
}
Cleaner的create函数
- 创建一个Cleaner对象。
- 获取cleaner中impl字段并调用其start方法.
public static Cleaner create(ThreadFactory threadFactory) {
Objects.requireNonNull(threadFactory, "threadFactory");
Cleaner cleaner = new Cleaner();
cleaner.impl.start(cleaner, threadFactory);
return cleaner;
}
Cleaner构造函数中初始化时创建一个CleanerImpl对象,
private Cleaner() {
impl = new CleanerImpl();
}
CleanerImpl的构造函数
主要创建ReferenceQueue,PhantomCleanableRef两个类型的变量.
public CleanerImpl() {
queue = new ReferenceQueue<>();
phantomCleanableList = new PhantomCleanableRef();
}
CleanerImpl的start函数
- 为Cleaner对象创建虚引用.
- 利用线程工厂创建Thread对象.并设置为Daemon为true.
- 调用线程的启动start启动线程.
public void start(Cleaner cleaner, ThreadFactory threadFactory) {
new CleanerCleanable(cleaner);
if (threadFactory == null) {
threadFactory = CleanerImpl.InnocuousThreadFactory.factory();
}
Thread thread = threadFactory.newThread(this);
thread.setDaemon(true);
thread.start();
}
CleanerImpl的run方法
这个Runnable就上CleanerImpl的start启动的后台线程的执行的run方法体。
- 获取当前运行的线程.判断是否是InnocuousThread类型,如果是则返回,否则返回null。
- 开始while循环,条件是phantomCleanableList不为空,PhantomCleanable是需要清理的动作的虚引用对象。
2.1 如果Thead对象不为空,则先清除TheadLocals变量的值。
2.2 从ReferenceQueue队列中删除一个元素,超时时间是60秒,如果取Cleanable对 象,并执行其clean方法,(这里是由GC回收虚引用对象PhantomCleanableRef后会将会后引用对象放入ReferenceQueue)
CleanerImpl implements Runable {
@Override
public void run() {
Thread t = Thread.currentThread();
InnocuousThread mlThread = (t instanceof InnocuousThread)
? (InnocuousThread) t
: null;
while (!phantomCleanableList.isListEmpty()) {
if (mlThread != null) {
// Clear the thread locals
mlThread.eraseThreadLocals();
}
try {
// Wait for a Ref, with a timeout to avoid getting hung
// due to a race with clear/clean
Cleanable ref = (Cleanable) queue.remove(60 * 1000L);
if (ref != null) {
ref.clean();
}
} catch (Throwable e) {
// ignore exceptions from the cleanup action
// (including interruption of cleanup thread)
}
}
Cleaner的register函数
Cleaner的注册主要创建一个幻引用PhantomCleanableRef,传入之前的Zipfile对象,Cleaner对象自己以及Runnable的对象(实际就是上面分下的CleanableResource对象),
public Cleanable register(Object obj, Runnable action) {
return new CleanerImpl.PhantomCleanableRef(obj, this, action);
}
PhantomCleanableRef的字段和构造函数
1.初始化父类传入对象引用和Cleaner。 2.赋值action为传入的Runnable对象(这里实际就是CleanableResource对象)
public static final class PhantomCleanableRef extends PhantomCleanable<Object> {
private final Runnable action;
public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
super(obj, cleaner);
this.action = action;
}
PhantomCleanable的构造方法
- 初始化父类传入虚引用对象以及referenceQueue队列.
- CleanerImpl中phantomCleanableList变量赋值给list变量。
- 将自己insert到phantomCleanableList双向链表中头部.
public PhantomCleanable(T referent, Cleaner cleaner) {
super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue);
this.list = CleanerImpl.getCleanerImpl(cleaner).phantomCleanableList;
insert();
}
PhantomCleanableRef的clean方法
- 首先从phantomCleanableList双向链表中删除自己
- 执行父类的clear方法.
- 执行performCleanup方法。
public final void clean() {
if (remove()) {
super.clear();
performCleanup();
}
}
PhantomCleanableRef的performCleanup方法
实际就是执行构造方法中传入Runnable对象的run方法(实际传入是CleanableResource对象)
@Override
protected void performCleanup() {
action.run();
}
CleanableResource的run方法
- 释放解压的缓存资源.
- 关闭流对象。
- 释放JVM的native的source资源.
private static class CleanableResource implements Runnable
//省略
public void run() {
IOException ioe = null;
// Release cached inflaters and close the cache first
Deque<Inflater> inflaters = this.inflaterCache;
if (inflaters != null) {
synchronized (inflaters) {
// no need to double-check as only one thread gets a
// chance to execute run() (Cleaner guarantee)...
Inflater inf;
while ((inf = inflaters.poll()) != null) {
inf.end();
}
// close inflaters cache
this.inflaterCache = null;
}
}
// Close streams, release their inflaters
if (istreams != null) {
synchronized (istreams) {
if (!istreams.isEmpty()) {
InputStream[] copy = istreams.toArray(new InputStream[0]);
istreams.clear();
for (InputStream is : copy) {
try {
is.close();
} catch (IOException e) {
if (ioe == null) ioe = e;
else ioe.addSuppressed(e);
}
}
}
}
}
// Release zip src
if (zsrc != null) {
synchronized (zsrc) {
try {
Source.release(zsrc);
zsrc = null;
} catch (IOException e) {
if (ioe == null) ioe = e;
else ioe.addSuppressed(e);
}
}
}
if (ioe != null) {
throw new UncheckedIOException(ioe);
}
}
}
总结
本文主要对Jar的文件资源的回收的分析,其主要利用利用创建虚引用并传入ReferenceQueue,然后ClearnerImpl开启一个后台线程,循环从ReferenceQueue取出虚引用对象,并执行其clean方法,然后回调传入Runnable的run方法,进行相应的资源清理工作。