• ehcache3-源码简析二


    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 }
  • 相关阅读:
    老周的ABP框架系列教程 -》 一、框架理论初步学习
    poi读写Excel记录
    get 和 post 请求的区别,一个不错的链接
    PostgreSQL的WAL日志概述与Full-Page Writes
    linux挂载硬盘到home目录下(home目录扩容)
    高可用的恢复点目标(RPO)和恢复时间目标(RTO)
    iscsi常用命令汇总
    如何同步linux集群时间
    内部类
    抽象和继承
  • 原文地址:https://www.cnblogs.com/holoyong/p/7423469.html
Copyright © 2020-2023  润新知