FileDocCategorySizeDatePackage
MultiLruCache.javaAPI DocGlassfish v2 API11287Fri May 04 22:35:28 BST 2007com.sun.appserv.util.cache

MultiLruCache

public class MultiLruCache extends BaseCache
MultiLruCache -- in-memory bounded LRU cache with multiple LRU lists Underlying Hashtable is made into logical segments, with each segment having its own LRU list.

Fields Summary
public static final int
LRU_HEAD
public static final int
LRU_TAIL
public static final int
DEFAULT_HASHTABLE_SEGMENT_SIZE
int
segmentSize
LruCacheItem[]
lists
protected int[]
listsLength
int
trimCount
int
trimIndex
Object
trimIndexLk
Constructors Summary
Methods Summary
protected CacheItemcreateItem(int hashCode, java.lang.Object key, java.lang.Object value, int size)
create new item

param
hashCode for the entry
param
key Object key
param
value Object value
param
size size in bytes of the item subclasses may override to provide their own CacheItem extensions e.g. one that permits persistence.

        return new LruCacheItem(hashCode, key, value, size);
    
private com.sun.appserv.util.cache.MultiLruCache$LruCacheItem[]getLRUList(int index)
get the LRU list associated with the index

param
index into the BaseCache hashtable
return
the LRU list to be used

        int segment = (index/segmentSize);
        return lists[segment];
    
intgetListsLength()

        return lists.length;
    
public java.lang.ObjectgetStatByName(java.lang.String key)
get the desired statistic counter

param
key to corresponding stat
return
an Object corresponding to the stat See also: Constant.java for the key

        Object stat = super.getStatByName(key);

        if (stat == null && key != null) {
            if (key.equals(Constants.STAT_MULTILRUCACHE_SEGMENT_SIZE))
                stat = Integer.valueOf(segmentSize);
            else if (key.equals(Constants.STAT_MULTILRUCACHE_TRIM_COUNT))
                stat = Integer.valueOf(trimCount);
            else if (key.equals(Constants.STAT_MULTILRUCACHE_SEGMENT_LIST_LENGTH)) {
                stat = new Integer[lists.length];

                for (int i = 0; i < lists.length; i++) {
                    ((Integer[])stat)[i] = Integer.valueOf(listsLength[i]);
                }
            }
        }

        return stat;
    
public java.util.MapgetStats()
get the stats snapshot

return
a Map of stats See also: Constant.java for the keys

        Map stats = super.getStats();

        stats.put(Constants.STAT_MULTILRUCACHE_SEGMENT_SIZE,
                  Integer.valueOf(segmentSize));
        for (int i = 0; i < lists.length; i++) {
            stats.put(Constants.STAT_MULTILRUCACHE_SEGMENT_LIST_LENGTH + "["
                      + i + "]:",
                      Integer.valueOf(listsLength[i]));
        }
        stats.put(Constants.STAT_MULTILRUCACHE_TRIM_COUNT,
                  Integer.valueOf(trimCount));
        return stats;
    
protected voidhandleOverflow()
cache has reached threshold so trim its size. subclasses are expected to provide a robust cache replacement algorithm.

        LruCacheItem l = null;

    
protected voidincrementTrimIndex()

        synchronized (trimIndexLk) {
            trimIndex = (trimIndex + 1) % lists.length;
        }
    
public voidinit(int maxCapacity, java.util.Properties props)
initialize the LRU cache

param
maxCapacity maximum number of entries this cache may hold


                       
            
        super.init(maxCapacity, props);

        segmentSize = DEFAULT_HASHTABLE_SEGMENT_SIZE;

        if (props != null) {
            String prop = props.getProperty("MultiLRUSegmentSize");
            if (prop != null) {
                try {
                    segmentSize = Integer.parseInt(prop);
                } catch (NumberFormatException nfe) {}
            }
        }

        // create the array of LRU lists
        int segments = ((maxBuckets / segmentSize) +
                        (((maxBuckets % segmentSize) != 0) ? 1 : 0));
       	lists = new LruCacheItem[segments][2];
        listsLength = new int[lists.length];
        for (int i = 0; i < lists.length; i++) {
            lists[i][LRU_HEAD] = null;
            lists[i][LRU_TAIL] = null;

            listsLength[i] = 0;
        }
    
protected voiditemAccessed(CacheItem item)
this item is accessed

param
item CacheItem accessed Cache bucket is already synchronized by the caller

        int index = getIndex(item.hashCode());
        int segment = (index/segmentSize);
        LruCacheItem[] list = lists[segment];

        LruCacheItem lc = (LruCacheItem) item;

        // update the LRU list
        synchronized (list) {
            LruCacheItem prev = lc.lPrev;
            LruCacheItem next = lc.lNext;

            if (prev != null) {
                // put the item at the head of LRU list
                lc.lPrev = null;
                lc.lNext = list[LRU_HEAD];
                list[LRU_HEAD].lPrev = lc;
                list[LRU_HEAD] = lc;
    
                // patch up the previous neighbors
                prev.lNext = next;
                if (next != null)
                    next.lPrev = prev;
                else
                    list[LRU_TAIL] = prev;

           }
        }
    
protected CacheItemitemAdded(CacheItem item)
this item is just added to the cache

param
item CacheItem that was created
return
a overflow item; may be null Cache bucket is already synchronized by the caller

        CacheItem overflow = null;
        LruCacheItem lc = (LruCacheItem) item;

        int index = getIndex(item.hashCode());
        int segment = (index/segmentSize);
        LruCacheItem[] list = lists[segment];

        // update the LRU
        synchronized (list) {
            if (list[LRU_HEAD] != null) {
                list[LRU_HEAD].lPrev = lc;
                lc.lNext = list[LRU_HEAD];
            }
            else
                list[LRU_TAIL] = lc;
            list[LRU_HEAD] = lc;

            listsLength[segment]++;

            if (isThresholdReached()) {
                overflow = trimLru(trimIndex);
                // go round robin for the next trim
                incrementTrimIndex();
            }
        }

        return overflow;
    
protected voiditemRefreshed(CacheItem item, int oldSize)
item value has been refreshed

param
item CacheItem that was refreshed
param
oldSize size of the previous value that was refreshed Cache bucket is already synchronized by the caller

        itemAccessed(item);   
    
protected voiditemRemoved(CacheItem item)
item value has been removed from the cache

param
item CacheItem that was just removed Cache bucket is already synchronized by the caller

        LruCacheItem l = (LruCacheItem) item;

        int index = getIndex(item.hashCode());
        int segment = (index/segmentSize);
        LruCacheItem[] list = lists[segment];

        // remove the item from the LRU list
        synchronized (list) {
            // if the item is already trimmed from the LRU list, nothing to do.
            if (l.isTrimmed)
                return;

            LruCacheItem prev = l.lPrev;
            LruCacheItem next = l.lNext;

            // patch up the neighbors and make sure head/tail are correct
            if (prev != null)
                prev.lNext = next;
            else
                list[LRU_HEAD] = next;

            if (next != null)
                next.lPrev = prev;
            else
                list[LRU_TAIL] = prev;

            listsLength[segment]--;
        }
    
protected CacheItemtrimLru(int segment)
remove an lru item from one of the LRU lists

param
the LRU segment index to trim
return
the item that was successfully trimmed

        LruCacheItem[] list = lists[segment];
        LruCacheItem l = null;

        l = list[LRU_TAIL];

        list[LRU_TAIL] = l.lPrev;
        list[LRU_TAIL].lNext = null;
   
        l.lPrev = null;
        listsLength[segment]--;
            
        l.isTrimmed = true;

        trimCount++;

        return l;