Guava Cache Implementation and source code analysis

Source: Internet
Author: User



Directory


    • Guava Cache
      • I. Overview
        • 1. Memory Cache
        • 2. Core Data structure
      • Second, the concrete realization
        • 0, a list of small mountains
        • 1. Cachebuilder Builder
        • 2, LocalCache
Guava cache One, overview 1, Memory caching


Can be regarded as a jdk7 concurrenthashmap, the core function get,put
But there are a few more features than the general map, such as:


    • ?? Over-limit failure (according to different dimensions failure, after reading n seconds, after write n seconds, max size, maximum weight)
    • Auto Refresh
    • Supports soft and weak references
    • Listener Delete
2. Core Data structure


Similar to Jdk7 's HashMap
There are n segment, each segment under a Hashtable, and each Hashtable is a linked list
Guava lock is a relatively heavy operation, the lock is the entire segment (segment inherited is Reetrentlock, surprise)


Second, the specific implementation of 0, a list of mountains small


The main classes:



cachebuilder setting parameters, building Loadingcache



LocalCache is the core implementation, although builder builds Localloadingcache (with refresh) and Localmanualcache (without the refresh function), but actually those two are just a shell


1. Cachebuilder Builder


Feed:
Record the required parameters


Public final class CacheBuilder<k, v=""> {</k,>

Public <k1 extends="" k,="" v1="" v=""> LoadingCache<k1, v1=""> build(</k1,></k1>
CacheLoader <!--?</span--> Super K1, V1> loader) {// loader is used to automatically refresh
CheckWeightWithWeigher ();
Return new LocalCache. LocalLoadingCache < > (this, loader);
}

Public <k1 extends="" k,="" v1="" v=""> Cache<k1, v1=""> build()</k1,></k1>
CheckWeightWithWeigher ();
CheckNonLoadingCache ();
Return new LocalCache. LocalManualCache < > (this);
}

Int initialCapacity = UNSET_INT; // initial map size
Int concurrencyLevel = UNSET_INT; / / concurrency
Long maximumSize = UNSET_INT;
Long maximumWeight = UNSET_INT;
Weigher <!--?</span--> Super, K? Super V > weigher;
Strength keyStrength; // key strong, weak, soft, default is strong
Strength valueStrength; // value strong, weak, soft, default is strong
Long expireAfterWriteNanos = UNSET_INT; / / write expired
Long expireAfterAccessNanos = UNSET_INT; //
Long refreshNanos = UNSET_INT; //
Equivalence < Object > keyEquivalence; // equals when strongly quoted, otherwise ==
Equivalence < Object > valueEquivalence; // equals when strongly quoted, otherwise ==
RemovalListener <!--?</span--> Super, K? Super V > removalListener; // listen for deletion
Ticker Ticker. // time clock, used to obtain the current time
: Supplier <!--?</span--> Extends StatsCounter> statsCounterSupplier = NULL_STATS_COUNTER; // counter to record data such as get or miss
}
2, LocalCache1) initialization


Feed:
A) Assign value
b) Initialize segment[] Array


LocalCache (
CacheBuilder <!--?</span--> Super, K? Super V> builder, @nullable CacheLoader<!--?</span--> Super K, V> loader) {

// a) assign the parameters to the builder

// b) construct the Segment[] array, the principle can refer to jdk7 point concurrentHashMap
Int segmentShift = 0;
Int segmentCount = 1; // set to just as large as concurrencyLevel to a power of 2
While (segmentCount < concurrencyLevel && (! EvictsBySize () || segmentCount * 20 <= maxWeight)) {
+ + segmentShift;
SegmentCount < < = 1;
}
This. SegmentShift = 32 - segmentShift;
SegmentMask = segmentCount - 1;

This. Segments = newSegmentArray (segmentCount);

Int segmentCapacity = initialCapacity/segmentCount; // capacity of each Segment
Int segmentSize = 1; // that's just like a big power of two
While (segmentSize < segmentCapacity) {
SegmentSize < < = 1;
}

If (evictsBySize ()) {
// Ensure sum of segment Max weights = overall Max weights
Long maxSegmentWeight = maxWeight/segmentCount + 1;
Long remainder = maxWeight % segmentCount;
For (int I = 0; I < enclosing segments. Length; + + I) {
If (I == remainder) {
MaxSegmentWeight -;
}
This. Segments [I] =
CreateSegment (segmentSize maxSegmentWeight, builder. GetStatsCounterSupplier (). The get ());
}
} else {
For (int I = 0; I < enclosing segments. Length; + + I) {
This. Segments [I] =
CreateSegment (segmentSize UNSET_INT, builder. GetStatsCounterSupplier (). The get ()); // into the Segment array
}
}
}


