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 */
23package jdk.vm.ci.hotspot;
24
25import java.lang.ref.Reference;
26import java.lang.ref.ReferenceQueue;
27import java.lang.ref.WeakReference;
28import java.util.Arrays;
29import java.util.Iterator;
30
31import jdk.vm.ci.meta.JavaKind;
32import 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 */
47public 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}
270