ehcache對於offheap是如何管理的呢?從get操作可以一窺,這里以heap+offheap分層cache為例。
cache由heap+offheap組成時,authoritativeTier為OffHeapStore,OffHeapStore也會從map中get元素,該map就是EhcacheConcurrentOffHeapClockCache。EhcacheConcurrentOffHeapClockCache繼承了AbstractConcurrentOffHeapMap,EhcacheConcurrentOffHeapClockCache也就具有了map的一些特性。AbstractConcurrentOffHeapMap中持有Segment數組,這點類似於jdk7中的ConcurrentHashMap,每一個Segment又是一個Map(但該Map有些特殊)。
1 public abstract class AbstractConcurrentOffHeapMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, ConcurrentMapInternals, HashingMap<K, V> { 2 3 protected final Segment<K, V>[] segments; 4 //可設置的最大segment數 5 private static final int MAX_SEGMENTS = 65536; 6 //與jdk7中的ConcurrentHashMap一樣,也有並發度的概念,即多少個segment 7 private static final int DEFAULT_CONCURRENCY = 16; 8 9 //get元素時會調用該方法 10 public MetadataTuple<V> computeIfPresentWithMetadata(K key, BiFunction<? super K, ? super MetadataTuple<V>, ? extends MetadataTuple<V>> remappingFunction) { 11 //segmentFor會根據key的hash值找到對應的segment,在具體的segment上執行get操作(computeIfPresentWithMetadata),類似jdk7中的ConcurrentHashMap的邏輯 12 return this.segmentFor(key).computeIfPresentWithMetadata(key, remappingFunction); 13 } 14 }
EhcacheSegment繼承了OffHeapHashMap,即EhcacheSegment也具有map的特征,但這個map不一般。
1 public class OffHeapHashMap<K, V> extends AbstractMap<K, V> implements MapInternals, Owner, HashingMap<K, V> { 2 3 //...... 4 5 //EhcacheSegment中的hashtable為一個IntBuffer,這就是它的特別之處,這個IntBuffer是一塊offheap內存(DirectByteBuffer) 6 protected volatile IntBuffer hashtable; 7 //hashtable的初始大小,128個entry 8 private static final int INITIAL_TABLE_SIZE = 128; 9 //resize大小,即負載因子 10 private static final float TABLE_RESIZE_THRESHOLD = 0.5F; 11 //entry大小,4個int 12 protected static final int ENTRY_SIZE = 4; 13 //entry第0個int 14 protected static final int STATUS = 0; 15 //entry第1個int,代表hashcode 16 protected static final int KEY_HASHCODE = 1; 17 //entry第2、3個int,代表key的offheap地址 18 protected static final int ENCODING = 2; 19 20 //...... 21 22 //EhcacheSegment的get操作 23 public MetadataTuple<V> computeIfPresentWithMetadata(K key, BiFunction<? super K, ? super MetadataTuple<V>, ? extends MetadataTuple<V>> remappingFunction) { 24 this.freePendingTables(); 25 int hash = key.hashCode(); 26 //hashtable是一個IntBuffer,該intBuffer中沒4個int構成一個元素(entry), 27 //entry中第一個int表示existingStatus,第二個int表示hash,后兩個int組成的long表示key在offheap中的位置。 28 //hashtable使用線性探測解決hash沖突,這里indexFor得到key在hashtable中未沖突的索引。 29 int start = this.indexFor(spread(hash)); 30 //設置hashtable(intBuffer)的position,意在讀取hashtable時,從start開始讀,也即從key首次映射的索引開始讀。 31 this.hashtable.position(start); 32 int limit = this.reprobeLimit(); 33 34 //開始線性探測 35 for(int i = 0; i < limit; ++i) { 36 if (!this.hashtable.hasRemaining()) { 37 this.hashtable.rewind(); 38 } 39 40 IntBuffer entry = (IntBuffer)this.hashtable.slice().limit(4); 41 if (isTerminating(entry)) { 42 return null; 43 } 44 //readLong(entry,2)就是從entry中讀取一個地址(storageEngine可以根據該地址讀取key、value),根據該地址再從storageEngine中獲取key的真實值; 45 //entry.get(1)是讀取hash值。 46 if (isPresent(entry) && this.keyEquals(key, hash, readLong((IntBuffer)entry, 2), entry.get(1))) { 47 long existingEncoding = readLong((IntBuffer)entry, 2); 48 int existingStatus = entry.get(0); 49 //storageEngine.readValue會根據existingEncoding(即address)獲取value,此時得到的existingValue是一個DirectByteBuffer 50 MetadataTuple<V> existingValue = MetadataTuple.metadataTuple(this.storageEngine.readValue(existingEncoding), existingStatus & -4); 51 //對DirectByteBuffer進行detach 52 MetadataTuple<V> result = (MetadataTuple)remappingFunction.apply(key, existingValue); 53 //..... 54 return result; 55 } 56 //線性探測的下一個 57 this.hashtable.position(this.hashtable.position() + 4); 58 } 59 60 return null; 61 } 62 63 //有相應的expand、shrink方法對hashtable進行動態調整 64 //...... 65 66 }
1 //detach 2 void detach() { 3 if (mode == Mode.ATTACHED) { 4 byte[] bytes = new byte[binaryValue.remaining()]; 5 binaryValue.get(bytes); 6 binaryValue = ByteBuffer.wrap(bytes); 7 mode = Mode.DETACHED; 8 } else { 9 throw new IllegalStateException("OffHeapValueHolder in mode " + mode + " cannot be prepared for delayed deserialization"); 10 } 11 }
1 //OffHeapBufferStorageEngine根據address讀取value 2 public ByteBuffer readValueBuffer(long address) { 3 int keyLength = this.storageArea.readInt(address + 4L); 4 int valueLength = this.storageArea.readInt(address + 8L); 5 //address, length 6 return this.storageArea.readBuffer(address + 12L + (long)keyLength, valueLength).asReadOnlyBuffer(); 7 }