1 /* 2 * Copyright (c) 2016, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.jfr.internal; 26 27 import java.util.concurrent.ConcurrentHashMap; 28 import java.util.concurrent.atomic.AtomicLong; 29 30 import jdk.internal.misc.Unsafe; 31 32 public final class StringPool { 33 34 private static final Unsafe unsafe = Unsafe.getUnsafe(); 35 36 static final int MIN_LIMIT = 16; 37 static final int MAX_LIMIT = 128; /* 0 MAX means disabled */ 38 private static final long epochAddress; 39 private static final SimpleStringIdPool sp = new SimpleStringIdPool(); 40 static { 41 epochAddress = JVM.getJVM().getEpochAddress(); 42 sp.reset(); 43 } 44 public static long addString(String s) { 45 return sp.addString(s); 46 } 47 private static boolean getCurrentEpoch() { 48 return unsafe.getByte(epochAddress) == 1; 49 } 50 private static class SimpleStringIdPool { 51 /* string id index */ 52 private final AtomicLong sidIdx = new AtomicLong(); 53 /* epoch of cached strings */ 54 private boolean poolEpoch; 55 /* the cache */ 56 private final ConcurrentHashMap<String, Long> cache; 57 /* max size */ 58 private final int MAX_SIZE = 32*1024; 59 /* max size bytes*/ 60 private final long MAX_SIZE_UTF16 = 16*1024*1024; 61 /* max size bytes*/ 62 private long currentSizeUTF16; 63 64 /* looking at a biased data set 4 is a good value */ 65 private final String[] preCache = new String[]{"", "" , "" ,""}; 66 /* index of oldest */ 67 private int preCacheOld = 0; 68 /* loop mask */ 69 private static final int preCacheMask = 0x03; 70 71 SimpleStringIdPool() { 72 cache = new ConcurrentHashMap<>(MAX_SIZE, 0.75f); 73 } 74 void reset() { 75 reset(getCurrentEpoch()); 76 } 77 private void reset(boolean epoch) { 78 this.cache.clear(); 79 this.poolEpoch = epoch; 80 this.currentSizeUTF16 = 0; 81 } 82 private long addString(String s) { 83 boolean currentEpoch = getCurrentEpoch(); 84 if (poolEpoch == currentEpoch) { 85 /* pool is for current chunk */ 86 Long lsid = this.cache.get(s); 87 if (lsid != null) { 88 return lsid.longValue(); 89 } 90 } else { 91 /* pool is for an old chunk */ 92 reset(currentEpoch); 93 } 94 if (!preCache(s)) { 95 /* we should not pool this string */ 96 return -1; 97 } 98 if (cache.size() > MAX_SIZE || currentSizeUTF16 > MAX_SIZE_UTF16) { 99 /* pool was full */ 100 reset(currentEpoch); 101 } 102 return storeString(s); 103 } 104 105 private long storeString(String s) { 106 long sid = this.sidIdx.getAndIncrement(); 107 /* we can race but it is ok */ 108 this.cache.put(s, sid); 109 boolean currentEpoch; 110 synchronized(SimpleStringIdPool.class) { 111 currentEpoch = JVM.addStringConstant(poolEpoch, sid, s); 112 currentSizeUTF16 += s.length(); 113 } 114 /* did we write in chunk that this pool represent */ 115 return currentEpoch == poolEpoch ? sid : -1; 116 } 117 private boolean preCache(String s) { 118 if (preCache[0].equals(s)) { 119 return true; 120 } 121 if (preCache[1].equals(s)) { 122 return true; 123 } 124 if (preCache[2].equals(s)) { 125 return true; 126 } 127 if (preCache[3].equals(s)) { 128 return true; 129 } 130 preCacheOld = (preCacheOld - 1) & preCacheMask; 131 preCache[preCacheOld] = s; 132 return false; 133 } 134 } 135 }