📄 stringcache.java
字号:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.buf;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeMap;
/**
* This class implements a String cache for ByteChunk and CharChunk.
*
* @author Remy Maucherat
*/
public class StringCache {
private static org.apache.juli.logging.Log log=
org.apache.juli.logging.LogFactory.getLog( StringCache.class );
// ------------------------------------------------------- Static Variables
/**
* Enabled ?
*/
protected static boolean byteEnabled =
("true".equals(System.getProperty("tomcat.util.buf.StringCache.byte.enabled", "false")));
protected static boolean charEnabled =
("true".equals(System.getProperty("tomcat.util.buf.StringCache.char.enabled", "false")));
protected static int trainThreshold =
Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.trainThreshold", "20000"));
protected static int cacheSize =
Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.cacheSize", "200"));
protected static int maxStringSize =
Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.maxStringSize", "128"));
/**
* Statistics hash map for byte chunk.
*/
protected static HashMap bcStats = new HashMap(cacheSize);
/**
* toString count for byte chunk.
*/
protected static int bcCount = 0;
/**
* Cache for byte chunk.
*/
protected static ByteEntry[] bcCache = null;
/**
* Statistics hash map for char chunk.
*/
protected static HashMap ccStats = new HashMap(cacheSize);
/**
* toString count for char chunk.
*/
protected static int ccCount = 0;
/**
* Cache for char chunk.
*/
protected static CharEntry[] ccCache = null;
/**
* Access count.
*/
protected static int accessCount = 0;
/**
* Hit count.
*/
protected static int hitCount = 0;
// ------------------------------------------------------------ Properties
/**
* @return Returns the cacheSize.
*/
public int getCacheSize() {
return cacheSize;
}
/**
* @param cacheSize The cacheSize to set.
*/
public void setCacheSize(int cacheSize) {
StringCache.cacheSize = cacheSize;
}
/**
* @return Returns the enabled.
*/
public boolean getByteEnabled() {
return byteEnabled;
}
/**
* @param byteEnabled The enabled to set.
*/
public void setByteEnabled(boolean byteEnabled) {
StringCache.byteEnabled = byteEnabled;
}
/**
* @return Returns the enabled.
*/
public boolean getCharEnabled() {
return charEnabled;
}
/**
* @param charEnabled The enabled to set.
*/
public void setCharEnabled(boolean charEnabled) {
StringCache.charEnabled = charEnabled;
}
/**
* @return Returns the trainThreshold.
*/
public int getTrainThreshold() {
return trainThreshold;
}
/**
* @param trainThreshold The trainThreshold to set.
*/
public void setTrainThreshold(int trainThreshold) {
StringCache.trainThreshold = trainThreshold;
}
/**
* @return Returns the accessCount.
*/
public int getAccessCount() {
return accessCount;
}
/**
* @return Returns the hitCount.
*/
public int getHitCount() {
return hitCount;
}
// -------------------------------------------------- Public Static Methods
public void reset() {
hitCount = 0;
accessCount = 0;
synchronized (bcStats) {
bcCache = null;
bcCount = 0;
}
synchronized (ccStats) {
ccCache = null;
ccCount = 0;
}
}
public static String toString(ByteChunk bc) {
// If the cache is null, then either caching is disabled, or we're
// still training
if (bcCache == null) {
String value = bc.toStringInternal();
if (byteEnabled && (value.length() < maxStringSize)) {
// If training, everything is synced
synchronized (bcStats) {
// If the cache has been generated on a previous invocation
// while waiting fot the lock, just return the toString value
// we just calculated
if (bcCache != null) {
return value;
}
// Two cases: either we just exceeded the train count, in which
// case the cache must be created, or we just update the count for
// the string
if (bcCount > trainThreshold) {
long t1 = System.currentTimeMillis();
// Sort the entries according to occurrence
TreeMap tempMap = new TreeMap();
Iterator entries = bcStats.keySet().iterator();
while (entries.hasNext()) {
ByteEntry entry = (ByteEntry) entries.next();
int[] countA = (int[]) bcStats.get(entry);
Integer count = new Integer(countA[0]);
// Add to the list for that count
ArrayList list = (ArrayList) tempMap.get(count);
if (list == null) {
// Create list
list = new ArrayList();
tempMap.put(count, list);
}
list.add(entry);
}
// Allocate array of the right size
int size = bcStats.size();
if (size > cacheSize) {
size = cacheSize;
}
ByteEntry[] tempbcCache = new ByteEntry[size];
// Fill it up using an alphabetical order
// and a dumb insert sort
ByteChunk tempChunk = new ByteChunk();
int n = 0;
while (n < size) {
Object key = tempMap.lastKey();
ArrayList list = (ArrayList) tempMap.get(key);
ByteEntry[] list2 =
(ByteEntry[]) list.toArray(new ByteEntry[list.size()]);
for (int i = 0; i < list.size() && n < size; i++) {
ByteEntry entry = (ByteEntry) list.get(i);
tempChunk.setBytes(entry.name, 0, entry.name.length);
int insertPos = findClosest(tempChunk, tempbcCache, n);
if (insertPos == n) {
tempbcCache[n + 1] = entry;
} else {
System.arraycopy(tempbcCache, insertPos + 1, tempbcCache,
insertPos + 2, n - insertPos - 1);
tempbcCache[insertPos + 1] = entry;
}
n++;
}
tempMap.remove(key);
}
bcCount = 0;
bcStats.clear();
bcCache = tempbcCache;
if (log.isDebugEnabled()) {
long t2 = System.currentTimeMillis();
log.debug("ByteCache generation time: " + (t2 - t1) + "ms");
}
} else {
bcCount++;
// Allocate new ByteEntry for the lookup
ByteEntry entry = new ByteEntry();
entry.value = value;
int[] count = (int[]) bcStats.get(entry);
if (count == null) {
int end = bc.getEnd();
int start = bc.getStart();
// Create byte array and copy bytes
entry.name = new byte[bc.getLength()];
System.arraycopy(bc.getBuffer(), start, entry.name, 0, end - start);
// Set encoding
entry.enc = bc.getEncoding();
// Initialize occurrence count to one
count = new int[1];
count[0] = 1;
// Set in the stats hash map
bcStats.put(entry, count);
} else {
count[0] = count[0] + 1;
}
}
}
}
return value;
} else {
accessCount++;
// Find the corresponding String
String result = find(bc);
if (result == null) {
return bc.toStringInternal();
}
// Note: We don't care about safety for the stats
hitCount++;
return result;
}
}
public static String toString(CharChunk cc) {
// If the cache is null, then either caching is disabled, or we're
// still training
if (ccCache == null) {
String value = cc.toStringInternal();
if (charEnabled && (value.length() < maxStringSize)) {
// If training, everything is synced
synchronized (ccStats) {
// If the cache has been generated on a previous invocation
// while waiting fot the lock, just return the toString value
// we just calculated
if (ccCache != null) {
return value;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -