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 }