1 /*
   2  * Copyright (c) 2015, 2019, 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 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 static class WeakTypeRef {
 149         private WeakReference<ResolvedJavaType> ref;
 150         WeakTypeRef(ResolvedJavaType value) {
 151             set(value);
 152         }
 153         void set(ResolvedJavaType value) {
 154             ref = new WeakReference<ResolvedJavaType>(value);
 155         }
 156         ResolvedJavaType get() {
 157             return ref.get();
 158         }
 159     };
 160 
 161     private final ClassValue<WeakTypeRef> resolvedJavaType = new ClassValue<>() {
 162         @Override
 163         protected WeakTypeRef computeValue(Class<?> type) {
 164             return new WeakTypeRef(createClass(type));
 165         }
 166     };
 167 
 168     /**
 169      * Gets the JVMCI mirror for a {@link Class} object.
 170      *
 171      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
 172      */
 173     public ResolvedJavaType fromClass(Class<?> javaClass) {
 174         WeakTypeRef ref = resolvedJavaType.get(javaClass);
 175         ResolvedJavaType javaType = ref.get();
 176         if (javaType == null) {
 177             /*
 178              * If the referent has become null, create a new value and
 179              * update cached weak reference.
 180              */
 181             javaType = createClass(javaClass);
 182             ref.set(javaType);
 183         }
 184         return javaType;
 185     }
 186 
 187     /**
 188      * A very simple append only chunked list implementation.
 189      */
 190     static class ChunkedList<T> implements Iterable<T> {
 191         private static final int CHUNK_SIZE = 32;
 192 
 193         private static final int NEXT_CHUNK_INDEX = CHUNK_SIZE - 1;
 194 
 195         private Object[] head;
 196         private int index;
 197         private int size;
 198 
 199         ChunkedList() {
 200             head = new Object[CHUNK_SIZE];
 201             index = 0;
 202         }
 203 
 204         void add(T element) {
 205             if (index == NEXT_CHUNK_INDEX) {
 206                 Object[] newHead = new Object[CHUNK_SIZE];
 207                 newHead[index] = head;
 208                 head = newHead;
 209                 index = 0;
 210             }
 211             head[index++] = element;
 212             size++;
 213         }
 214 
 215         Object[] getHead() {
 216             return head;
 217         }
 218 
 219         @Override
 220         public Iterator<T> iterator() {
 221             return new ChunkIterator<>();
 222         }
 223 
 224         int size() {
 225             return size;
 226         }
 227 
 228         class ChunkIterator<V> implements Iterator<V> {
 229 
 230             ChunkIterator() {
 231                 currentChunk = head;
 232                 currentIndex = -1;
 233                 next = findNext();
 234             }
 235 
 236             Object[] currentChunk;
 237             int currentIndex;
 238             V next;
 239 
 240             @SuppressWarnings("unchecked")
 241             V findNext() {
 242                 V result;
 243                 do {
 244                     currentIndex++;
 245                     if (currentIndex == NEXT_CHUNK_INDEX) {
 246                         currentChunk = (Object[]) currentChunk[currentIndex];
 247                         currentIndex = 0;
 248                         if (currentChunk == null) {
 249                             return null;
 250                         }
 251                     }
 252                     result = (V) currentChunk[currentIndex];
 253                 } while (result == null);
 254                 return result;
 255             }
 256 
 257             @Override
 258             public boolean hasNext() {
 259                 return next != null;
 260             }
 261 
 262             @Override
 263             public V next() {
 264                 V result = next;
 265                 next = findNext();
 266                 return result;
 267             }
 268 
 269         }
 270 
 271     }
 272 
 273     synchronized boolean isRegistered(MetaspaceWrapperObject wrapper) {
 274         for (WeakReference<MetaspaceWrapperObject> m : list) {
 275             if (m != null && m.get() == wrapper) {
 276                 return true;
 277             }
 278         }
 279         return false;
 280     }
 281 }