1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.hotspot;
  24 
  25 import java.lang.ref.Reference;
  26 import java.lang.ref.ReferenceQueue;
  27 import java.lang.ref.WeakReference;
  28 import java.util.Arrays;
  29 import java.util.Iterator;
  30 
  31 import jdk.vm.ci.meta.JavaKind;
  32 import jdk.vm.ci.meta.ResolvedJavaType;
  33 
  34 /**
  35  * This class manages the set of metadata roots that must be scanned during garbage collection.
  36  * Because of class redefinition Method* and ConstantPool* can be freed if they don't appear to be
  37  * in use so they must be tracked when there are live references to them from Java.
  38  *
  39  * The general theory of operation is that all {@link MetaspaceWrapperObject}s are created by
  40  * calling into the VM which calls back out to actually create the wrapper instance. During the call
  41  * the VM keeps the metadata reference alive through the use of metadata handles. Once the call
  42  * completes the wrapper object is registered here and will be scanned during metadata scanning. The
  43  * weakness of the reference to the wrapper object allows them to be reclaimed when they are no
  44  * longer used.
  45  *
  46  */
  47 public class HotSpotJVMCIMetaAccessContext {
  48 
  49     /**
  50      * The set of currently live contexts used for tracking of live metadata. Examined from the VM
  51      * during garbage collection.
  52      */
  53     private static WeakReference<?>[] allContexts = new WeakReference<?>[0];
  54 
  55     /**
  56      * This is a chunked list of metadata roots. It can be read from VM native code so it's been
  57      * marked volatile to ensure the order of updates are respected.
  58      */
  59     private volatile Object[] metadataRoots;
  60 
  61     private ChunkedList<WeakReference<MetaspaceWrapperObject>> list = new ChunkedList<>();
  62 
  63     /**
  64      * The number of weak references freed since the last time the list was shrunk.
  65      */
  66     private int freed;
  67 
  68     /**
  69      * The {@link ReferenceQueue} tracking the weak references created by this context.
  70      */
  71     private final ReferenceQueue<MetaspaceWrapperObject> queue = new ReferenceQueue<>();
  72 
  73     static synchronized void add(HotSpotJVMCIMetaAccessContext context) {
  74         for (int i = 0; i < allContexts.length; i++) {
  75             if (allContexts[i] == null || allContexts[i].get() == null) {
  76                 allContexts[i] = new WeakReference<>(context);
  77                 return;
  78             }
  79         }
  80         int index = allContexts.length;
  81         allContexts = Arrays.copyOf(allContexts, index + 2);
  82         allContexts[index] = new WeakReference<>(context);
  83     }
  84 
  85     HotSpotJVMCIMetaAccessContext() {
  86         add(this);
  87     }
  88 
  89     /**
  90      * Periodically trim the list of tracked metadata. A new list is created to replace the old to
  91      * avoid concurrent scanning issues.
  92      */
  93     private void clean() {
  94         Reference<?> ref = queue.poll();
  95         if (ref == null) {
  96             return;
  97         }
  98         while (ref != null) {
  99             freed++;
 100             ref = queue.poll();
 101         }
 102         if (freed > list.size() / 2) {
 103             ChunkedList<WeakReference<MetaspaceWrapperObject>> newList = new ChunkedList<>();
 104             for (WeakReference<MetaspaceWrapperObject> element : list) {
 105                 /*
 106                  * The referent could become null anywhere in here but it doesn't matter. It will
 107                  * get cleaned up next time.
 108                  */
 109                 if (element != null && element.get() != null) {
 110                     newList.add(element);
 111                 }
 112             }
 113             list = newList;
 114             metadataRoots = list.getHead();
 115             freed = 0;
 116         }
 117     }
 118 
 119     /**
 120      * Add a {@link MetaspaceWrapperObject} to tracked by the GC. It's assumed that the caller is
 121      * responsible for keeping the reference alive for the duration of the call. Once registration
 122      * is complete then the VM will ensure it's kept alive.
 123      *
 124      * @param metaspaceObject
 125      */
 126 
 127     public synchronized void add(MetaspaceWrapperObject metaspaceObject) {
 128         clean();
 129         list.add(new WeakReference<>(metaspaceObject, queue));
 130         if (list.getHead() != metadataRoots) {
 131             /*
 132              * The list enlarged so update the head.
 133              */
 134             metadataRoots = list.getHead();
 135         }
 136         assert isRegistered(metaspaceObject);
 137     }
 138 
 139     protected ResolvedJavaType createClass(Class<?> javaClass) {
 140         if (javaClass.isPrimitive()) {
 141             JavaKind kind = JavaKind.fromJavaClass(javaClass);
 142             return new HotSpotResolvedPrimitiveType(kind);
 143         } else {
 144             return new HotSpotResolvedObjectTypeImpl(javaClass, this);
 145         }
 146     }
 147 
 148     private final ClassValue<WeakReference<ResolvedJavaType>> resolvedJavaType = new ClassValue<WeakReference<ResolvedJavaType>>() {
 149         @Override
 150         protected WeakReference<ResolvedJavaType> computeValue(Class<?> type) {
 151             return new WeakReference<>(createClass(type));
 152         }
 153     };
 154 
 155     /**
 156      * Gets the JVMCI mirror for a {@link Class} object.
 157      *
 158      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
 159      */
 160     public ResolvedJavaType fromClass(Class<?> javaClass) {
 161         ResolvedJavaType javaType = null;
 162         while (javaType == null) {
 163             WeakReference<ResolvedJavaType> type = resolvedJavaType.get(javaClass);
 164             javaType = type.get();
 165             if (javaType == null) {
 166                 /*
 167                  * If the referent has become null, clear out the current value
 168                  * and let computeValue above create a new value.  Reload the
 169                  * value in a loop because in theory the WeakReference referent
 170                  * can be reclaimed at any point.
 171                  */
 172                 resolvedJavaType.remove(javaClass);
 173             }
 174         }
 175         return javaType;
 176     }
 177 
 178     /**
 179      * A very simple append only chunked list implementation.
 180      */
 181     static class ChunkedList<T> implements Iterable<T> {
 182         private static final int CHUNK_SIZE = 32;
 183 
 184         private static final int NEXT_CHUNK_INDEX = CHUNK_SIZE - 1;
 185 
 186         private Object[] head;
 187         private int index;
 188         private int size;
 189 
 190         ChunkedList() {
 191             head = new Object[CHUNK_SIZE];
 192             index = 0;
 193         }
 194 
 195         void add(T element) {
 196             if (index == NEXT_CHUNK_INDEX) {
 197                 Object[] newHead = new Object[CHUNK_SIZE];
 198                 newHead[index] = head;
 199                 head = newHead;
 200                 index = 0;
 201             }
 202             head[index++] = element;
 203             size++;
 204         }
 205 
 206         Object[] getHead() {
 207             return head;
 208         }
 209 
 210         public Iterator<T> iterator() {
 211             return new ChunkIterator<>();
 212         }
 213 
 214         int size() {
 215             return size;
 216         }
 217 
 218         class ChunkIterator<V> implements Iterator<V> {
 219 
 220             ChunkIterator() {
 221                 currentChunk = head;
 222                 currentIndex = -1;
 223                 next = findNext();
 224             }
 225 
 226             Object[] currentChunk;
 227             int currentIndex;
 228             V next;
 229 
 230             @SuppressWarnings("unchecked")
 231             V findNext() {
 232                 V result;
 233                 do {
 234                     currentIndex++;
 235                     if (currentIndex == NEXT_CHUNK_INDEX) {
 236                         currentChunk = (Object[]) currentChunk[currentIndex];
 237                         currentIndex = 0;
 238                         if (currentChunk == null) {
 239                             return null;
 240                         }
 241                     }
 242                     result = (V) currentChunk[currentIndex];
 243                 } while (result == null);
 244                 return result;
 245             }
 246 
 247             public boolean hasNext() {
 248                 return next != null;
 249             }
 250 
 251             public V next() {
 252                 V result = next;
 253                 next = findNext();
 254                 return result;
 255             }
 256 
 257         }
 258 
 259     }
 260 
 261     synchronized boolean isRegistered(MetaspaceWrapperObject wrapper) {
 262         for (WeakReference<MetaspaceWrapperObject> m : list) {
 263             if (m != null && m.get() == wrapper) {
 264                 return true;
 265             }
 266         }
 267         return false;
 268     }
 269 }