Segment (
LocalCache < K, V > map,
Int initialCapacity,
Long maxSegmentWeight,
StatsCounter StatsCounter) {
This. The map = map;
Enclosing maxSegmentWeight = maxSegmentWeight;
Enclosing statsCounter = checkNotNull (statsCounter);
InitTable (newEntryArray (initialCapacity));
// when the key is a weak or soft reference, initialize the keyReferenceQueue; When its superclass property determines its gc, the element being gc is placed in the queue
KeyReferenceQueue = map. UsesKeyReferences ()? New ReferenceQueue < K > () : null;

ValueReferenceQueue = map. UsesValueReferences ()? New ReferenceQueue < V > () : null;

RecencyQueue =
Map. UsesAccessQueue ()
? New ConcurrentLinkedQueue < ReferenceEntry < K, V > > ()
: LocalCache. < ReferenceEntry < K, V > > discardingQueue ();

WriteQueue =
Map. UsesWriteQueue ()
? New WriteQueue > < K, V ()
: LocalCache. < ReferenceEntry < K, V > > discardingQueue ();

AccessQueue =
Map. UsesAccessQueue ()
? New AccessQueue > < K, V ()
: LocalCache. < ReferenceEntry < K, V > > discardingQueue ();
}
2) put


Feed
A) Locate the segment where key is located and call the Segment.put method
b) lock segment, clean
i) if key exists
II) If key does not exist
c) Clean up


 
class LocalCache {
Public V put(K key, V value) {
CheckNotNull (key);
CheckNotNull (value);
Int hash = hash (key); / / calculate the hash
Return segmentFor(hash). Put (key, hash, value, false); // find the Segment to which the hash is assigned and put it in
}
}

// instead, let's look at the put method of Segment
Class Segment<k,v> implements ReentrantLock {</k,v>
V put(K key, int hash, V value, Boolean onlyIfAbsent) {
The lock (); // lock a segment
Try {
Long = map. The ticker. Read (); // get the current time
PreWriteCleanup (now); // clear soft/weak references in 2.4

Int newCount = this.count + 1;
If (newCount > this.threshold) {// expand if needed
Expand ();
NewCount = this.count + 1;
}

AtomicReferenceArray<referenceentry<k, v="">> table = this.table;</referenceentry<k,>
Int index = hash & (table. Length () -1);
ReferenceEntry<k, v=""> first = table. Get (index);</k,>

// Look for an existing entry.
// depending on the situation, 1) count++ number of updates 2) enqueueNotification notification 3) setValue update 4)evictEntries
For (ReferenceEntry<k, v=""> e = first;</k,> E! = null; E = um participant etNext ()) {
K entryKey = um participant etKey ();
// if the key already exists
If (um participant etHash () = = hash
&& entryKey! = null
&& map. KeyEquivalence. Equivalent (key, entryKey)) {
// We found an existing entry.

ValueReference<k, v=""> ValueReference = etValueReference();</k,>
V entryValue = valueReference. The get ();

If (entryValue == null) {
+ + modCount;
If (valueReference isActive ()) {
EnqueueNotification (
Key, hash, entryValue, valueReference. GetWeight (), removalcause.collected);
SetValue (e, key, value, now);
NewCount = this. Count; / / count remains unchanged
} else {
SetValue (e, key, value, now);
NewCount = this.count + 1;
}
This. Count = newCount; / / write - volatile
EvictEntries (e);
Return null;
} else if (onlyIfAbsent) {
RecordLockedRead (e, now);
Return entryValue;
} else {
// clobber existing entry, count remains unchanged
+ + modCount;
EnqueueNotification (
Key, hash, entryValue, valueReference. GetWeight (), removalcause.actor);
SetValue (e, key, value, now);
EvictEntries (e);
Return entryValue;
}
}
}

// if the key does not exist, create a new entry.
+ + modCount;
ReferenceEntry<k, v=""> newEntry = newEntry(key, hash, first);</k,>
SetValue (newEntry, key, value, now);
The table set (index, newEntry);
NewCount = this.count + 1;
This. Count = newCount; / / write - volatile
EvictEntries (newEntry);
Return null;
} the finally {
Unlock ();
PostWriteCleanup ();
}
}

@ GuardedBy (" this ")
ReferenceEntry<k, v=""> newEntry(K key, int hash, @nullable ReferenceEntry<k, v=""> next) {</k,></k,>
Return map. EntryFactory. NewEntry (this, checkNotNull (key), hash, next);
}
}


Create entry with Map.entryfactory. Where the initialization of entryfactory is obtained as follows


EntryFactory entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries());


Entryfactory is an enumeration class, the enumeration class can also be used so that the knowledge has risen!


Enum EntryFactory {
STRONG {
@ Override
<k, v=""> ReferenceEntry<k, v=""> newEntry(</k,></k,>
Segment<k, v=""> Segment, K key, int hash, @nullable ReferenceEntry<k, v=""> next) {</k,></k,>
Return new StrongEntry<>(key, hash, next);
}
},... // omit part
WEAK {// the essence of the soft/WEAK reference!!
@ Override
<k, v=""> ReferenceEntry<k, v=""> newEntry(</k,></k,>
Segment<k, v=""> Segment, K key, int hash, @nullable ReferenceEntry<k, v=""> next) {// so!!</k,></k,> This is it!! I put this queue in, and I finally found it
Return new WeakEntry < > (segment. KeyReferenceQueue, key, hash, next);
}
}};

