001 /*--------------------------------------------------------------------------+
002 $Id: SoftRefCacheBase.java 26268 2010-02-18 10:44:30Z juergens $
003 | |
004 | Copyright 2005-2010 Technische Universitaet Muenchen |
005 | |
006 | Licensed under the Apache License, Version 2.0 (the "License"); |
007 | you may not use this file except in compliance with the License. |
008 | You may obtain a copy of the License at |
009 | |
010 | http://www.apache.org/licenses/LICENSE-2.0 |
011 | |
012 | Unless required by applicable law or agreed to in writing, software |
013 | distributed under the License is distributed on an "AS IS" BASIS, |
014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015 | See the License for the specific language governing permissions and |
016 | limitations under the License. |
017 +--------------------------------------------------------------------------*/
018 package edu.tum.cs.commons.cache;
019
020 import java.lang.ref.SoftReference;
021 import java.util.HashMap;
022
023 import edu.tum.cs.commons.error.NeverThrownRuntimeException;
024
025 /**
026 * A base class for dynamic caches based on {@link SoftReference}s. If
027 * identifiers itself are suitable hash keys, use class
028 * {@link edu.tum.cs.commons.cache.SoftRefStraightCacheBase}.
029 * <p>
030 * The implementation is memory-sensitive, i.e. it dynamically removes entries
031 * from the cache if the virtual machine is short on memory. However, this
032 * dynamic is completely transparent to the user.
033 * <p>
034 * <b>Note:</b> To make this cache efficient the virtual machine must work in
035 * server mode.
036 *
037 * @author Florian Deissenboeck
038 * @author Tilman Seifert
039 * @author $Author: juergens $
040 *
041 * @version $Rev: 26268 $
042 * @levd.rating GREEN Hash: A74130243CAE0E00D2C6A51DDFEA68A0
043 *
044 * @param <I>
045 * the index type of the cache
046 * @param <H>
047 * the hash map key type
048 * @param <E>
049 * the type stored in the cache
050 * @param <X>
051 * the type of exception thrown by the {@link #obtainItem(Object)}
052 * method. Use the {@link NeverThrownRuntimeException} if no
053 * exception will be thrown.
054 */
055 public abstract class SoftRefCacheBase<I, H, E, X extends Exception> extends
056 CacheBase<I, H, E, X> {
057
058 /** Number of times the cache was accessed (for debugging purposes only) */
059 private static int accessCounter = 0;
060
061 /** Number of cache hits (for debugging purposes only) */
062 private static int hitCounter = 0;
063
064 /**
065 * Number of cache misses that were caused by accesses to items that were
066 * never cached (for debugging purposes only)
067 */
068 private static int missBecauseNotInCacheCounter = 0;
069
070 /**
071 * Number of cache misses that were caused by accesses to items that were
072 * removed from cache by garbage collection (for debugging purposes only)
073 */
074 private static int missBecauseRemovedFromCacheCounter = 0;
075
076 /** The actual cache. */
077 protected final HashMap<H, SoftReference<E>> cache = new HashMap<H, SoftReference<E>>();
078
079 /** {@inheritDoc} */
080 @Override
081 public E getItem(I identifier) throws X {
082 accessCounter++;
083
084 // check if item is cached
085 SoftReference<E> ref = cache.get(getHashKey(identifier));
086 if (ref == null) {
087 return obtainUncachedItem(identifier);
088 }
089
090 // item was cached
091 E item = ref.get();
092
093 // check if item was garbage collected
094 if (item == null) {
095 return obtainGarbageCollectedItem(identifier);
096 }
097
098 // return item
099 hitCounter++;
100 return item;
101 }
102
103 /** Obtain (and cache) and item which was killed by the GC. */
104 private E obtainGarbageCollectedItem(I identifier) throws X {
105 missBecauseRemovedFromCacheCounter++;
106 E item = obtainItem(identifier);
107
108 // if null remove from cache, otherwise re-cache
109 if (item == null) {
110 cache.remove(getHashKey(identifier));
111 } else {
112 cache.put(getHashKey(identifier), new SoftReference<E>(item));
113 }
114 return item;
115 }
116
117 /** Obtain (and cache) an uncached item. */
118 private E obtainUncachedItem(I identifier) throws X {
119 E item = obtainItem(identifier);
120
121 // only cache if not null
122 if (item != null) {
123 cache.put(getHashKey(identifier), new SoftReference<E>(item));
124 missBecauseNotInCacheCounter++;
125 }
126 return item;
127 }
128
129 /**
130 * Returns a statistics string with information about cache hits and misses
131 * for debugging purposes. As this is implemented in a static way results
132 * are only valid if only a single instance of the specific cache class
133 * exists.
134 */
135 public static String getStatistics() {
136 StringBuilder stats = new StringBuilder();
137 stats.append("#access: " + accessCounter);
138 stats.append(" #hits: " + hitCounter);
139 stats.append(" #miss (not cached): " + missBecauseNotInCacheCounter);
140 stats.append(" #miss (removed): " + missBecauseRemovedFromCacheCounter);
141 return stats.toString();
142 }
143 }