first cleanup of cache code - implemented a new CacheMap which uses

SoftReferences
This commit is contained in:
Eric J. Bowersox 2001-11-14 06:00:35 +00:00
parent 9558f0722b
commit 7e0f7b441f
5 changed files with 168 additions and 62 deletions

View File

@ -15,8 +15,9 @@
* *
* Contributor(s): * Contributor(s):
*/ */
package com.silverwrist.util.cachemap; package com.silverwrist.util.cache;
import java.lang.ref.*;
import java.util.*; import java.util.*;
public class CacheMap implements Map public class CacheMap implements Map
@ -92,6 +93,7 @@ public class CacheMap implements Map
private CacheMapStrategy strategy; // strategy routine to use to purge entries private CacheMapStrategy strategy; // strategy routine to use to purge entries
private HashMap base_map; // maps keys to CacheMapEntry values private HashMap base_map; // maps keys to CacheMapEntry values
private ArrayList element_list; // the actual elements private ArrayList element_list; // the actual elements
private ReferenceQueue rq; // holds references that the garbage collector has cleared
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Constructors * Constructors
@ -112,6 +114,7 @@ public class CacheMap implements Map
this.strategy = strategy; this.strategy = strategy;
this.base_map = new HashMap(10); this.base_map = new HashMap(10);
this.element_list = new ArrayList(10); this.element_list = new ArrayList(10);
this.rq = new ReferenceQueue();
} // end constructor } // end constructor
@ -127,6 +130,66 @@ public class CacheMap implements Map
} // end constructor } // end constructor
/*--------------------------------------------------------------------------------
* Internal operations
*--------------------------------------------------------------------------------
*/
private void doSweep()
{
Reference r = rq.poll();
ArrayList ditch = new ArrayList();
Iterator it;
while (r!=null)
{ // look for the dead reference in the element list
it = element_list.iterator();
while (it.hasNext())
{ // check each cache map entry in return
CacheMapEntry ntry = (CacheMapEntry)(it.next());
if (ntry.matchReference(r))
{ // remove the offending entry and save it in the temporary list
it.remove();
ditch.add(ntry);
break;
} // end if
} // end while
r = rq.poll(); // get next dead reference
} // end while
if (ditch.isEmpty())
return; // nothing to prune
it = ditch.iterator();
while (it.hasNext())
{ // clear all entries from the base hashmap as well
CacheMapEntry ntry = (CacheMapEntry)(it.next());
base_map.remove(ntry.getKey());
ntry.discard();
} // end while
} // end doSweep
public synchronized void doShrink(int num_remove)
{
// Sort the element list to figure out which elements to remove.
Collections.sort(element_list,new CacheOrdering(strategy));
// The elements we want to remove are at the end of the array, so start from there.
for (int i=0; i<num_remove; i++)
{ // remove the "removed" entries from the hash map
CacheMapEntry cme = (CacheMapEntry)(element_list.remove(element_list.size() - 1));
base_map.remove(cme.getKey());
} // end for
} // end doShrink
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Implementations from interface Map * Implementations from interface Map
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -134,24 +197,28 @@ public class CacheMap implements Map
public int size() public int size()
{ {
doSweep();
return base_map.size(); return base_map.size();
} // end size } // end size
public boolean isEmpty() public boolean isEmpty()
{ {
doSweep();
return base_map.isEmpty(); return base_map.isEmpty();
} // end isEmpty } // end isEmpty
public boolean containsKey(Object key) public boolean containsKey(Object key)
{ {
doSweep();
return base_map.containsKey(key); return base_map.containsKey(key);
} // end containsKey } // end containsKey
public boolean containsValue(Object value) public boolean containsValue(Object value)
{ {
doSweep();
Iterator it = element_list.iterator(); Iterator it = element_list.iterator();
while (it.hasNext()) while (it.hasNext())
{ // look at all the CacheMapEntry values we have { // look at all the CacheMapEntry values we have
@ -178,6 +245,7 @@ public class CacheMap implements Map
public Object get(Object key) public Object get(Object key)
{ {
doSweep();
CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); CacheMapEntry cme = (CacheMapEntry)(base_map.get(key));
if (cme==null) if (cme==null)
return null; return null;
@ -188,16 +256,17 @@ public class CacheMap implements Map
public Object put(Object key, Object value) public Object put(Object key, Object value)
{ {
doSweep();
Object rc = null; Object rc = null;
CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); CacheMapEntry cme = (CacheMapEntry)(base_map.get(key));
if (cme==null) if (cme==null)
{ // create a new CacheMapEntry for this key { // create a new CacheMapEntry for this key
cme = new CacheMapEntry(key,value); cme = new CacheMapEntry(key,value,rq);
synchronized (this) synchronized (this)
{ // insert it into the basic object { // insert it into the basic object
if (base_map.size()==capacity) if (base_map.size()==capacity)
shrink(); doShrink((element_list.size() * shrink_percentage) / 100);
element_list.add(cme); element_list.add(cme);
base_map.put(cme.getKey(),cme); base_map.put(cme.getKey(),cme);
@ -207,7 +276,7 @@ public class CacheMap implements Map
else else
{ // we have an old value - replace it and touch the entry { // we have an old value - replace it and touch the entry
cme.touch(); cme.touch();
rc = cme.setValue(value); rc = cme.setValue(value,rq);
} // end else } // end else
@ -217,6 +286,7 @@ public class CacheMap implements Map
public Object remove(Object key) public Object remove(Object key)
{ {
doSweep();
Object rc = null; Object rc = null;
CacheMapEntry cme = (CacheMapEntry)(base_map.get(key)); CacheMapEntry cme = (CacheMapEntry)(base_map.get(key));
if (cme!=null) if (cme!=null)
@ -230,6 +300,8 @@ public class CacheMap implements Map
} // end synchronized block } // end synchronized block
cme.discard(); // zap the reference
} // end if } // end if
return rc; return rc;
@ -238,10 +310,12 @@ public class CacheMap implements Map
public void putAll(Map map) public void putAll(Map map)
{ {
doSweep();
synchronized (this) synchronized (this)
{ // make sure we have enough space in the CacheMap for all the new elements! { // make sure we have enough space in the CacheMap for all the new elements!
while ((map.size() + base_map.size()) > capacity) int nover = (map.size() + base_map.size()) - capacity;
shrink(); if (nover>0)
doShrink(nover);
} // end synchronized block } // end synchronized block
@ -258,6 +332,14 @@ public class CacheMap implements Map
public synchronized void clear() public synchronized void clear()
{ {
base_map.clear(); base_map.clear();
Iterator it = element_list.iterator();
while (it.hasNext())
{ // discard all entries we have
CacheMapEntry cme = (CacheMapEntry)(it.next());
cme.discard();
} // end while
element_list.clear(); element_list.clear();
} // end clear } // end clear
@ -270,13 +352,13 @@ public class CacheMap implements Map
public Collection values() public Collection values()
{ {
return null; // not implemented throw new UnsupportedOperationException("CacheMap.values() is not implemented");
} // end values } // end values
public Set entrySet() public Set entrySet()
{ {
return null; // not implemented throw new UnsupportedOperationException("CacheMap.entrySet() is not implemented");
} // end entrySet } // end entrySet
@ -284,6 +366,7 @@ public class CacheMap implements Map
{ {
if ((o==null) || !(o instanceof Map)) if ((o==null) || !(o instanceof Map))
return false; // not a map return false; // not a map
doSweep();
Map other = (Map)o; Map other = (Map)o;
if (other.size()!=base_map.size()) if (other.size()!=base_map.size())
return false; // size does matter! return false; // size does matter!
@ -314,6 +397,7 @@ public class CacheMap implements Map
public int hashCode() public int hashCode()
{ {
doSweep();
int rc = 0; int rc = 0;
Iterator it = base_map.values().iterator(); Iterator it = base_map.values().iterator();
while (it.hasNext()) while (it.hasNext())
@ -384,17 +468,16 @@ public class CacheMap implements Map
// Figure out how many elements to remove. // Figure out how many elements to remove.
int num_remove = (element_list.size() * shrink_percentage) / 100; int num_remove = (element_list.size() * shrink_percentage) / 100;
// Sort the element list to figure out which elements to remove. // Try a sweep first.
Collections.sort(element_list,new CacheOrdering(strategy)); int n1 = base_map.size();
doSweep();
// The elements we want to remove are at the end of the array, so start from there. n1 -= base_map.size();
for (int i=0; i<num_remove; i++) if (n1<num_remove)
{ // remove the "removed" entries from the hash map doShrink(num_remove - n1);
CacheMapEntry cme = (CacheMapEntry)(element_list.remove(element_list.size() - 1));
base_map.remove(cme.getKey());
} // end for
} // end shrink } // end shrink
} // end class CacheMap } // end class CacheMap

View File

@ -15,31 +15,40 @@
* *
* Contributor(s): * Contributor(s):
*/ */
package com.silverwrist.util.cachemap; package com.silverwrist.util.cache;
import java.lang.ref.*;
import java.util.*; import java.util.*;
class CacheMapEntry implements Map.Entry final class CacheMapEntry implements Map.Entry
{ {
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Attributes * Attributes
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
private Object key; private Object key; // the key for this entry
private Object value; private SoftReference value; // reference to (discardable) value
private int hits = 0; private int hits = 0; // number of hits on this entry
private long timestamp; private long timestamp; // timestamp of this entry
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Constructor * Constructors
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
CacheMapEntry(Object key, Object value) CacheMapEntry(Object key, Object value)
{ {
this.key = key; this.key = key;
this.value = value; this.value = new SoftReference(value);
this.timestamp = System.currentTimeMillis();
} // end constructor
CacheMapEntry(Object key, Object value, ReferenceQueue q)
{
this.key = key;
this.value = new SoftReference(value,q);
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
} // end constructor } // end constructor
@ -52,12 +61,13 @@ class CacheMapEntry implements Map.Entry
protected void finalize() protected void finalize()
{ {
key = null; key = null;
value.clear();
value = null; value = null;
} // end finalize } // end finalize
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Implementations from interface MapEntry * Implementations from interface Map.Entry
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
@ -69,14 +79,14 @@ class CacheMapEntry implements Map.Entry
public final Object getValue() public final Object getValue()
{ {
return value; return value.get();
} // end getValue } // end getValue
public final Object setValue(Object o) public final Object setValue(Object o)
{ {
Object rc = value; Object rc = value.get();
value = o; value = new SoftReference(o);
return rc; return rc;
} // end setValue } // end setValue
@ -88,36 +98,17 @@ class CacheMapEntry implements Map.Entry
return false; return false;
Map.Entry other = (Map.Entry)o; Map.Entry other = (Map.Entry)o;
// compare the keys // compare the keys only (the values may change)
if (key==null) if (key==null)
{ // the other key must be null return (other.getKey()==null);
if (other.getKey()!=null)
return false;
} // end if
else else
{ // the other key must be equal to us return ((other.getKey()!=null) && key.equals(other.getKey()));
if ((other.getKey()==null) || (!(key.equals(other.getKey()))))
return false;
} // end else
// compare the values
if (value==null)
return (other.getValue()==null);
else
return ((other.getValue()!=null) && value.equals(other.getValue()));
} // end equals } // end equals
public final int hashCode() public final int hashCode()
{ {
int rc = 0; return ((key==null) ? 0 : key.hashCode());
if (key!=null)
rc ^= key.hashCode();
if (value!=null)
rc ^= value.hashCode();
return rc;
} // end hashCode } // end hashCode
@ -126,6 +117,38 @@ class CacheMapEntry implements Map.Entry
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
final Object setValue(Object o, ReferenceQueue q)
{
Object rc = value.get();
value.clear();
value = new SoftReference(o,q);
return rc;
} // end setValue
final boolean isCleared()
{
return (value.get()==null);
} // end isCleared
final boolean matchReference(Reference r)
{
if (r instanceof SoftReference)
return (value==(SoftReference)r);
else
return false;
} // end matchReference
final void discard()
{
key = null;
value.clear();
value = null;
} // end discard
final int getHits() final int getHits()
{ {
return hits; return hits;

View File

@ -15,7 +15,7 @@
* *
* Contributor(s): * Contributor(s):
*/ */
package com.silverwrist.util.cachemap; package com.silverwrist.util.cache;
public interface CacheMapStrategy public interface CacheMapStrategy
{ {

View File

@ -19,7 +19,7 @@ package com.silverwrist.venice.core.impl;
import java.sql.*; import java.sql.*;
import java.util.Random; import java.util.Random;
import com.silverwrist.util.cachemap.*; import com.silverwrist.util.cache.CacheMap;
import com.silverwrist.venice.core.*; import com.silverwrist.venice.core.*;
import com.silverwrist.venice.db.*; import com.silverwrist.venice.db.*;
@ -38,12 +38,12 @@ class AdvertisementImpl implements Advertisement
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
private DataPool datapool; private DataPool datapool; // data pool reference
private int adid; private int adid; // ad ID
private String imagepath; private String imagepath; // image path
private short style; private short style; // ad style
private String caption; private String caption; // ad caption
private String linkurl; private String linkurl; // ad link URL
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Constructor * Constructor

View File

@ -19,7 +19,7 @@ package com.silverwrist.venice.servlets.format;
import java.io.*; import java.io.*;
import com.silverwrist.util.IOUtil; import com.silverwrist.util.IOUtil;
import com.silverwrist.util.cachemap.CacheMap; import com.silverwrist.util.cache.CacheMap;
public class StaticRender implements ContentRender public class StaticRender implements ContentRender
{ {