/** * Removes all of the elements from this list. The list will * be empty after this call returns. */ public void clear() { modCount++;
// clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null;//显式将引用置为null
size = 0; }
其他三种类型的引用
软引用(soft Reference)
弱引用(waek reference)
虚引用(phantom reference)
Reference 抽象类是这些引用的父类
具体参见Reference类的JavaDoc文档
ReferenceQueue
Reference
java.lang.ref public abstract class Reference extends Object Abstract base class for reference objects. This class defines the operations common to all reference objects. Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly. Reference是引用对象的父类,这个类定义了一些适用于所有引用对象的操作,因为引用对象他的实现和垃圾收集器是紧密相关的,所以这个类是不可以被子类化的。 他的构造器:
/* A Reference instance is in one of four possible internal states: * * Active: Subject to special treatment by the garbage collector. Some * time after the collector detects that the reachability of the * referent has changed to the appropriate state, it changes the * instance's state to either Pending or Inactive, depending upon * whether or not the instance was registered with a queue when it was * created. In the former case it also adds the instance to the * pending-Reference list. Newly-created instances are Active. * * Pending: An element of the pending-Reference list, waiting to be * enqueued by the Reference-handler thread. Unregistered instances * are never in this state. * * Enqueued: An element of the queue with which the instance was * registered when it was created. When an instance is removed from * its ReferenceQueue, it is made Inactive. Unregistered instances are * never in this state. * * Inactive: Nothing more to do. Once an instance becomes Inactive its * state will never change again. * * The state is encoded in the queue and next fields as follows: * * Active: queue = ReferenceQueue with which instance is registered, or * ReferenceQueue.NULL if it was not registered with a queue; next = * null. * * Pending: queue = ReferenceQueue with which instance is registered; * next = this * * Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance * in queue, or this if at end of list. * * Inactive: queue = ReferenceQueue.NULL; next = this. * * With this scheme the collector need only examine the next field in order * to determine whether a Reference instance requires special treatment: If * the next field is null then the instance is active; if it is non-null, * then the collector should treat the instance normally. * * To ensure that a concurrent collector can discover active Reference * objects without interfering with application threads that may apply * the enqueue() method to those objects, collectors should link * discovered objects through the discovered field. The discovered * field is also used for linking Reference objects in the pending list. */
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
/* When active: NULL 还没有入队 * pending: this 表示也还没有入队,只是指向自己 * Enqueued: next reference in queue (or this if last) 入队,next指向是队列的下一个元素 * Inactive: this 他自己 */ @SuppressWarnings("rawtypes") Reference next;
/* When active: next element in a discovered reference list maintained by GC (or this if last) * pending: next element in the pending list (or null if last) * otherwise: NULL */ transient private Reference<T> discovered; /* used by VM */
/* Object used to synchronize with the garbage collector. The collector * must acquire this lock at the beginning of each collection cycle. It is * therefore critical that any code holding this lock complete as quickly * as possible, allocate no new objects, and avoid calling user code. */ static private class Lock { } private static Lock lock = new Lock();
/* List of References waiting to be enqueued. The collector adds * References to this list, while the Reference-handler thread removes * them. This list is protected by the above lock object. The * list uses the discovered field to link its elements. */ private static Reference<Object> pending = null;
/* High-priority thread to enqueue pending References */ private static class ReferenceHandler extends Thread {
static { // pre-load and initialize InterruptedException and Cleaner classes // so that we don't get into trouble later in the run loop if there's // memory shortage while loading/initializing them lazily. ensureClassInitialized(InterruptedException.class); ensureClassInitialized(Cleaner.class); }
public void run() { while (true) { tryHandlePending(true); } } }
/** * Try handle pending {@link Reference} if there is one.<p> * Return {@code true} as a hint that there might be another * {@link Reference} pending or {@code false} when there are no more pending * {@link Reference}s at the moment and the program can do some other * useful work instead of looping. * * @param waitForNotify if {@code true} and there was no pending * {@link Reference}, wait until notified from VM * or interrupted; if {@code false}, return immediately * when there is no pending {@link Reference}. * @return {@code true} if there was a {@link Reference} pending and it * was processed, or we waited for notification and either got it * or thread was interrupted before being notified; * {@code false} otherwise. */ static boolean tryHandlePending(boolean waitForNotify) { Reference<Object> r; Cleaner c; try { synchronized (lock) { if (pending != null) { r = pending; // 'instanceof' might throw OutOfMemoryError sometimes // so do this before un-linking 'r' from the 'pending' chain... c = r instanceof Cleaner ? (Cleaner) r : null; // unlink 'r' from 'pending' chain pending = r.discovered; r.discovered = null; } else { // The waiting on the lock may cause an OutOfMemoryError // because it may try to allocate exception objects. if (waitForNotify) { lock.wait(); } // retry if waited return waitForNotify; } } } catch (OutOfMemoryError x) { // Give other threads CPU time so they hopefully drop some live references // and GC reclaims some space. // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above // persistently throws OOME for some time... Thread.yield(); // retry return true; } catch (InterruptedException x) { // retry return true; }
// Fast path for cleaners if (c != null) { c.clean(); return true; }
ReferenceQueue<? super Object> q = r.queue; if (q != ReferenceQueue.NULL) q.enqueue(r); return true; }
static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); 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();
// provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override public boolean tryHandlePendingReference() { return tryHandlePending(false); } }); }
/* -- Referent accessor and setters -- */
/** * Returns this reference object's referent. If this reference object has * been cleared, either by the program or by the garbage collector, then * this method returns <code>null</code>. * * @return The object to which this reference refers, or * <code>null</code> if this reference object has been cleared */ public T get() { return this.referent; }
/** * Clears this reference object. Invoking this method will not cause this * object to be enqueued. * * <p> This method is invoked only by Java code; when the garbage collector * clears references it does so directly, without invoking this method. */ 调用这个方法并不会将对象入队。 当垃圾收集器清理的时候它会直接清理,而不会调用这个方法。 public void clear() { this.referent = null; }
/* -- Queue operations -- */
/** * Tells whether or not this reference object has been enqueued, either by * the program or by the garbage collector. If this reference object was * not registered with a queue when it was created, then this method will * always return <code>false</code>. * * @return <code>true</code> if and only if this reference object has * been enqueued */ public boolean isEnqueued() { return (this.queue == ReferenceQueue.ENQUEUED); }
/** * Adds this reference object to the queue with which it is registered, * if any. * * <p> This method is invoked only by Java code; when the garbage collector * enqueues references it does so directly, without invoking this method. * * @return <code>true</code> if this reference object was successfully * enqueued; <code>false</code> if it was already enqueued or if * it was not registered with a queue when it was created */ public boolean enqueue() { return this.queue.enqueue(this); }
ReferenceQueue java doc: Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected. 引用队列,当垃圾收集器检测到引用对象的可达性发生变化后,会把引用对象放到引用队列当中。
public class MyTest1 { public static void main(String[] args) { Date date= new Date(); SoftReference<Date> softReference = new SoftReference(date); Date date1 = softReference.get(); //这里一定要判空,因为不一定什么时刻内存不够了,软引用就会被清理掉。 if(null != date1){ System.out.println("hello"); }else{ System.out.println("world"); } System.out.println("========="); softReference.clear(); Date date2 = softReference.get(); System.out.println(date2); } }
运行结果: hello ========= null
那么接下来看一下SoftReference的文档: Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches. Suppose that the garbage collector determines at a certain point in time that an object is softly reachable. At that time it may choose to clear atomically all soft references to that object and all soft references to any other softly-reachable objects from which that object is reachable through a chain of strong references. At the same time or at some later time it will enqueue those newly-cleared soft references that are registered with reference queues. All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError. Otherwise no constraints are placed upon the time at which a soft reference will be cleared or the order in which a set of such references to different objects will be cleared. Virtual machine implementations are, however, encouraged to bias against clearing recently-created or recently-used soft references. Direct instances of this class may be used to implement simple caches; this class or derived subclasses may also be used in larger data structures to implement more sophisticated caches. As long as the referent of a soft reference is strongly reachable, that is, is actually in use, the soft reference will not be cleared. Thus a sophisticated cache can, for example, prevent its most recently used entries from being discarded by keeping strong referents to those entries, leaving the remaining entries to be discarded at the discretion of the garbage collector. 软引用对象 当垃圾收集器去响应一些内存需求的时候软引用会被清理掉,软引用经常用于实现内存敏感的缓存。
public class MyTests2 { public static void main(String[] args) { Date date= new Date(); ReferenceQueue<Date> referenceQueue = new ReferenceQueue<>(); SoftReference<Date> softReference = new SoftReference(date,referenceQueue);
public class MyTests2 { public static void main(String[] args) { Date date= new Date(); ReferenceQueue<Date> referenceQueue = new ReferenceQueue<>(); SoftReference<Date> softReference = new SoftReference(date,referenceQueue); date = null; System.gc(); System.out.println(softReference.get());
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings. Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.
public class MyTests3 { public static void main(String[] args) { Date date = new Date(); WeakReference<Date> weakReference = new WeakReference<>(date); System.out.println(weakReference.get()); } }
public class MyTests3 { public static void main(String[] args) throws InterruptedException { Date date = new Date(); WeakReference<Date> weakReference = new WeakReference<>(date); System.gc(); Thread.sleep(5000); System.out.println(weakReference.get()); } }
输出: Tue Mar 10 21:28:16 CST 2020
为什么没有被回收,原因其实是Date date = new Date();这个地方存在一个强引用,如果我们修改下:
1 2 3 4 5 6 7 8 9 10
public class MyTests3 { public static void main(String[] args) throws InterruptedException { Date date = new Date(); WeakReference<Date> weakReference = new WeakReference<>(date); date = null; System.gc(); Thread.sleep(5000); System.out.println(weakReference.get()); } }
输出: null
PhantomReference
Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used for scheduling pre-mortem(在真正的清除之前) cleanup actions in a more flexible way than is possible with the Java finalization mechanism. If the garbage collector determines at a certain point in time that the referent of a phantom reference is phantom reachable, then at that time or at some later time it will enqueue the reference. In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The get method of a phantom reference always returns null. Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable. 虚引用引用的对象,当垃圾收集器确定它们的引用可以被回收的情况下,会被入队,虚引用经常用于调度这种清除的动作,这个要比java的finalization的方式更加灵活,即在对象在被真正清理之前执行一些特殊的逻辑。 在垃圾收集器再确定的时间点可以确定虚引用包装对象里边的referent是虚引用可达的,在这个时刻或者之后的某个时刻,就会将这个引用执行入队操作。 为了确保一个可回收的对象遵守这样的规则,phantom reference 里边的referent就应该不应该被获取到,因此phantom reference 的get方法总是返回null。 试想,如果get方法能够被获取到,那么在外部我们可以使用强引用关联上这个referent,那么对象就不可能被回收掉,对象无法回收掉,就无法实现上面描述的规则,无法得到通知。 和弱引用软引用不用的是,对象入队的时候,虚引用的对象并不会被垃圾收集器自动清理掉,一个对象如果是虚引用可达的,它会依然保留,直到所有这样的引用都被清理掉或者它们自身都不可达的,才会被清理掉,虚引用的主要作用是虚引用的对象在被垃圾回收的时候,我们会收到一个通知。
1 2 3 4 5 6 7 8 9 10 11 12
public class PhantomReference<T> extends Reference<T> {
//get方法直接返回null public T get() { return null; } //构造方法让使用者必须传递一个引用队列。 public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }
}
实例:
1 2 3 4 5 6 7
public class MyTests4 { public static void main(String[] args) { Date date = new Date(); PhantomReference<Date> phantomReference = new PhantomReference(date, new ReferenceQueue<>()); System.out.println(phantomReference.get()); } }
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls. import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ 获取到ThreadLocal关联的map对象 ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
得到Thread的threadLocals,进入到Thread类:
1 2 3 4
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ 属于当前线程的ThreadLocal的值,这个map被ThreadLocal维护 ThreadLocal.ThreadLocalMap threadLocals = null;
public class MyTest5 { public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal<>(); threadLocal.set("hello"); threadLocal.set("world"); System.out.println(threadLocal.get()); } }