-
Notifications
You must be signed in to change notification settings - Fork 668
/
Cleaner.java
276 lines (264 loc) · 12.6 KB
/
Cleaner.java
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.ref;
import jdk.internal.ref.CleanerImpl;
import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.function.Function;
/**
* {@code Cleaner} manages a set of object references and corresponding cleaning actions.
* <p>
* Cleaning actions are {@link #register(Object object, Runnable action) registered}
* to run after the cleaner is notified that the object has become
* phantom reachable.
* The cleaner uses {@link PhantomReference} and {@link ReferenceQueue} to be
* notified when the <a href="package-summary.html#reachability">reachability</a>
* changes.
* <p>
* Each cleaner operates independently, managing the pending cleaning actions
* and handling threading and termination when the cleaner is no longer in use.
* Registering an object reference and corresponding cleaning action returns
* a {@link Cleanable Cleanable}. The most efficient use is to explicitly invoke
* the {@link Cleanable#clean clean} method when the object is closed or
* no longer needed.
* The cleaning action is a {@link Runnable} to be invoked at most once when
* the object has become phantom reachable unless it has already been explicitly cleaned.
* Note that the cleaning action must not refer to the object being registered.
* If so, the object will not become phantom reachable and the cleaning action
* will not be invoked automatically.
* <p>
* The execution of the cleaning action is performed
* by a thread associated with the cleaner.
* All exceptions thrown by the cleaning action are ignored.
* The cleaner and other cleaning actions are not affected by
* exceptions in a cleaning action.
* The thread runs until all registered cleaning actions have
* completed and the cleaner itself is reclaimed by the garbage collector.
* <p>
* The behavior of cleaners during {@link System#exit(int) System.exit}
* is implementation specific. No guarantees are made relating
* to whether cleaning actions are invoked or not.
* <p>
* Unless otherwise noted, passing a {@code null} argument to a constructor or
* method in this class will cause a
* {@link java.lang.NullPointerException NullPointerException} to be thrown.
*
* @apiNote The cleaning action is invoked only after the associated object becomes
* phantom reachable, so it is important that the object implementing the
* cleaning action does not hold references to the object.
* In this example, a static class encapsulates the cleaning state and action.
* An "inner" class, anonymous or not, must not be used because it implicitly
* contains a reference to the outer instance, preventing it from becoming
* phantom reachable.
* The choice of a new cleaner or sharing an existing cleaner is determined
* by the use case.
* <p>
* If the CleaningExample is used in a try-finally block then the
* {@code close} method calls the cleaning action.
* If the {@code close} method is not called, the cleaning action is called
* by the Cleaner when the CleaningExample instance has become phantom reachable.
* <pre>{@code
* public class CleaningExample implements AutoCloseable {
* // A cleaner, preferably one shared within a library
* private static final Cleaner cleaner = <cleaner>;
*
* static class State implements Runnable {
*
* State(...) {
* // initialize State needed for cleaning action
* }
*
* public void run() {
* // cleanup action accessing State, executed at most once
* }
* }
*
* private final State;
* private final Cleaner.Cleanable cleanable
*
* public CleaningExample() {
* this.state = new State(...);
* this.cleanable = cleaner.register(this, state);
* }
*
* public void close() {
* cleanable.clean();
* }
* }
* }</pre>
* The cleaning action could be a lambda but all too easily will capture
* the object reference, by referring to fields of the object being cleaned,
* preventing the object from becoming phantom reachable.
* Using a static nested class, as above, will avoid accidentally retaining the
* object reference.
* <p>
* <a id="compatible-cleaners"></a>
* Cleaning actions should be prepared to be invoked concurrently with
* other cleaning actions.
* Typically the cleaning actions should be very quick to execute
* and not block. If the cleaning action blocks, it may delay processing
* other cleaning actions registered to the same cleaner.
* All cleaning actions registered to a cleaner should be mutually compatible.
* @since 9
*/
/*
* 清理器(公开),内部默认使用虚引用完成对对象的跟踪。
*
* 注:此清理器与非公开的清理器jdk.internal.ref.Cleaner实现原理有些不一样。
*
* 下面就以虚引用清理器(PhantomCleanableRef)介绍Cleaner工作原理。
*
* ┌───█│█│█│█│█│█ ReferenceQueue
* Cleaner───────CleanerImpl─────┤
* └───▉│▉│▉│▉│▉ PhantomCleanableList
*
* 一个程序可以由多个清理器(Cleaner)。
* 每个清理器(Cleaner)可以追踪多个对象(obj),每个对象都有特定的清理操作(action)。
* 每当把一组[obj, action]注册给清理器(Cleaner),都会新建一个虚引用清理器(PhantomCleanableRef),并加入到虚引用清理器列表。
* 虚引用清理器本身是个虚引用(PhantomReference),它会跟踪此obj,并且记下其对应的action。
* 每次GC时,JVM将回收只剩虚引用的obj,并将追踪它的虚引用(这里是PhantomCleanableRef)加入到ReferenceQueue。
*
* 每创建(create)一个清理器(Cleaner),就开启一个清理服务(CleanerImpl)。
* 每个清理服务(CleanerImpl)运行在一个全新的守护线程中。
* 且每个清理服务(CleanerImpl)内部缓冲一个上述的虚引用清理器列表。
*
* 清理服务的作用就是轮询"报废引用"队列(ReferenceQueue),只要该队列中有报废的引用(PhantomCleanableRef),
* 就将其从ReferenceQueue和PhantomCleanableList中移除,并调用其相应的action。
* 如果ReferenceQueue为空,清理服务陷入阻塞。
*
* 相较于Finalizer机制,Cleaner的改进之处在于被追踪对象与清理动作是分开的,因此只需要一次gc就可以释放被追踪对象所占内容。
* 当然,如果清理动作内又持有了被追踪对象,则会造成该对象无法被回收,进而引起内存泄露。
* 另外,Cleaner内部采用虚引用追踪对象,这意味着只要GC发生,该对象就会被自动回收到报废引用队列,而不像在Finalizer机制,需要手动置空被追踪对象的引用。
*/
public final class Cleaner {
/**
* The Cleaner implementation.
*/
// Cleaner细节的实现者,提供清理服务,与Cleaner是一对一关系
final CleanerImpl impl;
static {
// 等价于:CleanerImpl.setCleanerImplAccess(cleaner -> cleaner.impl);
CleanerImpl.setCleanerImplAccess(new Function<>() {
@Override
public CleanerImpl apply(Cleaner cleaner) {
return cleaner.impl;
}
});
}
/**
* Construct a Cleaner implementation and start it.
*/
// 创建清理器,初始化清理服务
private Cleaner() {
impl = new CleanerImpl();
}
/**
* Returns a new {@code Cleaner}.
* <p>
* The cleaner creates a {@link Thread#setDaemon(boolean) daemon thread} to process the phantom reachable objects and to invoke cleaning actions.
* The {@linkplain java.lang.Thread#getContextClassLoader context class loader} of the thread is set to the {@link ClassLoader#getSystemClassLoader() system class loader}.
* The thread has no permissions, enforced only if a {@link java.lang.System#setSecurityManager(SecurityManager) SecurityManager is set}.
* <p>
* The cleaner terminates when it is phantom reachable and all of the registered cleaning actions are complete.
*
* @return a new {@code Cleaner}
*
* @throws SecurityException if the current thread is not allowed to create or start the thread.
*/
// 创建一个清理器,并开启清理服务
public static Cleaner create() {
Cleaner cleaner = new Cleaner();
// 为清理器开启清理服务(守护线程)
cleaner.impl.start(cleaner, null);
return cleaner;
}
/**
* Returns a new {@code Cleaner} using a {@code Thread} from the {@code ThreadFactory}.
* <p>
* A thread from the thread factory's {@link ThreadFactory#newThread(Runnable) newThread}
* method is set to be a {@link Thread#setDaemon(boolean) daemon thread}
* and started to process phantom reachable objects and invoke cleaning actions.
* On each call the {@link ThreadFactory#newThread(Runnable) thread factory}
* must provide a Thread that is suitable for performing the cleaning actions.
* <p>
* The cleaner terminates when it is phantom reachable and all of the
* registered cleaning actions are complete.
*
* @param threadFactory a {@code ThreadFactory} to return a new {@code Thread}
* to process cleaning actions
*
* @return a new {@code Cleaner}
*
* @throws IllegalThreadStateException if the thread from the thread
* factory was {@link Thread.State#NEW not a new thread}.
* @throws SecurityException if the current thread is not allowed to
* create or start the thread.
*/
// 创建一个清理器,附带自定义的线程工厂
public static Cleaner create(ThreadFactory threadFactory) {
Objects.requireNonNull(threadFactory, "threadFactory");
Cleaner cleaner = new Cleaner();
cleaner.impl.start(cleaner, threadFactory);
return cleaner;
}
/**
* Registers an object and a cleaning action to run when the object
* becomes phantom reachable.
* Refer to the <a href="#compatible-cleaners">API Note</a> above for
* cautions about the behavior of cleaning actions.
*
* @param obj the object to monitor
* @param action a {@code Runnable} to invoke when the object becomes phantom reachable
*
* @return a {@code Cleanable} instance
*/
/*
* 向Cleaner注册跟踪的对象obj和清理动作action
* obj默认使用虚引用包裹,在该对象被回收后,可执行action动作
*
* 返回的Cleanable对象可用于手动执行清理操作(不必要等到下一次GC)
*
* CleanerImpl.run()==>清理器clean()-->清理器performCleanup()-->action.run()
*/
public Cleanable register(Object obj, Runnable action) {
Objects.requireNonNull(obj, "obj");
Objects.requireNonNull(action, "action");
return new CleanerImpl.PhantomCleanableRef(obj, this, action);
}
/**
* {@code Cleanable} represents an object and a
* cleaning action registered in a {@code Cleaner}.
*
* @since 9
*/
// 清理器接口:可清理的
public interface Cleanable {
/**
* Unregisters the cleanable and invokes the cleaning action.
* The cleanable's cleaning action is invoked at most once regardless of the number of calls to {@code clean}.
*/
void clean();
}
}