| /* | |
| * Copyright (c) 2021, 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.io; | |
| import java.lang.ref.Reference; | |
| import java.lang.ref.ReferenceQueue; | |
| import java.lang.ref.SoftReference; | |
| import java.util.Objects; | |
| // Maps Class instances to values of type T. Under memory pressure, the | |
| // mapping is released (under soft references GC policy) and would be | |
| // recomputed the next time it is queried. The mapping is bound to the | |
| // lifetime of the class: when the class is unloaded, the mapping is | |
| // removed too. | |
| abstract class ClassCache<T> { | |
| private static class CacheRef<T> extends SoftReference<T> { | |
| private final Class<?> type; | |
| // This field is deliberately accessed without sychronization. ClassValue | |
| // provides synchronization when CacheRef is published. However, when | |
| // a thread reads this field, while another thread is clearing the field, it | |
| // would formally constitute a data race. But that data race is benign, and | |
| // fixing it could introduce noticeable performance penalty, see JDK-8309688. | |
| private T strongReferent; | |
| CacheRef(T referent, ReferenceQueue<T> queue, Class<?> type) { | |
| super(referent, queue); | |
| this.type = type; | |
| this.strongReferent = referent; | |
| } | |
| Class<?> getType() { | |
| return type; | |
| } | |
| T getStrong() { | |
| return strongReferent; | |
| } | |
| void clearStrong() { | |
| strongReferent = null; | |
| } | |
| } | |
| private final ReferenceQueue<T> queue; | |
| private final ClassValue<CacheRef<T>> map; | |
| protected abstract T computeValue(Class<?> cl); | |
| protected ClassCache() { | |
| queue = new ReferenceQueue<>(); | |
| map = new ClassValue<>() { | |
| @Override | |
| protected CacheRef<T> computeValue(Class<?> type) { | |
| T v = ClassCache.this.computeValue(type); | |
| Objects.requireNonNull(v); | |
| return new CacheRef<>(v, queue, type); | |
| } | |
| }; | |
| } | |
| T get(Class<?> cl) { | |
| while (true) { | |
| processQueue(); | |
| CacheRef<T> ref = map.get(cl); | |
| // Case 1: A recently created CacheRef. | |
| // We might still have strong referent, and can return it. | |
| // This guarantees progress for at least one thread on every CacheRef. | |
| // Clear the strong referent before returning to make the cache soft. | |
| T strongVal = ref.getStrong(); | |
| if (strongVal != null) { | |
| ref.clearStrong(); | |
| return strongVal; | |
| } | |
| // Case 2: Older or recently cleared CacheRef. | |
| // Check if its soft referent is still available, and return it. | |
| T val = ref.get(); | |
| if (val != null) { | |
| return val; | |
| } | |
| // Case 3: The reference was cleared. | |
| // Clear the mapping and retry. | |
| map.remove(cl); | |
| } | |
| } | |
| private void processQueue() { | |
| Reference<? extends T> ref; | |
| while((ref = queue.poll()) != null) { | |
| CacheRef<? extends T> cacheRef = (CacheRef<? extends T>)ref; | |
| map.remove(cacheRef.getType()); | |
| } | |
| } | |
| } |