--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIMetaAccessContext.java 2017-02-15 16:55:48.185265373 -0800 @@ -0,0 +1,269 @@ +/* + * 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. + * + * 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.vm.ci.hotspot; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Iterator; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * This class manages the set of metadata roots that must be scanned during garbage collection. + * Because of class redefinition Method* and ConstantPool* can be freed if they don't appear to be + * in use so they must be tracked when there are live references to them from Java. + * + * The general theory of operation is that all {@link MetaspaceWrapperObject}s are created by + * calling into the VM which calls back out to actually create the wrapper instance. During the call + * the VM keeps the metadata reference alive through the use of metadata handles. Once the call + * completes the wrapper object is registered here and will be scanned during metadata scanning. The + * weakness of the reference to the wrapper object allows them to be reclaimed when they are no + * longer used. + * + */ +public class HotSpotJVMCIMetaAccessContext { + + /** + * The set of currently live contexts used for tracking of live metadata. Examined from the VM + * during garbage collection. + */ + private static WeakReference[] allContexts = new WeakReference[0]; + + /** + * This is a chunked list of metadata roots. It can be read from VM native code so it's been + * marked volatile to ensure the order of updates are respected. + */ + private volatile Object[] metadataRoots; + + private ChunkedList> list = new ChunkedList<>(); + + /** + * The number of weak references freed since the last time the list was shrunk. + */ + private int freed; + + /** + * The {@link ReferenceQueue} tracking the weak references created by this context. + */ + private final ReferenceQueue queue = new ReferenceQueue<>(); + + static synchronized void add(HotSpotJVMCIMetaAccessContext context) { + for (int i = 0; i < allContexts.length; i++) { + if (allContexts[i] == null || allContexts[i].get() == null) { + allContexts[i] = new WeakReference<>(context); + return; + } + } + int index = allContexts.length; + allContexts = Arrays.copyOf(allContexts, index + 2); + allContexts[index] = new WeakReference<>(context); + } + + HotSpotJVMCIMetaAccessContext() { + add(this); + } + + /** + * Periodically trim the list of tracked metadata. A new list is created to replace the old to + * avoid concurrent scanning issues. + */ + private void clean() { + Reference ref = queue.poll(); + if (ref == null) { + return; + } + while (ref != null) { + freed++; + ref = queue.poll(); + } + if (freed > list.size() / 2) { + ChunkedList> newList = new ChunkedList<>(); + for (WeakReference element : list) { + /* + * The referent could become null anywhere in here but it doesn't matter. It will + * get cleaned up next time. + */ + if (element != null && element.get() != null) { + newList.add(element); + } + } + list = newList; + metadataRoots = list.getHead(); + freed = 0; + } + } + + /** + * Add a {@link MetaspaceWrapperObject} to tracked by the GC. It's assumed that the caller is + * responsible for keeping the reference alive for the duration of the call. Once registration + * is complete then the VM will ensure it's kept alive. + * + * @param metaspaceObject + */ + + public synchronized void add(MetaspaceWrapperObject metaspaceObject) { + clean(); + list.add(new WeakReference<>(metaspaceObject, queue)); + if (list.getHead() != metadataRoots) { + /* + * The list enlarged so update the head. + */ + metadataRoots = list.getHead(); + } + assert isRegistered(metaspaceObject); + } + + protected ResolvedJavaType createClass(Class javaClass) { + if (javaClass.isPrimitive()) { + JavaKind kind = JavaKind.fromJavaClass(javaClass); + return new HotSpotResolvedPrimitiveType(kind); + } else { + return new HotSpotResolvedObjectTypeImpl(javaClass, this); + } + } + + private final ClassValue> resolvedJavaType = new ClassValue>() { + @Override + protected WeakReference computeValue(Class type) { + return new WeakReference<>(createClass(type)); + } + }; + + /** + * Gets the JVMCI mirror for a {@link Class} object. + * + * @return the {@link ResolvedJavaType} corresponding to {@code javaClass} + */ + public ResolvedJavaType fromClass(Class javaClass) { + ResolvedJavaType javaType = null; + while (javaType == null) { + WeakReference type = resolvedJavaType.get(javaClass); + javaType = type.get(); + if (javaType == null) { + /* + * If the referent has become null, clear out the current value + * and let computeValue above create a new value. Reload the + * value in a loop because in theory the WeakReference referent + * can be reclaimed at any point. + */ + resolvedJavaType.remove(javaClass); + } + } + return javaType; + } + + /** + * A very simple append only chunked list implementation. + */ + static class ChunkedList implements Iterable { + private static final int CHUNK_SIZE = 32; + + private static final int NEXT_CHUNK_INDEX = CHUNK_SIZE - 1; + + private Object[] head; + private int index; + private int size; + + ChunkedList() { + head = new Object[CHUNK_SIZE]; + index = 0; + } + + void add(T element) { + if (index == NEXT_CHUNK_INDEX) { + Object[] newHead = new Object[CHUNK_SIZE]; + newHead[index] = head; + head = newHead; + index = 0; + } + head[index++] = element; + size++; + } + + Object[] getHead() { + return head; + } + + public Iterator iterator() { + return new ChunkIterator<>(); + } + + int size() { + return size; + } + + class ChunkIterator implements Iterator { + + ChunkIterator() { + currentChunk = head; + currentIndex = -1; + next = findNext(); + } + + Object[] currentChunk; + int currentIndex; + V next; + + @SuppressWarnings("unchecked") + V findNext() { + V result; + do { + currentIndex++; + if (currentIndex == NEXT_CHUNK_INDEX) { + currentChunk = (Object[]) currentChunk[currentIndex]; + currentIndex = 0; + if (currentChunk == null) { + return null; + } + } + result = (V) currentChunk[currentIndex]; + } while (result == null); + return result; + } + + public boolean hasNext() { + return next != null; + } + + public V next() { + V result = next; + next = findNext(); + return result; + } + + } + + } + + synchronized boolean isRegistered(MetaspaceWrapperObject wrapper) { + for (WeakReference m : list) { + if (m != null && m.get() == wrapper) { + return true; + } + } + return false; + } +}