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.*; 26 import java.util.*; 27 28 import jdk.vm.ci.meta.*; 29 30 /** 31 * This class manages the set of metadata roots that must be scanned during garbage collection. 32 * Because of class redefinition Method* and ConstantPool* can be freed if they don't appear to be 33 * in use so they must be tracked when there are live references to them from Java. 34 * 35 * The general theory of operation is that all {@link MetaspaceWrapperObject}s are created by 36 * calling into the VM which calls back out to actually create the wrapper instance. During the call 37 * the VM keeps the metadata reference alive through the use of metadata handles. Once the call 38 * completes the wrapper object is registered here and will be scanned during metadata scanning. The 39 * weakness of the reference to the wrapper object allows them to be reclaimed when they are no 40 * longer used. 41 * 42 */ 43 public class HotSpotJVMCIMetaAccessContext implements JVMCIMetaAccessContext { 44 45 /** 46 * The set of currently live contexts used for tracking of live metadata. Examined from the VM 47 * during garbage collection. 48 */ 49 private static WeakReference<?>[] allContexts = new WeakReference<?>[0]; 50 51 /** 52 * This is a chunked list of metadata roots. It can be read from VM native code so it's been 53 * marked volatile to ensure the order of updates are respected. 54 */ 55 private volatile Object[] metadataRoots; 56 57 private ChunkedList<WeakReference<MetaspaceWrapperObject>> list = new ChunkedList<>(); 58 59 /** 60 * The number of weak references freed since the last time the list was shrunk. 61 */ 62 private int freed; 63 64 /** 65 * The {@link ReferenceQueue} tracking the weak references created by this context. 66 */ 67 private final ReferenceQueue<MetaspaceWrapperObject> queue = new ReferenceQueue<>(); 68 69 static synchronized void add(HotSpotJVMCIMetaAccessContext context) { 70 for (int i = 0; i < allContexts.length; i++) { 71 if (allContexts[i] == null || allContexts[i].get() == null) { 72 allContexts[i] = new WeakReference<>(context); 73 return; 74 } 75 } 76 int index = allContexts.length; 77 allContexts = Arrays.copyOf(allContexts, index + 2); 78 allContexts[index] = new WeakReference<>(context); 79 } 80 81 HotSpotJVMCIMetaAccessContext() { 82 add(this); 83 } 84 85 /** 86 * Periodically trim the list of tracked metadata. A new list is created to replace the old to 87 * avoid concurrent scanning issues. 88 */ 89 private void clean() { 90 Reference<?> ref = queue.poll(); 91 if (ref == null) { 92 return; 93 } 94 while (ref != null) { 95 freed++; 96 ref = queue.poll(); 97 } 98 if (freed > list.size() / 2) { 99 ChunkedList<WeakReference<MetaspaceWrapperObject>> newList = new ChunkedList<>(); 100 for (WeakReference<MetaspaceWrapperObject> element : list) { 101 /* 102 * The referent could become null anywhere in here but it doesn't matter. It will 103 * get cleaned up next time. 104 */ 105 if (element != null && element.get() != null) { 106 newList.add(element); 107 } 108 } 109 list = newList; 110 metadataRoots = list.getHead(); 111 freed = 0; 112 } 113 } 114 115 /** 116 * Add a {@link MetaspaceWrapperObject} to tracked by the GC. It's assumed that the caller is 117 * responsible for keeping the reference alive for the duration of the call. Once registration 118 * is complete then the VM will ensure it's kept alive. 119 * 120 * @param metaspaceObject 121 */ 122 123 public synchronized void add(MetaspaceWrapperObject metaspaceObject) { 124 clean(); 125 list.add(new WeakReference<>(metaspaceObject, queue)); 126 if (list.getHead() != metadataRoots) { 127 /* 128 * The list enlarged so update the head. 129 */ 130 metadataRoots = list.getHead(); 131 } 132 } 133 134 protected ResolvedJavaType createClass(Class<?> javaClass) { 135 if (javaClass.isPrimitive()) { 136 JavaKind kind = JavaKind.fromJavaClass(javaClass); 137 return new HotSpotResolvedPrimitiveType(kind); 138 } else { 139 return new HotSpotResolvedObjectTypeImpl(javaClass, this); 140 } 141 } 142 143 private final Map<Class<?>, WeakReference<ResolvedJavaType>> typeMap = new WeakHashMap<>(); 144 145 @Override 146 public synchronized ResolvedJavaType fromClass(Class<?> javaClass) { 147 WeakReference<ResolvedJavaType> typeRef = typeMap.get(javaClass); 148 ResolvedJavaType type = typeRef != null ? typeRef.get() : null; 149 if (type == null) { 150 type = createClass(javaClass); 151 typeMap.put(javaClass, new WeakReference<>(type)); 152 } 153 return type; 154 } 155 156 /** 157 * A very simple append only chunked list implementation. 158 */ 159 static class ChunkedList<T> implements Iterable<T> { 160 private static final int CHUNK_SIZE = 32; 161 162 private static final int NEXT_CHUNK_INDEX = CHUNK_SIZE - 1; 163 164 private Object[] head; 165 private int index; 166 private int size; 167 168 ChunkedList() { 169 head = new Object[CHUNK_SIZE]; 170 index = 0; 171 } 172 173 void add(T element) { 174 if (index == NEXT_CHUNK_INDEX) { 175 Object[] newHead = new Object[CHUNK_SIZE]; 176 newHead[index] = head; 177 head = newHead; 178 index = 0; 179 } 180 head[index++] = element; 181 size++; 182 } 183 184 Object[] getHead() { 185 return head; 186 } 187 188 public Iterator<T> iterator() { 189 return new ChunkIterator<>(); 190 } 191 192 int size() { 193 return size; 194 } 195 196 class ChunkIterator<V> implements Iterator<V> { 197 198 ChunkIterator() { 199 currentChunk = head; 200 currentIndex = -1; 201 findNext(); 202 } 203 204 Object[] currentChunk; 205 int currentIndex; 206 V next; 207 208 @SuppressWarnings("unchecked") 209 V findNext() { 210 V result; 211 do { 212 currentIndex++; 213 if (currentIndex == NEXT_CHUNK_INDEX) { 214 currentChunk = (Object[]) currentChunk[currentIndex]; 215 currentIndex = 0; 216 if (currentChunk == null) { 217 return null; 218 } 219 } 220 result = (V) currentChunk[currentIndex]; 221 } while (result == null); 222 return result; 223 } 224 225 public boolean hasNext() { 226 return next != null; 227 } 228 229 public V next() { 230 V result = next; 231 next = findNext(); 232 return result; 233 } 234 235 } 236 237 } 238 }