// Masks used to compute indices in the following table.

Static final int ACCESS_MASK = 1;
Static final int WRITE_MASK = 2;
Static final int WEAK_MASK = 4;

/** Look up table for factories. */
Static final EntryFactory[] factories = {
STRONG,
STRONG_ACCESS,
STRONG_WRITE,
STRONG_ACCESS_WRITE,
WEAK,
WEAK_ACCESS,
WEAK_WRITE,
WEAK_ACCESS_WRITE,
};

The static EntryFactory getFactory (
Strength keyStrength, Boolean usesAccessQueue, Boolean usesWriteQueue) {
Int flags =
((keyStrength = = Strength, WEAK)? WEAK_MASK: 0)
| (usesAccessQueue? ACCESS_MASK: 0)
| (usesWriteQueue? WRITE_MASK: 0);
Return factories (flags);
}

// abstract method: create an entry
Abstract <k, v=""> ReferenceEntry<k, v=""> newEntry(</k,></k,>
Segment<k, v=""> Segment, K key, int hash, @nullable ReferenceEntry<k, v=""> next);</k,></k,>
}

Static class WeakEntry<k, v=""> extends WeakReference<k> implements ReferenceEntry<k, v=""> {</k,></k></k,>
WeakEntry(ReferenceQueue<k> queue, K key, int hash, @nullable ReferenceEntry<k, v=""> next) {</k,></k>
Super (key, queue); // this is the method of Reference, so put it into the queue, Java WeakReference class is its own function
Enclosing the hash = hash;
This. Next = next;
}
}
3) Get


Feed
A) Locate the segment where key is located and call the Segment.get method
b) Get referenceentry, if present, check if value is out of date, return result
c) Clean up


The class LocalCache {
Nullable V get(@nullable Object key) {
If (key == null) {
Return null;
}
Int hash = hash (key);
Return segmentFor (hash). Get (key, hash);
}
}

The class Segment {
V get(Object key, int hash) {
Try {
If (count! = 0) {// read-volatile
Long = map. The ticker. Read ();
ReferenceEntry<k, v=""> e = getLiveEntry(key, hash, now);</k,> // null is returned if not found or expired
If (e == null) {
Return null;
}

V value = um participant etValueReference (). The get ();
If (the value! = null) {
RecordRead (e, now);
Return scheduleRefresh(e, greetkey (), hash, value, now, map.defaultloader); // refresh if there is a loader and during the refresh period, otherwise skip
}
TryDrainReferenceQueues (); // this spooky operation is uncomfortable
}
Return null;
} the finally {
PostReadCleanup ();
}
}
}
4) Clear soft/weak references


Clean check before and after each put, get


@ GuardedBy (" this ")
Void preWriteCleanup(long now) {//
RunLockedCleanup (now);
}
Void runLockedCleanup(long now) {// lock + execute method
If (tryLock ()) {
Try {
DrainReferenceQueues ();
ExpireEntries (now); / / calls drainRecencyQueue
ReadCount. Set (0);
} the finally {
Unlock ();
}
}
}
@ GuardedBy (" this ")
Void drainReferenceQueues() {// empty soft/weak references key and value
If (map. UsesKeyReferences ()) {
DrainKeyReferenceQueue ();
}
If (map. UsesValueReferences ()) {
DrainValueReferenceQueue ();
}
}
@ GuardedBy (" this ")
Void drainKeyReferenceQueue() {// empty soft/weak reference key
The Reference <!--?</span--> Extends K > ref;
Int I = 0;
While ((ref = keyReferenceQueue. Poll ())! = null) {
@ SuppressWarnings (" unchecked ")
ReferenceEntry<k, v=""> entry = (ReferenceEntry<k, v="">) ref;</k,></k,>
Map. ReclaimKey (entry);
If (++ I == DRAIN_MAX) {
Break;
}
}
}
}

// one of the things I haven't figured out is when the keyReferenceQueue was inserted into the element??
// need to look at the operation when creating the entry!! You'll find out


Public class ReentrantLock implements Lock, java.io.Serializable {

Private Sync Sync;

Public Boolean tryLock () {
Return the sync. NonfairTryAcquire (1);
}

The abstract static class Sync extends AbstractQueuedSynchronizer {
Private static final long serialVersionUID = -5179523762034025860l;

The abstract void the lock ();

Final Boolean nonfairTryAcquire (int acquires) {
Final Thread current = thread.currentthread (); // gets the current thread
Int c = getState ();
If (c == 0) {// no thread to hold, that is, no lock state
If (compareAndSetState (0, acquires)) {/ / set the thread
SetExclusiveOwnerThread (current);
Return true;
}
}
Else if (current == getExclusiveOwnerThread()) {// if the holder is the current thread, perfect
Int nextc = c + acquires;
If (nextc < 0) // overflow
Throw new Error (" Maximum lock count exceeded ");
SetState (nextc);
Return true;
}
Return false;
}
}
} 


Guava Cache Implementation and source code analysis


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.