180 lines
6.8 KiB
Java
180 lines
6.8 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2011 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package java.lang.ref;
|
||
|
|
||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||
|
import dalvik.annotation.optimization.FastNative;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public final class FinalizerReference<T> extends Reference<T> {
|
||
|
// This queue contains those objects eligible for finalization.
|
||
|
@UnsupportedAppUsage
|
||
|
public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
|
||
|
|
||
|
// Guards the list (not the queue).
|
||
|
private static final Object LIST_LOCK = new Object();
|
||
|
|
||
|
// This list contains a FinalizerReference for every finalizable object in the heap.
|
||
|
// Objects in this list may or may not be eligible for finalization yet.
|
||
|
@UnsupportedAppUsage
|
||
|
private static FinalizerReference<?> head = null;
|
||
|
|
||
|
// The links used to construct the list.
|
||
|
private FinalizerReference<?> prev;
|
||
|
@UnsupportedAppUsage
|
||
|
private FinalizerReference<?> next;
|
||
|
|
||
|
// When the GC wants something finalized, it moves it from the 'referent' field to
|
||
|
// the 'zombie' field instead.
|
||
|
private T zombie;
|
||
|
|
||
|
public FinalizerReference(T r, ReferenceQueue<? super T> q) {
|
||
|
super(r, q);
|
||
|
}
|
||
|
|
||
|
@Override public T get() {
|
||
|
return zombie;
|
||
|
}
|
||
|
|
||
|
@Override public void clear() {
|
||
|
zombie = null;
|
||
|
}
|
||
|
|
||
|
@UnsupportedAppUsage
|
||
|
public static void add(Object referent) {
|
||
|
FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
|
||
|
synchronized (LIST_LOCK) {
|
||
|
reference.prev = null;
|
||
|
reference.next = head;
|
||
|
if (head != null) {
|
||
|
head.prev = reference;
|
||
|
}
|
||
|
head = reference;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@UnsupportedAppUsage
|
||
|
public static void remove(FinalizerReference<?> reference) {
|
||
|
synchronized (LIST_LOCK) {
|
||
|
FinalizerReference<?> next = reference.next;
|
||
|
FinalizerReference<?> prev = reference.prev;
|
||
|
reference.next = null;
|
||
|
reference.prev = null;
|
||
|
if (prev != null) {
|
||
|
prev.next = next;
|
||
|
} else {
|
||
|
head = next;
|
||
|
}
|
||
|
if (next != null) {
|
||
|
next.prev = prev;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Waits for all currently-enqueued references to be finalized.
|
||
|
*/
|
||
|
public static void finalizeAllEnqueued(long timeout) throws InterruptedException {
|
||
|
// Alloate a new sentinel, this creates a FinalizerReference.
|
||
|
Sentinel sentinel;
|
||
|
// Keep looping until we safely enqueue our sentinel FinalizerReference.
|
||
|
// This is done to prevent races where the GC updates the pendingNext
|
||
|
// before we get the chance.
|
||
|
do {
|
||
|
sentinel = new Sentinel();
|
||
|
} while (!enqueueSentinelReference(sentinel));
|
||
|
sentinel.awaitFinalization(timeout);
|
||
|
}
|
||
|
|
||
|
private static boolean enqueueSentinelReference(Sentinel sentinel) {
|
||
|
synchronized (LIST_LOCK) {
|
||
|
// When a finalizable object is allocated, a FinalizerReference is added to the list.
|
||
|
// We search the list for that FinalizerReference (it should be at or near the head),
|
||
|
// and then put it on the queue so that it can be finalized.
|
||
|
for (FinalizerReference<?> r = head; r != null; r = r.next) {
|
||
|
// Use getReferent() instead of directly accessing the referent field not to race
|
||
|
// with GC reference processing. Can't use get() either because it's overridden to
|
||
|
// return the zombie.
|
||
|
if (r.getReferent() == sentinel) {
|
||
|
FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r;
|
||
|
sentinelReference.clearReferent();
|
||
|
sentinelReference.zombie = sentinel;
|
||
|
// Make a single element list, then enqueue the reference on the daemon unenqueued
|
||
|
// list. This is required instead of enqueuing directly on the finalizer queue
|
||
|
// since there could be recently freed objects in the unqueued list which are not
|
||
|
// yet on the finalizer queue. This could cause the sentinel to run before the
|
||
|
// objects are finalized. b/17381967
|
||
|
// Make circular list if unenqueued goes through native so that we can prevent
|
||
|
// races where the GC updates the pendingNext before we do. If it is non null, then
|
||
|
// we update the pending next to make a circular list while holding a lock.
|
||
|
// b/17462553
|
||
|
if (!sentinelReference.makeCircularListIfUnenqueued()) {
|
||
|
return false;
|
||
|
}
|
||
|
ReferenceQueue.add(sentinelReference);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// We just created a finalizable object and still hold a reference to it.
|
||
|
// It must be on the list.
|
||
|
throw new AssertionError("newly-created live Sentinel not on list!");
|
||
|
}
|
||
|
|
||
|
@FastNative
|
||
|
private final native T getReferent();
|
||
|
@FastNative
|
||
|
private native boolean makeCircularListIfUnenqueued();
|
||
|
|
||
|
/**
|
||
|
* A marker object that we can immediately enqueue. When this object's
|
||
|
* finalize() method is called, we know all previously-enqueued finalizable
|
||
|
* references have been finalized.
|
||
|
*/
|
||
|
private static class Sentinel {
|
||
|
boolean finalized = false;
|
||
|
|
||
|
@Override protected synchronized void finalize() throws Throwable {
|
||
|
if (finalized) {
|
||
|
throw new AssertionError();
|
||
|
}
|
||
|
finalized = true;
|
||
|
notifyAll();
|
||
|
}
|
||
|
|
||
|
synchronized void awaitFinalization(long timeout) throws InterruptedException {
|
||
|
final long startTime = System.nanoTime();
|
||
|
final long endTime = startTime + timeout; // May wrap.
|
||
|
while (!finalized) {
|
||
|
// 0 signifies no timeout.
|
||
|
if (timeout != 0) {
|
||
|
final long deltaTime = endTime - System.nanoTime();
|
||
|
if (deltaTime <= 0) {
|
||
|
break;
|
||
|
} else {
|
||
|
wait(deltaTime / 1_000_000, (int)(deltaTime % 1_000_000));
|
||
|
}
|
||
|
} else {
|
||
|
wait();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|