/
githubmirror
/
riscv-port
Обзор
Документация
Войти
/
githubmirror
/
riscv-port
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java
365 строк
12 KB
Kim Barrett
8351374: Improve comment about queue.remove timeout in CleanerImpl.run
18 мар 2025, 21:59
18 мар 2025, 21:59
355b2f3
Код
Авторство
О чём код?
/* * Copyright (c) 2015, 2025, 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 jdk.internal.ref; import java.lang.ref.Cleaner; import java.lang.ref.Cleaner.Cleanable; import java.lang.ref.ReferenceQueue; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import jdk.internal.misc.InnocuousThread; /** * CleanerImpl manages a set of object references and corresponding cleaning actions. * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}. */ public final class CleanerImpl implements Runnable { /** * An object to access the CleanerImpl from a Cleaner; set by Cleaner init. */ private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null; /** * Currently active PhantomCleanable-s. */ final CleanableList activeList; // The ReferenceQueue of pending cleaning actions final ReferenceQueue<Object> queue; /** * Called by Cleaner static initialization to provide the function * to map from Cleaner to CleanerImpl. * @param access a function to map from Cleaner to CleanerImpl */ public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) { if (cleanerImplAccess == null) { cleanerImplAccess = access; } else { throw new InternalError("cleanerImplAccess"); } } /** * Called to get the CleanerImpl for a Cleaner. * @param cleaner the cleaner * @return the corresponding CleanerImpl */ static CleanerImpl getCleanerImpl(Cleaner cleaner) { return cleanerImplAccess.apply(cleaner); } /** * Constructor for CleanerImpl. */ public CleanerImpl() { queue = new ReferenceQueue<>(); activeList = new CleanableList(); } /** * Starts the Cleaner implementation. * Ensure this is the CleanerImpl for the Cleaner. * When started waits for Cleanables to be queued. * @param cleaner the cleaner * @param threadFactory the thread factory */ public void start(Cleaner cleaner, ThreadFactory threadFactory) { if (getCleanerImpl(cleaner) != this) { throw new AssertionError("wrong cleaner"); } // schedule a nop cleaning action for the cleaner, so the associated thread // will continue to run at least until the cleaner is reclaimable. new CleanerCleanable(cleaner); if (threadFactory == null) { threadFactory = CleanerImpl.InnocuousThreadFactory.factory(); } // now that there's at least one cleaning action, for the cleaner, // we can start the associated thread, which runs until // all cleaning actions have been run. Thread thread = threadFactory.newThread(this); thread.setDaemon(true); thread.start(); } /** * Process queued Cleanables as long as the cleanable lists are not empty. * A Cleanable is in one of the lists for each Object and for the Cleaner * itself. * Terminates when the Cleaner is no longer reachable and * has been cleaned and there are no more Cleanable instances * for which the object is reachable. * <p> * If the thread is a ManagedLocalsThread, the threadlocals * are erased before each cleanup */ @Override public void run() { Thread t = Thread.currentThread(); InnocuousThread mlThread = (t instanceof InnocuousThread) ? (InnocuousThread) t : null; while (!activeList.isEmpty()) { if (mlThread != null) { // Clear the thread locals mlThread.eraseThreadLocals(); } try { // Wait for a Ref, with a timeout to avoid a potential hang. // The Cleaner may become unreachable and its cleanable run, // while there are registered cleanables for other objects. // If the application explicitly calls clean() on all remaining // Cleanables, there won't be any references enqueued to unblock // this. Using a timeout is simpler than unblocking this by // having cleaning of the last registered cleanable enqueue a // dummy reference. 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) } } } /** * Perform cleaning on an unreachable PhantomReference. */ public static final class PhantomCleanableRef extends PhantomCleanable<Object> { private final Runnable action; /** * Constructor for a phantom cleanable reference. * @param obj the object to monitor * @param cleaner the cleaner * @param action the action Runnable */ public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { super(obj, cleaner); this.action = action; } @Override protected void performCleanup() { action.run(); } /** * Prevent access to referent even when it is still alive. * * @throws UnsupportedOperationException always */ @Override public Object get() { throw new UnsupportedOperationException("get"); } /** * Direct clearing of the referent is not supported. * * @throws UnsupportedOperationException always */ @Override public void clear() { throw new UnsupportedOperationException("clear"); } } /** * A ThreadFactory for InnocuousThreads. * The factory is a singleton. */ static final class InnocuousThreadFactory implements ThreadFactory { static final ThreadFactory factory = new InnocuousThreadFactory(); static ThreadFactory factory() { return factory; } final AtomicInteger cleanerThreadNumber = new AtomicInteger(); public Thread newThread(Runnable r) { return InnocuousThread.newThread("Cleaner-" + cleanerThreadNumber.getAndIncrement(), r, Thread.MAX_PRIORITY - 2); } } /** * A PhantomCleanable implementation for tracking the Cleaner itself. */ static final class CleanerCleanable extends PhantomCleanable<Cleaner> { CleanerCleanable(Cleaner cleaner) { super(cleaner, cleaner); } @Override protected void performCleanup() { // no action } } /** * A specialized implementation that tracks phantom cleanables. */ static final class CleanableList { /** * Capacity for a single node in the list. * This balances memory overheads vs locality vs GC walking costs. */ static final int NODE_CAPACITY = 4096; /** * Head node. This is the only node where PhantomCleanables are * added to or removed from. This is the only node with variable size, * all other nodes linked from the head are always at full capacity. */ private Node head; /** * Cached node instance to provide better behavior near NODE_CAPACITY * threshold: if list size flips around NODE_CAPACITY, it would reuse * the cached node instead of wasting and re-allocating a new node all * the time. */ private Node cache; public CleanableList() { reset(); } /** * Testing support: reset list to initial state. */ synchronized void reset() { this.head = new Node(); } /** * Returns true if cleanable list is empty. * * @return true if the list is empty */ public synchronized boolean isEmpty() { // Head node size is zero only when the entire list is empty. return head.size == 0; } /** * Insert this PhantomCleanable in the list. */ public synchronized void insert(PhantomCleanable<?> phc) { if (head.size == NODE_CAPACITY) { // Head node is full, insert new one. // If possible, pick a pre-allocated node from cache. Node newHead; if (cache != null) { newHead = cache; cache = null; } else { newHead = new Node(); } newHead.next = head; head = newHead; } assert head.size < NODE_CAPACITY; // Put the incoming object in head node and record indexes. final int lastIndex = head.size; phc.node = head; phc.index = lastIndex; head.arr[lastIndex] = phc; head.size++; } /** * Remove this PhantomCleanable from the list. * * @return true if Cleanable was removed or false if not because * it had already been removed before */ public synchronized boolean remove(PhantomCleanable<?> phc) { if (phc.node == null) { // Not in the list. return false; } assert phc.node.arr[phc.index] == phc; // Replace with another element from the head node, as long // as it is not the same element. This keeps all non-head // nodes at full capacity. final int lastIndex = head.size - 1; assert lastIndex >= 0; if (head != phc.node || (phc.index != lastIndex)) { PhantomCleanable<?> mover = head.arr[lastIndex]; mover.node = phc.node; mover.index = phc.index; phc.node.arr[phc.index] = mover; } // Now we can unlink the removed element. phc.node = null; // Remove the last element from the head node. head.arr[lastIndex] = null; head.size--; // If head node becomes empty after this, and there are // nodes that follow it, replace the head node with another // full one. If needed, stash the now free node in cache. if (head.size == 0 && head.next != null) { Node newHead = head.next; if (cache == null) { cache = head; cache.next = null; } head = newHead; } return true; } /** * Segment node. */ static class Node { // Array of tracked cleanables, and the amount of elements in it. final PhantomCleanable<?>[] arr = new PhantomCleanable<?>[NODE_CAPACITY]; int size; // Linked list structure. Node next; } } }