History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/misc/SoftCache.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/misc/SoftCache.java+459−0
@@ -0,0 +1,459 @@
1+/*
2+ * Copyright (c) 1998, 2006, 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+
26+package sun.misc;
27+
28+import java.lang.ref.SoftReference;
29+import java.lang.ref.ReferenceQueue;
30+
31+import java.util.Iterator;
32+import java.util.Map;
33+import java.util.AbstractMap;
34+import java.util.HashMap;
35+import java.util.Set;
36+import java.util.AbstractSet;
37+import java.util.NoSuchElementException;
38+
39+
40+/**
41+ * A memory-sensitive implementation of the <code>Map</code> interface.
42+ *
43+ * <p> A <code>SoftCache</code> object uses {@link java.lang.ref.SoftReference
44+ * soft references} to implement a memory-sensitive hash map. If the garbage
45+ * collector determines at a certain point in time that a value object in a
46+ * <code>SoftCache</code> entry is no longer strongly reachable, then it may
47+ * remove that entry in order to release the memory occupied by the value
48+ * object. All <code>SoftCache</code> objects are guaranteed to be completely
49+ * cleared before the virtual machine will throw an
50+ * <code>OutOfMemoryError</code>. Because of this automatic clearing feature,
51+ * the behavior of this class is somewhat different from that of other
52+ * <code>Map</code> implementations.
53+ *
54+ * <p> Both null values and the null key are supported. This class has the
55+ * same performance characteristics as the <code>HashMap</code> class, and has
56+ * the same efficiency parameters of <em>initial capacity</em> and <em>load
57+ * factor</em>.
58+ *
59+ * <p> Like most collection classes, this class is not synchronized. A
60+ * synchronized <code>SoftCache</code> may be constructed using the
61+ * <code>Collections.synchronizedMap</code> method.
62+ *
63+ * <p> In typical usage this class will be subclassed and the <code>fill</code>
64+ * method will be overridden. When the <code>get</code> method is invoked on a
65+ * key for which there is no mapping in the cache, it will in turn invoke the
66+ * <code>fill</code> method on that key in an attempt to construct a
67+ * corresponding value. If the <code>fill</code> method returns such a value
68+ * then the cache will be updated and the new value will be returned. Thus,
69+ * for example, a simple URL-content cache can be constructed as follows:
70+ *
71+ * <pre>
72+ * public class URLCache extends SoftCache {
73+ * protected Object fill(Object key) {
74+ * return ((URL)key).getContent();
75+ * }
76+ * }
77+ * </pre>
78+ *
79+ * <p> The behavior of the <code>SoftCache</code> class depends in part upon
80+ * the actions of the garbage collector, so several familiar (though not
81+ * required) <code>Map</code> invariants do not hold for this class. <p>
82+ * Because entries are removed from a <code>SoftCache</code> in response to
83+ * dynamic advice from the garbage collector, a <code>SoftCache</code> may
84+ * behave as though an unknown thread is silently removing entries. In
85+ * particular, even if you synchronize on a <code>SoftCache</code> instance and
86+ * invoke none of its mutator methods, it is possible for the <code>size</code>
87+ * method to return smaller values over time, for the <code>isEmpty</code>
88+ * method to return <code>false</code> and then <code>true</code>, for the
89+ * <code>containsKey</code> method to return <code>true</code> and later
90+ * <code>false</code> for a given key, for the <code>get</code> method to
91+ * return a value for a given key but later return <code>null</code>, for the
92+ * <code>put</code> method to return <code>null</code> and the
93+ * <code>remove</code> method to return <code>false</code> for a key that
94+ * previously appeared to be in the map, and for successive examinations of the
95+ * key set, the value set, and the entry set to yield successively smaller
96+ * numbers of elements.
97+ *
98+ * @author Mark Reinhold
99+ * @since 1.2
100+ * @see java.util.HashMap
101+ * @see java.lang.ref.SoftReference
102+ */
103+
104+
105+public class SoftCache extends AbstractMap implements Map {
106+
107+ /* The basic idea of this implementation is to maintain an internal HashMap
108+ that maps keys to soft references whose referents are the keys' values;
109+ the various accessor methods dereference these soft references before
110+ returning values. Because we don't have access to the innards of the
111+ HashMap, each soft reference must contain the key that maps to it so
112+ that the processQueue method can remove keys whose values have been
113+ discarded. Thus the HashMap actually maps keys to instances of the
114+ ValueCell class, which is a simple extension of the SoftReference class.
115+ */
116+
117+
118+ static private class ValueCell extends SoftReference {
119+ static private Object INVALID_KEY = new Object();
120+ static private int dropped = 0;
121+ private Object key;
122+
123+ private ValueCell(Object key, Object value, ReferenceQueue queue) {
124+ super(value, queue);
125+ this.key = key;
126+ }
127+
128+ private static ValueCell create(Object key, Object value,
129+ ReferenceQueue queue)
130+ {
131+ if (value == null) return null;
132+ return new ValueCell(key, value, queue);
133+ }
134+
135+ private static Object strip(Object val, boolean drop) {
136+ if (val == null) return null;
137+ ValueCell vc = (ValueCell)val;
138+ Object o = vc.get();
139+ if (drop) vc.drop();
140+ return o;
141+ }
142+
143+ private boolean isValid() {
144+ return (key != INVALID_KEY);
145+ }
146+
147+ private void drop() {
148+ super.clear();
149+ key = INVALID_KEY;
150+ dropped++;
151+ }
152+
153+ }
154+
155+
156+ /* Hash table mapping keys to ValueCells */
157+ private Map hash;
158+
159+ /* Reference queue for cleared ValueCells */
160+ private ReferenceQueue queue = new ReferenceQueue();
161+
162+
163+ /* Process any ValueCells that have been cleared and enqueued by the
164+ garbage collector. This method should be invoked once by each public
165+ mutator in this class. We don't invoke this method in public accessors
166+ because that can lead to surprising ConcurrentModificationExceptions.
167+ */
168+ private void processQueue() {
169+ ValueCell vc;
170+ while ((vc = (ValueCell)queue.poll()) != null) {
171+ if (vc.isValid()) hash.remove(vc.key);
172+ else ValueCell.dropped--;
173+ }
174+ }
175+
176+
177+ /* -- Constructors -- */
178+
179+ /**
180+ * Construct a new, empty <code>SoftCache</code> with the given
181+ * initial capacity and the given load factor.
182+ *
183+ * @param initialCapacity The initial capacity of the cache
184+ *
185+ * @param loadFactor A number between 0.0 and 1.0
186+ *
187+ * @throws IllegalArgumentException If the initial capacity is less than
188+ * or equal to zero, or if the load
189+ * factor is less than zero
190+ */
191+ public SoftCache(int initialCapacity, float loadFactor) {
192+ hash = new HashMap(initialCapacity, loadFactor);
193+ }
194+
195+ /**
196+ * Construct a new, empty <code>SoftCache</code> with the given
197+ * initial capacity and the default load factor.
198+ *
199+ * @param initialCapacity The initial capacity of the cache
200+ *
201+ * @throws IllegalArgumentException If the initial capacity is less than
202+ * or equal to zero
203+ */
204+ public SoftCache(int initialCapacity) {
205+ hash = new HashMap(initialCapacity);
206+ }
207+
208+ /**
209+ * Construct a new, empty <code>SoftCache</code> with the default
210+ * capacity and the default load factor.
211+ */
212+ public SoftCache() {
213+ hash = new HashMap();
214+ }
215+
216+
217+ /* -- Simple queries -- */
218+
219+ /**
220+ * Return the number of key-value mappings in this cache. The time
221+ * required by this operation is linear in the size of the map.
222+ */
223+ public int size() {
224+ return entrySet().size();
225+ }
226+
227+ /**
228+ * Return <code>true</code> if this cache contains no key-value mappings.
229+ */
230+ public boolean isEmpty() {
231+ return entrySet().isEmpty();
232+ }
233+
234+ /**
235+ * Return <code>true</code> if this cache contains a mapping for the
236+ * specified key. If there is no mapping for the key, this method will not
237+ * attempt to construct one by invoking the <code>fill</code> method.
238+ *
239+ * @param key The key whose presence in the cache is to be tested
240+ */
241+ public boolean containsKey(Object key) {
242+ return ValueCell.strip(hash.get(key), false) != null;
243+ }
244+
245+
246+ /* -- Lookup and modification operations -- */
247+
248+ /**
249+ * Create a value object for the given <code>key</code>. This method is
250+ * invoked by the <code>get</code> method when there is no entry for
251+ * <code>key</code>. If this method returns a non-<code>null</code> value,
252+ * then the cache will be updated to map <code>key</code> to that value,
253+ * and that value will be returned by the <code>get</code> method.
254+ *
255+ * <p> The default implementation of this method simply returns
256+ * <code>null</code> for every <code>key</code> value. A subclass may
257+ * override this method to provide more useful behavior.
258+ *
259+ * @param key The key for which a value is to be computed
260+ *
261+ * @return A value for <code>key</code>, or <code>null</code> if one
262+ * could not be computed
263+ * @see #get
264+ */
265+ protected Object fill(Object key) {
266+ return null;
267+ }
268+
269+ /**
270+ * Return the value to which this cache maps the specified
271+ * <code>key</code>. If the cache does not presently contain a value for
272+ * this key, then invoke the <code>fill</code> method in an attempt to
273+ * compute such a value. If that method returns a non-<code>null</code>
274+ * value, then update the cache and return the new value. Otherwise,
275+ * return <code>null</code>.
276+ *
277+ * <p> Note that because this method may update the cache, it is considered
278+ * a mutator and may cause <code>ConcurrentModificationException</code>s to
279+ * be thrown if invoked while an iterator is in use.
280+ *
281+ * @param key The key whose associated value, if any, is to be returned
282+ *
283+ * @see #fill
284+ */
285+ public Object get(Object key) {
286+ processQueue();
287+ Object v = hash.get(key);
288+ if (v == null) {
289+ v = fill(key);
290+ if (v != null) {
291+ hash.put(key, ValueCell.create(key, v, queue));
292+ return v;
293+ }
294+ }
295+ return ValueCell.strip(v, false);
296+ }
297+
298+ /**
299+ * Update this cache so that the given <code>key</code> maps to the given
300+ * <code>value</code>. If the cache previously contained a mapping for
301+ * <code>key</code> then that mapping is replaced and the old value is
302+ * returned.
303+ *
304+ * @param key The key that is to be mapped to the given
305+ * <code>value</code>
306+ * @param value The value to which the given <code>key</code> is to be
307+ * mapped
308+ *
309+ * @return The previous value to which this key was mapped, or
310+ * <code>null</code> if if there was no mapping for the key
311+ */
312+ public Object put(Object key, Object value) {
313+ processQueue();
314+ ValueCell vc = ValueCell.create(key, value, queue);
315+ return ValueCell.strip(hash.put(key, vc), true);
316+ }
317+
318+ /**
319+ * Remove the mapping for the given <code>key</code> from this cache, if
320+ * present.
321+ *
322+ * @param key The key whose mapping is to be removed
323+ *
324+ * @return The value to which this key was mapped, or <code>null</code> if
325+ * there was no mapping for the key
326+ */
327+ public Object remove(Object key) {
328+ processQueue();
329+ return ValueCell.strip(hash.remove(key), true);
330+ }
331+
332+ /**
333+ * Remove all mappings from this cache.
334+ */
335+ public void clear() {
336+ processQueue();
337+ hash.clear();
338+ }
339+
340+
341+ /* -- Views -- */
342+
343+ private static boolean valEquals(Object o1, Object o2) {
344+ return (o1 == null) ? (o2 == null) : o1.equals(o2);
345+ }
346+
347+
348+ /* Internal class for entries.
349+ Because it uses SoftCache.this.queue, this class cannot be static.
350+ */
351+ private class Entry implements Map.Entry {
352+ private Map.Entry ent;
353+ private Object value; /* Strong reference to value, to prevent the GC
354+ from flushing the value while this Entry
355+ exists */
356+
357+ Entry(Map.Entry ent, Object value) {
358+ this.ent = ent;
359+ this.value = value;
360+ }
361+
362+ public Object getKey() {
363+ return ent.getKey();
364+ }
365+
366+ public Object getValue() {
367+ return value;
368+ }
369+
370+ public Object setValue(Object value) {
371+ return ent.setValue(ValueCell.create(ent.getKey(), value, queue));
372+ }
373+
374+ public boolean equals(Object o) {
375+ if (! (o instanceof Map.Entry)) return false;
376+ Map.Entry e = (Map.Entry)o;
377+ return (valEquals(ent.getKey(), e.getKey())
378+ && valEquals(value, e.getValue()));
379+ }
380+
381+ public int hashCode() {
382+ Object k;
383+ return ((((k = getKey()) == null) ? 0 : k.hashCode())
384+ ^ ((value == null) ? 0 : value.hashCode()));
385+ }
386+
387+ }
388+
389+
390+ /* Internal class for entry sets */
391+ private class EntrySet extends AbstractSet {
392+ Set hashEntries = hash.entrySet();
393+
394+ public Iterator iterator() {
395+
396+ return new Iterator() {
397+ Iterator hashIterator = hashEntries.iterator();
398+ Entry next = null;
399+
400+ public boolean hasNext() {
401+ while (hashIterator.hasNext()) {
402+ Map.Entry ent = (Map.Entry)hashIterator.next();
403+ ValueCell vc = (ValueCell)ent.getValue();
404+ Object v = null;
405+ if ((vc != null) && ((v = vc.get()) == null)) {
406+ /* Value has been flushed by GC */
407+ continue;
408+ }
409+ next = new Entry(ent, v);
410+ return true;
411+ }
412+ return false;
413+ }
414+
415+ public Object next() {
416+ if ((next == null) && !hasNext())
417+ throw new NoSuchElementException();
418+ Entry e = next;
419+ next = null;
420+ return e;
421+ }
422+
423+ public void remove() {
424+ hashIterator.remove();
425+ }
426+
427+ };
428+ }
429+
430+ public boolean isEmpty() {
431+ return !(iterator().hasNext());
432+ }
433+
434+ public int size() {
435+ int j = 0;
436+ for (Iterator i = iterator(); i.hasNext(); i.next()) j++;
437+ return j;
438+ }
439+
440+ public boolean remove(Object o) {
441+ processQueue();
442+ if (o instanceof Entry) return hashEntries.remove(((Entry)o).ent);
443+ else return false;
444+ }
445+
446+ }
447+
448+
449+ private Set entrySet = null;
450+
451+ /**
452+ * Return a <code>Set</code> view of the mappings in this cache.
453+ */
454+ public Set entrySet() {
455+ if (entrySet == null) entrySet = new EntrySet();
456+ return entrySet;
457+ }
458+
459+}