Path: blob/master/src/java.base/share/classes/jdk/internal/jimage/ImageBufferCache.java
41159 views
/*1* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package jdk.internal.jimage;2526import java.lang.ref.WeakReference;27import java.nio.ByteBuffer;28import java.util.AbstractMap;29import java.util.Arrays;30import java.util.Comparator;31import java.util.Map;3233/**34* @implNote This class needs to maintain JDK 8 source compatibility.35*36* It is used internally in the JDK to implement jimage/jrtfs access,37* but also compiled and delivered as part of the jrt-fs.jar to support access38* to the jimage file provided by the shipped JDK by tools running on JDK 8.39*/40class ImageBufferCache {41private static final int MAX_CACHED_BUFFERS = 3;42private static final int LARGE_BUFFER = 0x10000;4344/*45* We used to have a class BufferReference extending from WeakReference<ByteBuffer>.46* BufferReference class had an instance field called "capacity". This field was47* used to make DECREASING_CAPACITY_NULLS_LAST comparator stable in the presence48* of GC clearing the WeakReference concurrently.49*50* But this scheme results in metaspace leak. The thread local is alive till the51* the thread is alive. And so ImageBufferCache$BufferReference class was kept alive.52* Because this class and ImageBufferCache$BufferReference are all loaded by a URL53* class loader from jrt-fs.jar, the class loader and so all the classes loaded by it54* were alive!55*56* Solution is to avoid using a URL loader loaded class type with thread local. All we57* need is a pair of WeakReference<ByteBuffer>, Integer (saved capacity for stability58* of comparator). We use Map.Entry as pair implementation. With this, all types used59* with thread local are bootstrap types and so no metaspace leak.60*/61@SuppressWarnings("unchecked")62private static final ThreadLocal<Map.Entry<WeakReference<ByteBuffer>, Integer>[]> CACHE =63new ThreadLocal<Map.Entry<WeakReference<ByteBuffer>, Integer>[]>() {64@Override65protected Map.Entry<WeakReference<ByteBuffer>, Integer>[] initialValue() {66// 1 extra slot to simplify logic of releaseBuffer()67return (Map.Entry<WeakReference<ByteBuffer>, Integer>[])new Map.Entry<?,?>[MAX_CACHED_BUFFERS + 1];68}69};7071private static ByteBuffer allocateBuffer(long size) {72return ByteBuffer.allocateDirect((int)((size + 0xFFF) & ~0xFFF));73}7475static ByteBuffer getBuffer(long size) {76if (size < 0 || Integer.MAX_VALUE < size) {77throw new IndexOutOfBoundsException("size");78}7980ByteBuffer result = null;8182if (size > LARGE_BUFFER) {83result = allocateBuffer(size);84} else {85Map.Entry<WeakReference<ByteBuffer>, Integer>[] cache = CACHE.get();8687// buffers are ordered by decreasing capacity88// cache[MAX_CACHED_BUFFERS] is always null89for (int i = MAX_CACHED_BUFFERS - 1; i >= 0; i--) {90Map.Entry<WeakReference<ByteBuffer>, Integer> reference = cache[i];9192if (reference != null) {93ByteBuffer buffer = getByteBuffer(reference);9495if (buffer != null && size <= buffer.capacity()) {96cache[i] = null;97result = buffer;98result.rewind();99break;100}101}102}103104if (result == null) {105result = allocateBuffer(size);106}107}108109result.limit((int)size);110111return result;112}113114static void releaseBuffer(ByteBuffer buffer) {115if (buffer.capacity() > LARGE_BUFFER) {116return;117}118119Map.Entry<WeakReference<ByteBuffer>, Integer>[] cache = CACHE.get();120121// expunge cleared BufferRef(s)122for (int i = 0; i < MAX_CACHED_BUFFERS; i++) {123Map.Entry<WeakReference<ByteBuffer>, Integer> reference = cache[i];124if (reference != null && getByteBuffer(reference) == null) {125cache[i] = null;126}127}128129// insert buffer back with new BufferRef wrapping it130cache[MAX_CACHED_BUFFERS] = newCacheEntry(buffer);131Arrays.sort(cache, DECREASING_CAPACITY_NULLS_LAST);132// squeeze the smallest one out133cache[MAX_CACHED_BUFFERS] = null;134}135136private static Map.Entry<WeakReference<ByteBuffer>, Integer> newCacheEntry(ByteBuffer bb) {137return new AbstractMap.SimpleEntry<WeakReference<ByteBuffer>, Integer>(138new WeakReference<ByteBuffer>(bb), bb.capacity());139}140141private static int getCapacity(Map.Entry<WeakReference<ByteBuffer>, Integer> e) {142return e == null? 0 : e.getValue();143}144145private static ByteBuffer getByteBuffer(Map.Entry<WeakReference<ByteBuffer>, Integer> e) {146return e == null? null : e.getKey().get();147}148149private static Comparator<Map.Entry<WeakReference<ByteBuffer>, Integer>> DECREASING_CAPACITY_NULLS_LAST =150new Comparator<Map.Entry<WeakReference<ByteBuffer>, Integer>>() {151@Override152public int compare(Map.Entry<WeakReference<ByteBuffer>, Integer> br1,153Map.Entry<WeakReference<ByteBuffer>, Integer> br2) {154return Integer.compare(getCapacity(br1), getCapacity(br2));155}156};157}158159160