﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-上善若水-随笔分类-EHCache</title><link>http://www.blogjava.net/DLevin/category/54909.html</link><description>In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation.
To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra</description><language>zh-cn</language><lastBuildDate>Fri, 04 Sep 2015 15:05:46 GMT</lastBuildDate><pubDate>Fri, 04 Sep 2015 15:05:46 GMT</pubDate><ttl>60</ttl><item><title>Java Cache-EHCache系列之Store实现</title><link>http://www.blogjava.net/DLevin/archive/2013/11/03/405887.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sun, 03 Nov 2013 14:05:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/11/03/405887.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/405887.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/11/03/405887.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/405887.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/405887.html</trackback:ping><description><![CDATA[<div>写了那么多，终于到Store了。Store是EHCache中Element管理的核心，所有的Element都存放在Store中，也就是说Store用于所有和Element相关的处理。<br />
<br />
<strong style="color: orange;">EHCache中的Element</strong><br />
在EHCache中，它将所有的键值对抽象成一个Element，作为面向对象的设计原则，把数据和操作放在一起，Element除了包含key、value属性以外，它还加入了其他和一个Element相关的统计、配置信息以及操作：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Element&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Serializable,&nbsp;Cloneable&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">the&nbsp;cache&nbsp;key.&nbsp;从1.2以后不再强制要求Serializable，因为如果只是作为内存缓存，则不需要对它做序列化。IgnoreSizeOf注解表示在做SizeOf计算时key会被忽略。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;@IgnoreSizeOf<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Object&nbsp;key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">the&nbsp;value.&nbsp;从1.2以后不再强制要求Serializable，因为如果只是作为内存缓存，则不需要对它做序列化。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Object&nbsp;value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">version&nbsp;of&nbsp;the&nbsp;element.&nbsp;这个属性只是作为纪录信息，EHCache实际代码中并没有用到，用户代码可以通过它来实现不同版本的处理问题。默认值是1。<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">如果net.sf.ehcache.element.version.auto系统属性设置为true，则当Element加入到Cache中时会被更新为当前系统时间。此时，用户设置的值会丢失。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;version;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">The&nbsp;number&nbsp;of&nbsp;times&nbsp;the&nbsp;element&nbsp;was&nbsp;hit.命中次数，在每次查找到一个Element会加1。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;hitCount;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">The&nbsp;amount&nbsp;of&nbsp;time&nbsp;for&nbsp;the&nbsp;element&nbsp;to&nbsp;live,&nbsp;in&nbsp;seconds.&nbsp;0&nbsp;indicates&nbsp;unlimited.&nbsp;即一个Element自创建（CreationTime）以后可以存活的时间。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;timeToLive&nbsp;=&nbsp;Integer.MIN_VALUE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">The&nbsp;amount&nbsp;of&nbsp;time&nbsp;for&nbsp;the&nbsp;element&nbsp;to&nbsp;idle,&nbsp;in&nbsp;seconds.&nbsp;0&nbsp;indicates&nbsp;unlimited.&nbsp;即一个Element自最后一次被使用（min(CreationTime，LastAccessTime)）以后可以存活的时间。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;timeToIdle&nbsp;=&nbsp;Integer.MIN_VALUE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">Pluggable&nbsp;element&nbsp;eviction&nbsp;data&nbsp;instance，它存储这个Element的CreationTime、LastAccessTime等信息，窃以为这个抽取成一个单独的类没什么理由，而且这个类的名字也不好。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;ElementEvictionData&nbsp;elementEvictionData;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">If&nbsp;there&nbsp;is&nbsp;an&nbsp;Element&nbsp;in&nbsp;the&nbsp;Cache&nbsp;and&nbsp;it&nbsp;is&nbsp;replaced&nbsp;with&nbsp;a&nbsp;new&nbsp;Element&nbsp;for&nbsp;the&nbsp;same&nbsp;key,&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">then&nbsp;both&nbsp;the&nbsp;version&nbsp;number&nbsp;and&nbsp;lastUpdateTime&nbsp;should&nbsp;be&nbsp;updated&nbsp;to&nbsp;reflect&nbsp;that.&nbsp;The&nbsp;creation&nbsp;time<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">will&nbsp;be&nbsp;the&nbsp;creation&nbsp;time&nbsp;of&nbsp;the&nbsp;new&nbsp;Element,&nbsp;not&nbsp;the&nbsp;original&nbsp;one,&nbsp;so&nbsp;that&nbsp;TTL&nbsp;concepts&nbsp;still&nbsp;work.&nbsp;在put和replace操作中该属性会被更新。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;lastUpdateTime;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">如果timeToLive和timeToIdle没有手动设置，该值为true，此时在计算expired时使用CacheConfiguration中的timeTiLive、timeToIdle的值，否则使用Element自身的值。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;cacheDefaultLifespan&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">这个ID值用于EHCache内部，但是暂时不知道怎么用。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;id&nbsp;=&nbsp;NOT_SET_ID;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">判断是否expired，这里如果timeToLive、timeToIdle都是Integer.MIN_VALUE时返回false，当他们都是0时，isEternal返回true</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isExpired()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!isLifespanSet()&nbsp;||&nbsp;isEternal())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">false</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;now&nbsp;=&nbsp;System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;expirationTime&nbsp;=&nbsp;getExpirationTime();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;now&nbsp;&gt;&nbsp;expirationTime;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">expirationTime算法：如果timeToIdle没有设置，或设置了，但是该Element还没有使用过，取timeToLive计算出的值；如果timeToLive没有设置，则取timeToIdle计算出的值，<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">否则，取他们的最小值。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getExpirationTime()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!isLifespanSet()&nbsp;||&nbsp;isEternal())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;Long.MAX_VALUE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;expirationTime&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;ttlExpiry&nbsp;=&nbsp;elementEvictionData.getCreationTime()&nbsp;+&nbsp;TimeUtil.toMillis(getTimeToLive());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;mostRecentTime&nbsp;=&nbsp;Math.max(elementEvictionData.getCreationTime(),&nbsp;elementEvictionData.getLastAccessTime());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;ttiExpiry&nbsp;=&nbsp;mostRecentTime&nbsp;+&nbsp;TimeUtil.toMillis(getTimeToIdle());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(getTimeToLive()&nbsp;!=&nbsp;0&nbsp;&amp;&amp;&nbsp;(getTimeToIdle()&nbsp;==&nbsp;0&nbsp;||&nbsp;elementEvictionData.getLastAccessTime()&nbsp;==&nbsp;0))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expirationTime&nbsp;=&nbsp;ttlExpiry;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(getTimeToLive()&nbsp;==&nbsp;0)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expirationTime&nbsp;=&nbsp;ttiExpiry;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expirationTime&nbsp;=&nbsp;Math.min(ttlExpiry,&nbsp;ttiExpiry);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;expirationTime;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">在将Element加入到Cache中并且它的timeToLive和timeToIdle都没有设置时，它的timeToLive和timeToIdle会根据CacheConfiguration的值调用这个方法更新。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLifespanDefaults(<span style="color: #0000FF; ">int</span>&nbsp;tti,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;ttl,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;eternal)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(eternal)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.timeToIdle&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.timeToLive&nbsp;=&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(isEternal())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.timeToIdle&nbsp;=&nbsp;Integer.MIN_VALUE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.timeToLive&nbsp;=&nbsp;Integer.MIN_VALUE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeToIdle&nbsp;=&nbsp;tti;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeToLive&nbsp;=&nbsp;ttl;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;DefaultElementEvictionData&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;ElementEvictionData&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;creationTime;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;lastAccessTime;<br />
}<br />
<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Cache&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;InternalEhcache,&nbsp;StoreListener&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;applyDefaultsToElementWithoutLifespanSet(Element&nbsp;element)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!element.isLifespanSet())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;element.setLifespanDefaults(TimeUtil.convertTimeToInt(configuration.getTimeToIdleSeconds()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUtil.convertTimeToInt(configuration.getTimeToLiveSeconds()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;configuration.isEternal());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
<br />
<strong style="color: orange;">EHCache中的Store设计</strong><br />
Store是EHCache中用于存储、管理所有Element的仓库，它抽象出了所有对Element在内存中以及磁盘中的操作。基本的它可以向一个Store中添加Element（put、putAll、putWithWriter、putIfAbsent）、从一个Store获取一个或一些Element（get、getQuiet、getAll、getAllQuiet）、获取一个Store中所有key（getKeys）、从一个Store中移除一个或一些Element（remove、removeElement、removeAll、removeWithWriter）、替换一个Store中已存储的Element（replace）、pin或unpin一个Element（unpinAll、isPinned、setPinned）、添加或删除StoreListener（addStoreListener、removeStoreListener）、获取一个Store的Element数量（getSize、getInMemorySize、getOffHeapSize、getOnDiskSize、getTerracottaClusteredSize）、获取一个Store的Element以Byte为单位的大小（getInMemorySizeInBytes、getOffHeapSizeInBytes、getOnDiskSizeInBytes）、判断一个key的存在性（containsKey、containsKeyOnDisk、containsKeyOffHeap、containsKeyInMemory）、query操作（setAttributeExtractors、executeQuery、getSearchAttribute）、cluster相关操作（isCacheCoherent、isClusterCoherent、isNodeCoherent、setNodeCoherent、waitUtilClusterCoherent）、其他操作（dispose、getStatus、getMBean、hasAbortedSizeOf、expireElements、flush、bufferFull、getInMemoryEvictionPolicy、setInMemoryEvictionPolicy、getInternalContext、calculateSize）。<br />
<br />
所谓Cache，就是将部分常用数据缓存在内存中，从而提升程序的效率，然而内存大小毕竟有限，因而有时候也需要有磁盘加以辅助，因而在EHCache中真正的Store实现就两种（不考虑分布式缓存的情况下）：存储在内存中的MemoryStore和存储在磁盘中的DiskStore，而所有其他Store都是给予这两个Store的基础上来扩展Store的功能，如因为内存大小的限制，有些时候需要将内存中的暂时不用的Element写入到磁盘中，以腾出空间给其他更常用的Element，此时就需要MemoryStore和DiskStore共同来完成，这就是FrontCacheTier做的事情，所有可以结合FrontEndCacheTier一起使用的Store都要实现TierableStore接口（DiskStore、MemoryStore、NullStore）；对于可控制Store占用空间大小做限制的Store还可以实现PoolableStore（DiskStore、MemoryStore）；对于具有Terracotta特性的Store还实现了TerracottaStore接口（TransactionStore等）。<br />
<br />
EHCache中Store的设计类结构图如下：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/EHCacheStore.jpg" alt="" height="879" width="1656" /></div>
<br />
<strong style="color: orange;">AbstractStore</strong><br />
几乎所有的Store实现都继承自AbstractStore，它实现了Query、Cluster等相关的接口，但没有涉及Element的管理，而且这部分现在也不了解，不详述。<br />
<br />
<strong style="color: orange;">MemoryStore和NotifyingMemoryStore</strong><br />
MemoryStore是EHCache中存储在内存中的Element的仓库。它使用SelectableConcurrentHashMap作为内部的存储结构，该类实现参考ConcurrentHashMap，只是它加入了pinned、evict等逻辑，不详述（注：它的setPinned方法中，对不存在的key，会使用一个DUMMY_PINNED_ELEMENT来创建一个节点，并将它添加到HashEntry的链中，这时为什么？窃以为这个应该是为了在以后这个key添加进来后，当前的pinned设置可以对它有影响，因为MemoryStore中并没有包含所有的Element，还有一部分Element是在DiskStore中）。而MemoryStore中的基本实现都代理给SelectableConcurrentHashMap，里面的其他细节在之前的文章中也有说明，不再赘述。 <br />
<br />
而NotifyingMemoryStore继承自MemoryStore，它在Element evict和exipre时会调用注册的CacheEventListener。<br />
<br />
<strong style="color: orange;">DiskStore</strong><br />
DiskStore依然采用ConcurrentHashMap的实现思想，因而这部分逻辑不赘述。对DiskStore，当一个Element添加进来后，需要将其写入到磁盘中，这是接下来关注的重点。在DiskStore中，一个Element不再以Element本身而存在，而是以DiskSubstitute的实例而存在，DiskSubstitute有两个子类：PlaceHolder和DiskMarker，当一个Element初始被添加到DiskStore中时，它是以PlaceHolder的形式存在，当这个PlaceHolder被写入到磁盘中时，它会转换成DiskMarker。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;DiskSubstitute&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;onHeapSize;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@IgnoreSizeOf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;DiskStorageFactory&nbsp;factory;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DiskSubstitute(DiskStorageFactory&nbsp;factory)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.factory&nbsp;=&nbsp;factory;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;Object&nbsp;getKey();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getHitCount();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getExpirationTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;installed();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;DiskStorageFactory&nbsp;getFactory()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;factory;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Placeholder&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;DiskSubstitute&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@IgnoreSizeOf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Object&nbsp;key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Element&nbsp;element;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;failedToFlush;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Placeholder(Element&nbsp;element)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>(DiskStorageFactory.<span style="color: #0000FF; ">this</span>);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.key&nbsp;=&nbsp;element.getObjectKey();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.element&nbsp;=&nbsp;element;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;installed()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DiskStorageFactory.<span style="color: #0000FF; ">this</span>.schedule(<span style="color: #0000FF; ">new</span>&nbsp;PersistentDiskWriteTask(<span style="color: #0000FF; ">this</span>));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;DiskMarker&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;DiskSubstitute&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Serializable&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@IgnoreSizeOf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Object&nbsp;key;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;position;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;hitCount;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;expiry;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DiskMarker(DiskStorageFactory&nbsp;factory,&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;position,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size,&nbsp;Element&nbsp;element)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>(factory);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.position&nbsp;=&nbsp;position;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.size&nbsp;=&nbsp;size;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.key&nbsp;=&nbsp;element.getObjectKey();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.hitCount&nbsp;=&nbsp;element.getHitCount();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.expiry&nbsp;=&nbsp;element.getExpirationTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;installed()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">no-op</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;hit(Element&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hitCount++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expiry&nbsp;=&nbsp;e.getExpirationTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
当向DiskStore添加一个Element时，它会先创建一个PlaceHolder，并将该PlaceHolder添加到DiskStore中，并在添加完成后调用PlaceHolder的installed()方法，该方法会使用DiskStorageFactory schedule一个PersistentDiskWriteTask，将该PlaceHolder写入到磁盘（在DiskStorageFactory有一个DiskWriter线程会在一定的时候执行该Task）生成一个DiskMarker，释放PlaceHolder占用的内存。在从DiskStore移除一个Element时，它会先读取磁盘中的数据，将其解析成Element，然后释放这个Element占用的磁盘空间，并返回这个被移除的Element。在从DiskStore读取一个Element时，它需要找到DiskStore中的DiskSubstitute，对DiskMarker读取磁盘中的数据，解析成Element，然后返回。 <br />
<br />
<strong style="color: orange;">FrontEndCacheTier</strong>
<br />
上述的MemoryStore和DiskStore，他们是各自独立的，然而Cache的一个重要特点是可以将部分内存中的数据evict出到磁盘，因为内存毕竟是有限的，所以需要有另一个Store可以将MemoryStore和DiskStore联系起来，这就是FrontEndCacheTier做的事情。FrontEndCacheTier有两个子类：DiskBackedMemoryStore和MemoryOnlyStore，这两个类的名字已经能很好的说明他们的用途了，DiskBackedMemoryStore可以将部分Element先evict出到磁盘，它也支持把磁盘文件作为persistent介质，在下一次读取时可以直接从磁盘中的文件直接读取并重新构建原来的缓存；而MemoryOnlyStore则只支持将Element存储在内存中。FrontEndCacheTier有两个Store属性：cache和authority，它将基本上所有的操作都直接同时代理给这两个Store，其中把authority作为主的存储Store，而将cache作为缓存的Store。在DiskBackedMemoryStore中，authority是DiskStore，而cache是MemoryStore，即DiskBackedMemoryStore将DiskStore作为主的存储Store，这刚开始让我很惊讶，不过仔细想想也是合理的，因为毕竟这里的Disk是作为persistent介质的；在MemoryOnlyStore中，authority是MemoryStore，而cache是NullStore。<br />
FrontEndCacheTier在实现get方法时，添加了faults属性的ConcurrentHashMap，它是用于多个线程在同时读取同一key的Element时避免多次读取，每次之前将key和一个Fault新实例添加到faults中，这样第二个线程发现已经有另一个线程在读这个Element了，它就可以等待第一个线程读完直接拿第一个线程读取的结果即可，以提升性能。<br />
所有作为FrontEndCacheTier的内部Store都必须实现TierableStore接口，其中fill、removeIfNotPinned、isTierPinned、getPresentPinnedKeys为cache store准备，而removeNoReturn、isPersistent为authority store准备。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;TierableStore&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Store&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;fill(Element&nbsp;e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;removeIfNotPinned(Object&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeNoReturn(Object&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isTierPinned();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Set&nbsp;getPresentPinnedKeys();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isPersistent();<br />
}</div>
<br />
<strong style="color: orange;">LruMemoryStore和LegacyStoreWrapper</strong><br />
这两个Store只是为了兼容而存在，其中LruMemoryStore使用LinkedHashMap作为其存储结构，他只支持一种Evict算法：LRU，这个Store的名字也因此而来，其他功能它类似MemoryStore，而LegacyStoreWrapper则类似FrontEndCacheTier。这两个Store的代码比较简单，而且他们也不应该再被使用，因而不细究。 <br />
<br />
<strong style="color: orange;">TerraccottaStore</strong><br />
对所有实现这个接口的Store都还不了解，看以后有没有时间回来了。。。。<img src ="http://www.blogjava.net/DLevin/aggbug/405887.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-11-03 22:05 <a href="http://www.blogjava.net/DLevin/archive/2013/11/03/405887.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache-EHCache系列之使用Pool和PoolAccessor抽象实现内存和磁盘中数据字节数的控制和Evict</title><link>http://www.blogjava.net/DLevin/archive/2013/11/03/405886.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 02 Nov 2013 16:49:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/11/03/405886.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/405886.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/11/03/405886.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/405886.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/405886.html</trackback:ping><description><![CDATA[<div>在上一篇《<a href="">Java Cache-EHCache系列之计算实例占用的内存大小（SizeOf引擎）</a>》中有说到：在EHCache中，可以设置maxBytesLocalHeap、maxBytesLocalOffHeap、maxBytesLocalDisk值，以控制Cache占用的内存、磁盘的大小（注：这里Off Heap是指Element中的值已被序列化，但是还没写入磁盘的状态，貌似只有企业版的EHCache支持这种配置；而这里maxBytesLocalDisk是指在最大在磁盘中的数据大小，而不是磁盘文件大小，因为磁盘文中有一些数据是空闲区）。那么如何实现这个需求呢？<br />
<br />
<strong style="color: orange;">常规设计思路</strong><br />
对这个需求，我们已经有SizeOfEngine用于计算内存中以及磁盘中的实例大小了，按我的常规思路，实现起来会比较简单，直接在Store中添加maxBytes以及currentBytes字段，用于表达当前Store可以存放的最大实例大小以及当前Store已存放的实例总大小。对MemoryStore，在添加新Element之前使用DefaultSizeOfEngine计算要添加实例占用内存的大小，然后判断要添加的数据大小和currentBytes相加的值是否会超过maxBytes，如果是，先找出已经expired的Element，将这些Element移除，如果此时还没能在maxBytes大小的限制中添加新的Element，则对当前Store做evict操作，evict出至少超过maxBytes大小的的数据大小；如果是更新原来已存在的Element，一种方法先查找出已存在的Element，然后计算新添加的Element和原来Element占用内存大小的Delta值，如果该值大于0并且和currentBytes相加会大于maxBytes值，则如上做expire和evict操作，另一种方法是先移除已存在的Element，然后对当前Element做新添加Element操作。对DiskStore，使用DiskSizeOfEngine计算要添加的实例大小，其他和MemoryStore类似。<br />
<br />
在EHCache中，MemoryStore和DiskStore都是使用了Segment的设计思想（不知道是否因为ConcurrentHashMap的影响，甚至Guava Cache也采用了这种Segment的设计思想），因而我们可以将maxBytes、currentBytes抽象出一个MaxBytesGuarder类，并且将该实例赋值给每个Segment实例，作为面向对象设计的基本思路，将数据和操作放在一起它已经有了maxBytes和currentBytes数据了，我们就可以尝试这将和它相关的操作添加到这个类中，比如实例占用内存、磁盘大小的计算，因而可以将SizeOfEngine引人该类中，另外它还可以包含一个ElementEvictor实例，它可用于evict操作，同时因为MaxBytesGuarder在每个Segment共享，因而它必须是线程安全的。对更新已存在的Element可以采用添加之前先移除的方法，这样MaxBytesGuarder就可以设计成包含如下方法：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;MaxBytesGuarder&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getCurrentSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMaxSize(<span style="color: #0000FF; ">int</span>&nbsp;maxSize);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMaxSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;addWithEvict(Element&nbsp;element);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;canAddWithoutEvict(Element&nbsp;element);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;delete(<span style="color: #0000FF; ">int</span>&nbsp;size);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ElementEvictor&nbsp;getEvictor();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Store&nbsp;getStore();<br />
}</div>
</div>
<br />
常规的Cache设计思路是用MemoryStore存储内存中的Element，而将DiskStore作为MemoryStore的从属，只有在Evict发生才将Element通过DiskStore写入磁盘，读取时，只有在MemoryStore中找不到对应的Element时，才从DiskStore中查找，并且将找到的Element读回MemoryStore，对这种设计MaxBytesGuarder只需要在各自的Store中存在即可。但是在EHCache当前Store的设计中，DiskStore并没有作为MemoryStore的从属，它们是单独可用的Store，EHCache使用DiskBackedMemoryStore把它们联系起来，这个在接下来的文章中会详细分析。在DiskBackedMemoryStore的实现中，DiskStore作为authority，对每次新添加Element，会首先添加到DiskStore中，然后才是到MemoryStore，从而对MemoryStore来说，它在做evict时，直接删除即可，因为DiskStore已经存储了所有的Element信息。既然DiskStore可以存储内存中的Element以及磁盘中的Element，并且对磁盘中的Element，EHCache也会在内存中有一些纪录信息，因而它需要同时控制内存中的Element占用内存最大值以及磁盘中能保留的Element的最大值。对这个需求，以上的MaxBytesGuarder就很难办到了，因而EHCache做了进一步的抽象，把MaxBytesGuarder拆分成两个接口：Pool和PoolAccessor，其中Pool用于对maxSize的抽象，它包含PoolEvictor引用，已处理需要Evict时的操作，通过Pool创建PoolAccessor，PoolAccessor和一个Store相关联，用于处理和某个Store相关的put、remove等操作，一个Pool当前Size是通过其创建的所有PoolAccessor的size总和。<br />
<br />
<strong style="color: orange;">Pool和PoolAccessor设计</strong><br />
Pool和PoolAccessor的类结构图如下：
<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/EHCachePool.jpg" alt="" height="757" width="1109" />
<br />
从类结构图中可以知道，Pool包含maxSize、PoolEvictor、SizeOfEngine以及PoolAccessor的List，并且Pool内部所有PoolAccessor的size和是当前Pool实例当前使用的size。Pool还可用于创建相应的PoolAccessor实例，通过Pool创建的PoolAccessor会被自动的添加到Pool中。在Cache实例初始化时，它会根据配置创建相应的onHeapPool和onDiskPool，它们可以是UnboundedPool，它的createPoolAccessor方法创建UnboundedPoolAccessor实例；可以是BoundedPool，用于创建AtomicPoolAccessor；可以是StrictlyBoundedPool，用于创建LockedPoolAccessor。其中AtomicPoolAccessor和LockedPoolAccessor的区别在于对size的锁机制，AtomicPoolAccessor使用AtomicLong纪录size的值而不是通过锁，这样会引起maxSize的限制有保证的情况，比如两个线程同时在添加Element，但是在一个线程还没添加完成时另一个线程已经执行了判断语句说当前还有足够多的空间，然而事实上在第一个线程执行完成后，当前的空间可能已经不够了，这种方式的好处是没有锁，效率很高，在那些对maxSize不是很敏感的项目中可以使用（感觉一般对这个值都不会很敏感）；而LockedPoolAccessor则是采用锁的机制来保证size值的一致性，它一般用于那些对maxSize值特别敏感的项目中。PoolAccessor由Pool创建，除了从Pool中继承Pool自身实例、SizeOfEngine实例以外，它还和一个PoolableStore相绑定，这个绑定的PoolableStore可以通过PoolAccessor向Pool中消费一个Element大小的空间（add方法）、判断是否可以在没有Evict的情况下消费一个Element大小的空间（canAddWithoutEvicting方法）、移除指定大小的空间、以另一个Element大小的空间替换已存在的另一个Element空间大小（replace）等。这里的add方法，当可以成功的向这个PoolAccessor消费一个Element大小的空间时，它返回新添加的Element大小，否则返回－1，表示添加失败；另外对add方法的container表示存放该Element的实例，比如HashEntry（只用于计算大小，因而一般都用一个空的HashEntry来替代），对onDiskPoolAccessor来说，它用于表示一个DiskMarker。 <br />
<br />
<strong style="color: orange;">PoolEvictor</strong><br />
PoolableStore在调用PoolAccessor的add方法用于消费Pool中一个Element大小的空间时，会调用PoolEvictor的freeSpace将该Pool相关联的所有PoolableStore释放掉不够的空间大小：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;add(<span style="color: #0000FF; ">long</span>&nbsp;sizeOf,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;force)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;newSize&nbsp;=&nbsp;getPool().getSize()&nbsp;+&nbsp;sizeOf;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(newSize&nbsp;&lt;=&nbsp;getPool().getMaxSize())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;there&nbsp;is&nbsp;enough&nbsp;room&nbsp;=&gt;&nbsp;add&nbsp;&amp;&nbsp;approve</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size.addAndGet(sizeOf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;sizeOf;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;check&nbsp;that&nbsp;the&nbsp;element&nbsp;isn't&nbsp;too&nbsp;big</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!force&nbsp;&amp;&amp;&nbsp;sizeOf&nbsp;&gt;&nbsp;getPool().getMaxSize())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;this&nbsp;is&nbsp;too&nbsp;big&nbsp;to&nbsp;fit&nbsp;in&nbsp;the&nbsp;pool</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;-1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;if&nbsp;there&nbsp;is&nbsp;not&nbsp;enough&nbsp;room&nbsp;=&gt;&nbsp;evict</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;missingSize&nbsp;=&nbsp;newSize&nbsp;-&nbsp;getPool().getMaxSize();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(getPool().getEvictor().freeSpace(getPool().getPoolableStores(),&nbsp;missingSize)&nbsp;||&nbsp;force)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size.addAndGet(sizeOf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;sizeOf;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;cannot&nbsp;free&nbsp;enough&nbsp;bytes</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;-1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
PoolEvictor接口则是对如何从多个PoolableStore中evict出指定空间大小的算法的抽象，在EHCache中默认实现了两中算法：Balanced和FromLargest。FromLargest算法比较简单，它只需要遍历所有的PoolableStore，每次选择一个没有evict过的使用空间最大的PoolableStore，尝试从它evict出指定大小的空间直到指定大小的空间被腾出来；Balanced算法有点复杂，它先打乱PoolableStore，然后遍历乱序的PoolableStore，每次选取其后部分PoolableStore，对齐按一下算法排序，遍历排序后的子PoolableStore集，对每个PoolableStore尝试evict指定大小的空间，直到evict执行成功。<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><span style="color: #008000; ">/*</span><span style="color: #008000; "><br />
&nbsp;*&nbsp;The&nbsp;code&nbsp;below&nbsp;is&nbsp;a&nbsp;simplified&nbsp;version&nbsp;of&nbsp;this:<br />
&nbsp;*<br />
&nbsp;*&nbsp;float&nbsp;meanEntrySize&nbsp;=&nbsp;byteSize&nbsp;/&nbsp;countSize;<br />
&nbsp;*&nbsp;float&nbsp;accessRate&nbsp;=&nbsp;hitRate&nbsp;+&nbsp;missRate;<br />
&nbsp;*&nbsp;float&nbsp;fillLevel&nbsp;=&nbsp;hitRate&nbsp;/&nbsp;accessRate;<br />
&nbsp;*&nbsp;float&nbsp;deltaFillLevel&nbsp;=&nbsp;fillLevel&nbsp;/&nbsp;byteSize;<br />
&nbsp;*<br />
&nbsp;*&nbsp;return&nbsp;meanEntrySize&nbsp;*&nbsp;accessRate&nbsp;*&nbsp;deltaFillLevel&nbsp;*&nbsp;hitDistributionFunction(fillLevel);<br />
</span><span style="color: #008000; ">*/</span></div>
因为在PoolableStore中将从磁盘evict和从内存evict定义成两个不同的方法，因而对每种算法的PoolEvictor都由两个子类实现：BalancedAccessOnDiskPoolEvictor、BalancedAccessOnHeapPoolEvictor和FromLargestCacheOnDiskPoolEvictor、FromLargestCacheOnHeapPoolEvictor。这里PoolableStore接口的抽象用于在提供Evict操作时的信息，如PoolEvictor中evict方法的实现、Balanced算法中的一些统计信息的获得等：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;PoolableStore&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Store&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;evictFromOnHeap(<span style="color: #0000FF; ">int</span>&nbsp;count,&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;size);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;evictFromOnDisk(<span style="color: #0000FF; ">int</span>&nbsp;count,&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;size);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">float</span>&nbsp;getApproximateDiskHitRate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">float</span>&nbsp;getApproximateDiskMissRate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getApproximateDiskCountSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getApproximateDiskByteSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">float</span>&nbsp;getApproximateHeapHitRate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">float</span>&nbsp;getApproximateHeapMissRate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getApproximateHeapCountSize();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getApproximateHeapByteSize();<br />
}</div>
<br />
<strong style="color: orange;">PoolableStroe中evict逻辑的实现</strong><br />
所谓evict就是使用配置的evict算法选出部分Element实例，将它们从Store中移除。对MemoryStore，它只实现evictFromOnHeap方法，而对DiskStore只需实现evictFromOnDisk方法。<br />
<br />
对MemoryStore，evict操作的主要流程是根据配置的EvictPolicy选取下一个expired或要被evict的Element，将这个Element移除，并出发expired或evict事件，在做evict之前先判断该Element或当前Store处于pinned状态，如果是，则不做evict，返回false。因而这里最主要的是要如何使用EvictPolicy选取下一个要被Evict的Element。EHCache实现了四种算法：最近最少使用算法（LRU）、先进先出算法（FIFO）、最少使用算法（LFU）、钟算法（CLOCK）。<br />
钟算法实现比较简单，它随即的选择一个Segment，每个Segment内部保存一个evictionIterator，每一次evict调用就是从这个Iterator中获取下一个expired Element或unpinned Element（如果该Iterator遍历到最后一个Element，则重新开始，即像钟一样不同的循环），将找到的Element从该Segment中移除。<br />
对其他的算法，都要先从MemoryStore中选取一个Element的样本数组，然后使用不同的Policy实现获取样本中的候选evict Element。样本Element数组的最大容量是30，其选取算法是：如果当前evict是因为新添加一个Element引起，则从新添加的Element所在的Segment中选取样本，否则随机的选取一个Segment，在选取的Segment中随机的选取一个HashEntry链，将这个链中所有unpinned Element加入的样本数据中，如果一条链不够，则循环的查找下一条链直到样本量达到指定的要求或整个Segment所有unpinned Element都已经添加到样本中。所有的算法都是基于这些样本选择下一个候选的evict Element。<br />
FifoPolicy：样本中Update（Create）时间最早的Element。<br />
LfuPolicy：样本中最少被使用的Element（Hit Count最少）。<br />
LruPolicy：样本中最近最少被使用的Element（LastAccessTime最小）。<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/EHCacheEvictionPolicy.jpg" alt="" height="257" width="440" />
<br /><br />对DiskStore，evict操作类似MemoryStore，先找到一个DiskSubstitute（必须是DiskMarker类型）样本数组（算法和MemoryStore中找Element样本数组类似，最大样本容量也是30），对找到的样本数组采用最少使用算法（Hit Count）或根据传入要被evict的key作为下一个evict的候选，并尝试将该DiskSubstitute（DiskMarker）从磁盘中移除，读取磁盘中的数据并反序列化成Element，返回凡序列化后的Element实例。移除的步骤包括：将他从MemoryStore中移除；将DiskSubstitute对应的节点从HashEntry中删除；释放该Element原本在磁盘中占用的空间；释放Disk Pool中占用的空间；释放Heap Pool中占用的空间。<img src ="http://www.blogjava.net/DLevin/aggbug/405886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-11-03 00:49 <a href="http://www.blogjava.net/DLevin/archive/2013/11/03/405886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache-EHCache系列之计算实例占用的内存大小（SizeOf引擎）</title><link>http://www.blogjava.net/DLevin/archive/2013/11/01/405822.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Fri, 01 Nov 2013 03:03:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/11/01/405822.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/405822.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/11/01/405822.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/405822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/405822.html</trackback:ping><description><![CDATA[在EHCache中，可以设置maxBytesLocalHeap、maxBytesLocalOffHeap、maxBytesLocalDisk值，以控制Cache占用的内存、磁盘的大小（注：这里Off Heap是指Element中的值已被序列化，但是还没写入磁盘的状态，貌似只有企业版的EHCache支持这种配置；而这里maxBytesLocalDisk是指在最大在磁盘中的数据大小，而不是磁盘文件大小，因为磁盘文中有一些数据是空闲区），因而EHCache需要有一种机制计算一个类在内存、磁盘中占用的字节数，其中在磁盘中占用的字节大小计算比较容易，只需要知道序列化后字节数组的大小，并且加上一些统计信息，如过期时间、磁盘位置、命中次数等信息即可，而要计算一个对象实例在内存中占用的大小则要复杂一些。<br />
<br />
<strong style="color: orange;">计算一个实例内存占用大小思路</strong><br />
在Java中，除了基本类型，其他所有通过字段包含其他实例的关系都是引用关系，因而我们不能直接计算该实例占用的内存大小，而是要递归的计算其所有字段占用的内存大小的和。在Java中，我们可以将所有这些通过字段引用简单的看成一种树状结构，这样就可以遍历这棵树，计算每个节点占用的内存大小，所有这些节点占用的内存大小的总和就当前实例占用的内存大小，遍历的算法有：先序遍历、中序遍历、后序遍历、层级遍历等。但是在实际情况中很容易出现环状引用（最简单的是两个实例之间的直接引用，还有是多个实例构成的一个引用圈），而破坏这种树状结构，而让引用变成图状结构。然而图的遍历相对比较复杂（至少对我来说），因而我更愿意把它继续看成一颗树状图，采用层级遍历，通过一个IdentitySet纪录已经计算过的节点（实例），并且使用一个Queue来纪录剩余需要计算的节点。算法步骤如下：<br />
1. 先将当前实例加入Queue尾中。<br />
2. 循环取出Queue中的头节点，计算它占用的内存大小，加到总内存大小中，并将该节点添加到IdentitySet中。<br />
3. 找到该节点所有非基本类型的子节点，对每个子节点，如果在IdentityMap中没有这个子节点的实例，则将该实例加入的Queue尾。<br />
4. 回到2继续计算直到Queue为空。<br />
剩下的问题就是如何计算一个实例本身占用的内存大小了。这个以我目前的经验，我只能想到遍历一个实例的所有实例字段，根据每个字段的类型来判断每个字段占用的内存大小，然后它们的和就是该实例占用的总内存的大小。对于字段的类型，首先是基本类型字段，byte、boolean占一个字节，short、char占2个字节，int、float占4个字节，double占8个字节等；然后是引用类型，对类型，印象中虚拟机规范中没有定义其大小，但是一般来说对32位系统占4个字节，对64位系统占8个字节；再就是对数组，基本类型的数组，byte每个元素占1个字节，short、char每个元素占2个字节，int每个元素占4个字节，double每个元素占8个字节，引用类型的数组，先计算每个引用元素占用的字节数，然后是引用本省占用的字节数。<br />
以上是我对EHCache中计算一个实例逻辑不了解的时候的个人看法，那么接下来我们看看EHCache怎么来计算。<br />
<br />
<strong style="color: orange;">Java对象内存结构（以Sun JVM为例）</strong><br />
参考：http://www.importnew.com/1305.html，之所以把参考链接放在开头是因为下面基本上是对链接所在文章的整理，之所以要整理一遍，一是怕原链接文章消失，二则是为了加深自己的理解。<br />
在Sun JVM中，除数组以外的对象都有8个字节的头部（数组还有额外的4个字节头部用于存放长度信息），前面4个字节包含这个对象的标识哈希码以及其他一些flag，如锁状态、年龄等标识信息，后4个字节包含一个指向对象的类实例（Class实例）的引用。在这头部8个字节之后的内存结构遵循一下5个规则：<br />
<em style="color: purple;">规则1: 任何对象都是以8个字节为粒度进行对齐的。</em><br />
比如对一个Object类，因为它没有任何实例，因而它只有8个头部直接，则它占8个字节大小。而对一个只包含一个byte字段的实例，它需要填上（padding）7个字节的大小，因而它占16个字节，典型的如一个Boolean实例要占用16个字节的内存！<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">class</span>&nbsp;MyClass&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;a;<br />
}<br />
[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;8<br />
[a:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;<span style="color: purple;">byte</span>&nbsp;] 9<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;7&nbsp;bytes] 16</div>
<em style="color: purple;">规则2: 类属性按照如下优先级进行排列：长整型和双精度类型；整型和浮点型；字符和短整型；字节类型和布尔类型；最后是引用类型。这些属性都按照各自的单位对齐。</em><br />
在Java对象内存结构中，对象以上述的8个字节的头部开始，然后对象属性紧随其后。为了节省内存，Sun VM并没有按照属性声明时顺序来进行内存布局，而是使用如下顺序排列：<br />
1. 双精度型（double）和长整型（long），8字节。<br />
2. 整型（int）和浮点型（float），4字节。<br />
3. 短整型（short）和字符型（char），2字节。<br />
4. 布尔型（boolean）和字节型（byte），2字节。<br />
5. 引用类型。<br />
并且对象属性总是以它们的单位对齐，对于不满4字节的数据类型，会填充未满4字节的部分。之所以要填充是出于性能考虑：因为从内存中读取4字节数据到4字节寄存器的动作，如果数据以4字节对齐的情况小，效率要高的多。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">class</span>&nbsp;MyClass&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;a;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;c;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;d;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;e;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;f;<br />
}<br />
<span style="color: #008000; ">//</span><span style="color: #008000; ">如果JVM不对其重排序，它要占40个字节</span><span style="color: #008000; "><br />
</span>[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;8<br />
[a: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;9<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;bytes]&nbsp;12<br />
[c: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 4&nbsp;bytes]&nbsp;16<br />
[d: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;17<br />
[padding: &nbsp; &nbsp;7&nbsp;bytes]&nbsp;24<br />
[e: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 8&nbsp;bytes]&nbsp;32<br />
[f: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp;bytes]&nbsp;36<br />
[padding: &nbsp; &nbsp; 4&nbsp;bytes]&nbsp;40<br />
<span style="color: #008000; ">//</span><span style="color: #008000; ">经JVM重排序后，只需要占32个字节</span><span style="color: #008000; "><br />
</span>[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;8<br />
[e: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;8&nbsp;bytes]&nbsp;16<br />
[c: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp;bytes]&nbsp;20<br />
[a: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;21<br />
[d: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;22<br />
[padding: &nbsp; &nbsp; &nbsp; 2&nbsp;bytes]&nbsp;24<br />
[f: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp;bytes]&nbsp;28<br />
[padding: &nbsp; &nbsp; &nbsp; 4&nbsp;bytes]&nbsp;32</div>
<span style="color: black;"><em style="color: purple;">规则3: 不同类继承关系中的成员不能混合排列。首先按照规则2处理父类中的成员，接着才是子类的成员。</em><br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">class</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;a;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;c;<br />
}<br />
<span style="color: #0000FF; ">class</span>&nbsp;B&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;d;<br />
}<br />
[HEADER: &nbsp; &nbsp; &nbsp;8&nbsp;bytes]&nbsp;8<br />
[a:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;16<br />
[b:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;bytes]&nbsp;20<br />
[c:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;32</div>
<span style="color: black;"><em style="color: purple;">规则4: 当父类最后一个属性和子类第一个属性之间间隔不足4字节时，必须扩展到4个字节的基本单位。</em><br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">class</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;a;<br />
}<br />
<span style="color: #0000FF; ">class</span>&nbsp;B&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;b;<br />
}<br />
[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;8<br />
[a: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;9<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;bytes]&nbsp;12<br />
[b: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;13<br />
[padding: &nbsp; &nbsp;3&nbsp;bytes]&nbsp;16</div>
<span style="color: black;"><em style="color: purple;">规则5: 如果子类第一个成员时一个双精度或长整型，并且父类没有用完8个字节，JVM会破坏规则2，按整型（int）、短整型（short）、字节型（byte）、引用类型（reference）的顺序向未填满的空间填充。</em><br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">class</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;a;<br />
}<br />
<span style="color: #0000FF; ">class</span>&nbsp;B&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;A&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">short</span>&nbsp;c;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;d;<br />
}<br />
[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;8<br />
[a: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;9<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;bytes]&nbsp;12<br />
[c: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 2&nbsp;bytes]&nbsp;14<br />
[d: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;15<br />
[padding: &nbsp; &nbsp;8&nbsp;bytes]&nbsp;24</div>
<span style="color: black;"><em style="color: purple;">数组内存布局</em><br />
数组对象除了作为对象而存在的头以外，还存在一个额外的头部成员用来存放数组的长度，它占4个字节。<br />
</span>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000; ">//</span><span style="color: #008000; ">三个元素的字节数组</span><span style="color: #008000; "><br />
</span>[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;12&nbsp;bytes]&nbsp;12<br />
[[0]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;13<br />
[[1]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;14<br />
[[2]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;15<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;]&nbsp;16<br />
<span style="color: #008000; ">//</span><span style="color: #008000; ">三个元素的长整型数组</span><span style="color: #008000; "><br />
</span>[HEADER:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;12&nbsp;bytes]&nbsp;12<br />
[padding:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;bytes&nbsp;]&nbsp;16<br />
[[0]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;24<br />
[[1]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;32<br />
[[2]:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;bytes]&nbsp;40</div>
<span style="color: black;"><em style="color: purple;">非静态内部类</em><br />
非静态内不累它又一个额外的&#8220;隐藏&#8221;成员，这个成员时一个指向外部类的引用变量。这个成员是一个普通引用，因此遵循引用内存布局的规则。因此内部类有4个字节的额外开销。<br />
<br />
<strong style="color: orange;">EHCache计算一个实例占用的内存大小</strong></span><br />
EHCache中计算一个实例占用内存大小的基本思路和以上类似：遍历实例数上的所有节点，对每个节点计算其占用的内存大小。不过它结构设计的更好，而且它有三种用于计算一个实例占用内存大小的实现。我们先来看这三种用于计算一个实例占用内存大小的逻辑：<br />
<ol style="margin-top: 1px;">
     <li>ReflectionSizeOf<br />
     使用反射的方式计算计算一个实例占用的内存大小就是我上面想到的这种方法。<br />
     <br />
     因为使用反射计算一个实例占用内存大小的根据不同虚拟机的特性是来判断一个实例的各个字段占用的大小以及该实例存储额外信息占用的大小，因而EHCache中采用JvmInformation枚举类型来抽象这种对不同虚拟机实现的不同：<br />
     <table style="border-collapse: collapse; width: 996pt;" border="0" cellpadding="0" cellspacing="0" width="1327">
         <colgroup><col style="width:254pt" width="339"> <col style="width:88pt" width="117"> <col style="width:96pt" width="128"> <col style="width:100pt" width="133"> <col style="width:87pt" width="116"> <col style="width:96pt" width="128"> <col style="width:135pt" width="180"> <col style="width:140pt" width="186"> </colgroup>
         <tbody>
             <tr style="height: 21pt; color: green;" height="28">
                 <td style="height:21.0pt;width:254pt" height="28" width="339"><strong>JVM Desc</strong></td>
                 <td style="width:88pt" width="117"><strong>PointerSize</strong></td>
                 <td style="width:96pt" width="128"><strong>JavaPointerSize</strong></td>
                 <td style="width:100pt" width="133"><strong>MinimumObjectSize</strong></td>
                 <td style="width:87pt" width="116"><strong>ObjectAlignment</strong></td>
                 <td style="width:96pt" width="128"><strong>ObjectHeaderSize</strong></td>
                 <td style="width:135pt" width="180"><strong>FieldOffsetAdjustment</strong></td>
                 <td style="width:140pt" width="186"><strong>AgentSizeOfAdjustment</strong></td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>HotSpot 32-Bit</strong></td>
                 <td style="width:88pt" align="right" width="117">4</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">8</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>HotSpot 32-Bit with Concurrent Mark-and-Sweep GC</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">4</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">16</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">8</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>HotSpot 64-Bit</strong></td>
                 <td style="width:88pt" align="right" width="117">8</td>
                 <td style="width:96pt" align="right" width="128">8</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">16</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>HotSpot 64-Bit With Concurrent Mark-and-Sweep GC</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">8</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">24</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">16</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>HotSpot 64-Bit with Compressed OOPs</strong></td>
                 <td style="width:88pt" align="right" width="117">8</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">12</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:30.75pt;" height="41">
                 <td style="height:30.75pt;width:254pt" height="41" width="339"><strong>HotSpot 64-Bit with Compressed OOPs and Concurrent Mark-and-Sweep GC</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">24</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">12</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>JRockit 32-Bit</strong></td>
                 <td style="width:88pt" align="right" width="117">4</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">16</td>
                 <td style="width:135pt" align="right" width="180">8</td>
                 <td style="width:140pt" align="right" width="186">8</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>JRockit 64-Bit(with no reference compression)</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">4</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">8</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">16</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">8</td>
                 <td style="width:140pt" align="right" width="186">8</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>JRockit 64-Bit with 4GB compressed References</strong></td>
                 <td style="width:88pt" align="right" width="117">4</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">16</td>
                 <td style="width:135pt" align="right" width="180">8</td>
                 <td style="width:140pt" align="right" width="186">8</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>JRockit 64-Bit with 32GB Compressed References</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">4</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">8</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">16</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">8</td>
                 <td style="width:140pt" align="right" width="186">8</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>JRockit 64-Bit with 64GB Compressed References</strong></td>
                 <td style="width:88pt" align="right" width="117">4</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">16</td>
                 <td style="width:87pt" align="right" width="116">16</td>
                 <td style="width:96pt" align="right" width="128">24</td>
                 <td style="width:135pt" align="right" width="180">16</td>
                 <td style="width:140pt" align="right" width="186">16</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>IBM 64-Bit with Compressed References</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">4</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">8</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">16</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>IBM 64-Bit with no reference compression</strong></td>
                 <td style="width:88pt" align="right" width="117">8</td>
                 <td style="width:96pt" align="right" width="128">8</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">24</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>IBM 32-Bit</strong></td>
                 <td style="width:88pt;padding-bottom:0cm; padding-top:0cm" align="right" width="117">4</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">4</td>
                 <td style="width:100pt;padding-bottom:0cm; padding-top:0cm" align="right" width="133">8</td>
                 <td style="width:87pt;padding-bottom:0cm; padding-top:0cm" align="right" width="116">8</td>
                 <td style="width:96pt;padding-bottom:0cm; padding-top:0cm" align="right" width="128">16</td>
                 <td style="width:135pt;padding-bottom:0cm; padding-top:0cm" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>UNKNOWN 32-Bit</strong></td>
                 <td style="width:88pt" align="right" width="117">4</td>
                 <td style="width:96pt" align="right" width="128">4</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">8</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
             <tr style="height:16.5pt;" height="22">
                 <td style="height:16.5pt;width:254pt" height="22" width="339"><strong>UNKNOWN 64-Bit</strong></td>
                 <td style="width:88pt" align="right" width="117">8</td>
                 <td style="width:96pt" align="right" width="128">8</td>
                 <td style="width:100pt" align="right" width="133">8</td>
                 <td style="width:87pt" align="right" width="116">8</td>
                 <td style="width:96pt" align="right" width="128">16</td>
                 <td style="width:135pt" align="right" width="180">0</td>
                 <td style="width:140pt" align="right" width="186">0</td>
             </tr>
         </tbody>
     </table>
     <p>ObjectAligment default: 8<br />
     MinimumObjectSize default equals ObjectAligment<br />
     ObjectHeaderSize default: PointerSize + JavaPointerSize<br />
     FIeldOffsetAdjustment default: 0<br />
     AgentSizeOfAdjustment default: 0<br />
     ReferenceSize equals JavaPointerSize<br />
     ArrayHeaderSize: ObjectHeaderSize + 4(INT Size)<br />
     JRockit and IBM JVM do not support ReflectionSizeOf</p>
     <br />
     而对基本类型，则因为虚拟机的规范，它们都是相同的，EHCache中采用PrimitiveType枚举类型来定义不同基本类型的长度：<br />
     <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
     <br />
     Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
     http://www.CodeHighlighter.com/<br />
     <br />
     --><span style="color: #0000FF; ">enum</span>&nbsp;PrimitiveType&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;BOOLEAN(<span style="color: #0000FF; ">boolean</span>.<span style="color: #0000FF; ">class</span>,&nbsp;1),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;BYTE(<span style="color: #0000FF; ">byte</span>.<span style="color: #0000FF; ">class</span>,&nbsp;1),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;CHAR(<span style="color: #0000FF; ">char</span>.<span style="color: #0000FF; ">class</span>,&nbsp;2),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;SHORT(<span style="color: #0000FF; ">short</span>.<span style="color: #0000FF; ">class</span>,&nbsp;2),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;INT(<span style="color: #0000FF; ">int</span>.<span style="color: #0000FF; ">class</span>,&nbsp;4),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;FLOAT(<span style="color: #0000FF; ">float</span>.<span style="color: #0000FF; ">class</span>,&nbsp;4),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;DOUBLE(<span style="color: #0000FF; ">double</span>.<span style="color: #0000FF; ">class</span>,&nbsp;8),<br />
     &nbsp;&nbsp;&nbsp;&nbsp;LONG(<span style="color: #0000FF; ">long</span>.<span style="color: #0000FF; ">class</span>,&nbsp;8);<br />
     <br />
     &nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Class&lt;?&gt;&nbsp;type;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size;<br />
     <br />
     &nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getReferenceSize()&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;CURRENT_JVM_INFORMATION.getJavaPointerSize();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;}<br />
     &nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getArraySize()&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;CURRENT_JVM_INFORMATION.getObjectHeaderSize()&nbsp;+&nbsp;INT.getSize();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;}<br />
     }</div>
     <br />
     反射计算一个实例（instance）占用内存大小（size）步骤如下：<br />
     a. 如果instance为null，size为0，直接返回。<br />
     b. 如果instance是数组类型，size为数组头部大小＋每个数组元素占用大小＊数组长度＋填充到对象对齐最小单位，最后保证如果size要比对象最小大小大过相等。<br />
     c. 如果instance是普通实例，size初始值为对象头部大小，然后找到对象对应类的所有继承类，从最顶层类开始遍历所有类（规则3），对每个类，纪录长整型和双精度型、整型和浮点型、短整型和字符型、布尔型和字节型以及引用类型的非静态字段的个数。如果整型和双精度型字段个数不为0，且当前size没有按长整型的大小对齐（规则5），选择部分其他类型字段排在长整型和双精度型之前，直到填充到以长整型大小对齐，然后按照先规则2的顺序排列个字计算不同类型字段的大小。在每个类之间如果没有按规定大小对齐，则填充缺少的字节（规则4）。在所有类计算完成后，如果没有按照类的对齐方式，则按类对齐规则对齐（规则1）。最后保证一个对象实例的大小要一个对象最小大小要大或相等。<br />
     <br />
     </li>
     <li>UnsafeSizeOf中<br />
     UnsafeSizeOf的实现比反射的实现要简单的多，它使用Sun内部库的Unsafe类来获取字段的offset值来计算一个类占用的内存大小（个人理解，这个应该只支持Sun JVM，但是怎么JRockit中有对FieldOffsetAdjustment的配置，而该方法只在这个类中被使用。。。）。对数组，它使用Unsafe.arrayBaseOffset()方法返回数组头大小，使用Unsafe.arrayIndexScale()方法返回一个数组元素占用的内存大小，其他计算和反射机制类似。这里在最后计算填充前有对FieldOffsetAdjustment的调整，貌似在JRockit JVM中使用到了，不了解为什么它需要这个调整。对实例大小的计算也比较简单，它首先遍历当前类和父类的所有非静态字段，通过Unsafe.objectFieldOffset()找到最后一个字段的offset，根据之前Java实例内存结构，要找到最后一个字段，只需从当前类到最顶层父类遍历第一个有非静态字段的类的所有非静态字段即可。在找到最后一个字段的offset以后也需要做FieldOffsetAdjustment调整，之后还需要加1（因为有对象对齐大小对齐，因而通过加1而避免考虑最后一个字段类型的问题，很巧妙的代码！）。最后根据规则以对对象以对象对齐大小对齐。<br />
     <br />
     </li>
     <li>AgentSizeOf<br />
     在Java 1.5以后，提供了Instrumentation接口，可以调用该接口的getObjectSize方法获取一个对象实例占用的内存大小。对Instrumentation的机制不熟，但是从EHCache代码的实现角度上，它首先需要有一个sizeof-agent.jar的包(包含在net.sf.ehcache.pool.sizeof中)，在该jar包的MANIFEST.MF文件中指定Premain-Class类，这个类实现两个静态的premain、agentmain方法。在实际运行时，EHCache会将sizeof-agent.jar拷贝到临时文件夹中，然后调用Sun工具包中的VirtualMachine的静态attach方法，获取一个VirtualMachine实例，然后调用其实例方法loadAgent方法，传入sizeof-agent.jar文件全路径，即可将一个SizeOfAgent类附着到当前实例中，而我们就可以通过SizeOfAgent类来获取它的Instrumentation实例来计算一个实例的大小。<br />
     </li>
</ol>
我们可以使用一下一个简单的例子来测试一下各种不同计算方法得出的结果：
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;EhcacheSizeOfTest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyClass&nbsp;ins&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;MyClass();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("ReflectionSizeOf:&nbsp;"&nbsp;+&nbsp;calculate(<span style="color: #0000FF; ">new</span>&nbsp;ReflectionSizeOf(),&nbsp;ins));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("UnsafeSizeOf:&nbsp;"&nbsp;+&nbsp;calculate(<span style="color: #0000FF; ">new</span>&nbsp;UnsafeSizeOf(),&nbsp;ins));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("AgentSizeOf:&nbsp;"&nbsp;+&nbsp;calculate(<span style="color: #0000FF; ">new</span>&nbsp;AgentSizeOf(),&nbsp;ins));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;calculate(SizeOf&nbsp;sizeOf,&nbsp;Object&nbsp;instance)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;sizeOf.sizeOf(instance);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;MyClass&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;a;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;c;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;d;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;e;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;f;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<span style="color: #008000; ">//</span><span style="color: #008000; ">输出结果如下(问题：这里的JVM是64-Bit HotSpot JVM with Compressed OOPs，它的实例头部占用了12个字节大小，但是它占用内存的大小还是和32位的大小一样，</span><span style="color: red;">这是为什么？</span><span style="color: #008000; ">)：</span><span style="color: #008000; "><br />
</span>[31&nbsp;23:21:19,598&nbsp;INFO&nbsp;]&nbsp;[main]&nbsp;sizeof.JvmInformation&nbsp;-&nbsp;Detected&nbsp;JVM&nbsp;data&nbsp;model&nbsp;settings&nbsp;of:&nbsp;64-Bit&nbsp;HotSpot&nbsp;JVM&nbsp;with&nbsp;Compressed&nbsp;OOPs<br />
ReflectionSizeOf:&nbsp;32<br />
UnsafeSizeOf:&nbsp;32<br />
[31&nbsp;23:26:52,479&nbsp;INFO&nbsp;]&nbsp;[main]&nbsp;sizeof.AgentLoader&nbsp;-&nbsp;Located&nbsp;valid&nbsp;'tools.jar'&nbsp;at&nbsp;'C:\Program&nbsp;Files\Java\jdk1.7.0_25\jre\..\lib\tools.jar'<br />
[31&nbsp;23:26:52,729&nbsp;INFO&nbsp;]&nbsp;[main]&nbsp;sizeof.AgentLoader&nbsp;-&nbsp;Extracted&nbsp;agent&nbsp;jar&nbsp;to&nbsp;temporary&nbsp;file&nbsp;C:\Users\DINGLE~1\AppData\Local\Temp\ehcache-sizeof-agent6171098352070763093.jar<br />
[31&nbsp;23:26:52,729&nbsp;INFO&nbsp;]&nbsp;[main]&nbsp;sizeof.AgentLoader&nbsp;-&nbsp;Trying&nbsp;to&nbsp;load&nbsp;agent&nbsp;@&nbsp;C:\Users\DINGLE~1\AppData\Local\Temp\ehcache-sizeof-agent6171098352070763093.jar<br />
AgentSizeOf:&nbsp;32</div>
<br />
<strong style="color: orange;">Deep SizeOf计算</strong><br />
EHCache中的SizeOf类中还提供了deepSize计算，它的步骤是：使用ObjectGraphWalker遍历一个实例的所有对象引用，在遍历中通过使用传入的SizeOfFilter过滤掉那些不需要的字段，然后调用传入的Visitor对每个需要计算的实例做计算。<br />
ObjectGraphWalker的实现算法和我之前所描述的类似，稍微不同的是它使用了Stack，我更倾向于使用Queue，只是这个也只是影响遍历的顺序，这里有点深度优先还是广度优先的味道。另外，它抽象了SizeOfFilter接口，可以用于过滤掉一些不想用于计算内存大小的字段，如Element中的key字段。SizeOfFilter提供了对类和字段的过滤：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;SizeOfFilter&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Returns&nbsp;the&nbsp;fields&nbsp;to&nbsp;walk&nbsp;and&nbsp;measure&nbsp;for&nbsp;a&nbsp;type</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Collection&lt;Field&gt;&nbsp;filterFields(Class&lt;?&gt;&nbsp;klazz,&nbsp;Collection&lt;Field&gt;&nbsp;fields);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Checks&nbsp;whether&nbsp;the&nbsp;type&nbsp;needs&nbsp;to&nbsp;be&nbsp;filtered</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;filterClass(Class&lt;?&gt;&nbsp;klazz);<br />
}</div>
SizeOfFilter的实现类可以用于过滤过滤掉@IgnoreSizeOf注解的字段和类，以及通过net.sf.ehcache.sizeof.filter系统变量定义的文件，读取其中的每一行为包名或字段名作为过滤条件。最后，为了性能考虑，它对一些计算结果做了缓存。<br />
<br />
ObjectGraphWalker中，它还会忽略一些系统原本就存在的一些静态变量以及类实例，所有这些信息都定义在FlyweightType类中。<br />
<br />
<strong style="color: orange;">SizeOfEngine类</strong><br />
SizeOfEngine是EHCache中对使用不同方式做SizeOf计算的抽象，如在计算内存中对象的大小需要使用SizeOf类来实现，而计算磁盘中数据占用的大小直接使用其size值即可，因而在EHCache中对SizeOfEngine有两个实现：DefaultSizeOfEngine和DiskSizeOfEngine。对DiskSizeOfEngine比较简单，其container参数必须是DiskMarker类型，并且直接返回其size字段即可；对DefaultSizeOfEngine，则需要配置SizeOfFilter和SizeOf子类实现问题，对SizeOfFilter，它会默认加入AnnotationSizeOfFilter、使用builtin-sizeof.filter文件中定义的类、字段配置的ResourceSizeOfFilter、用户通过net.sf.ehcache.sizeof.filter配置的filter文件的ResourceSizeOfFilter；对SizeOf的子类实现问题，它优先选择AgentSizeOf，如果不支持则使用UnsafeSizeOf，最后才使用ReflectionSizeOf。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;SizeOfEngine&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Size&nbsp;sizeOf(Object&nbsp;key,&nbsp;Object&nbsp;value,&nbsp;Object&nbsp;container);<br />
&nbsp;&nbsp;&nbsp;&nbsp;SizeOfEngine&nbsp;copyWith(<span style="color: #0000FF; ">int</span>&nbsp;maxDepth,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;abortWhenMaxDepthExceeded);<br />
}</div><h2>
还可以参考</h2>http://www.javamex.com/tutorials/memory/<br /><img src ="http://www.blogjava.net/DLevin/aggbug/405822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-11-01 11:03 <a href="http://www.blogjava.net/DLevin/archive/2013/11/01/405822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache-EHCache系列之AA-Tree实现溢出到磁盘的数据管理(2)</title><link>http://www.blogjava.net/DLevin/archive/2013/10/29/405728.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 28 Oct 2013 17:59:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/10/29/405728.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/405728.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/10/29/405728.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/405728.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/405728.html</trackback:ping><description><![CDATA[在上一篇《<a href="http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html">Java Cache-EHCache系列之AA-Tree实现溢出到磁盘的数据管理(1)</a>》已经详细讲解了EHCache中在AATreeSet中对AA Tree算法的实现，并且指出EHCache是采用它作为空闲磁盘管理数据结构，本文主要关注于EHCache是如何采用AATreeSet类来管理空闲磁盘的（这里的磁盘管理是只EHCache data文件中的空闲磁盘）。<br />
<br />
<strong style="color: orange;">空闲磁盘管理数据结构和算法</strong><br />
在上一篇《<a href="http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html">Java Cache-EHCache系列之AA-Tree实现溢出到磁盘的数据管理</a>》有提到类似内存分配管理，空闲磁盘管理可以采用多种数据结构，也有多种算法实现，EHCache采用AA Tree作为空闲磁盘的数据结构，以及首次适应算法和最坏适应算法相结合的算法。<br />
最坏适应算法（Worst Fit），在《计算机操作系统（汤子瀛版）》中对该算法描述如下：最坏适应分配算法要扫瞄整个空闲分区表或链表，总是挑选一个最大的空闲分区分分割给作业使用，其优点是可使剩下的空闲区不至于太小，产生碎片的几率最小，对中小作业有利，同时最坏适应算法查找效率很高。该算法要求将所有的空闲分区按其容量以从大到小的顺序形成一空闲分区链，查找时只要看第一个分区能否满足作业要求。但该算法的缺点也时明显的，它会使存储器中缺乏大的空闲分区。最坏适应算法与前面所述的首次适应算法、循环首次适应算法、最佳适应算法一起被成为顺序搜索算法。 <br />
首次适应算法在《计算机操作系统（汤子瀛版）》描述如下：我们以空闲分区链为例来说明采用FF算法时的分配情况。FF算法要求空闲分区链以地址递增次序链接。在分配内存时，从链首开始顺序查找，直到找到一个大小能满足要求的空闲分区为止；然后再按照作业的大小，从该分区中划出一块内存空间分配给请求者，余下的空闲分区仍留在空闲链中。若从链首直至链尾都不能找到一个满足要求的分区，则此次内存分配失败，返回。该算法倾向于有限利用内存中低地址部分的空闲分区，从而保留了高地址部分的大空闲区。这给尾以后到达的大作业分配大的内存空间创造了条件。其缺点时低地址部分不断被划分，会留下许多难以利用的、很小的空闲分区，而每次查找右都时从低地址部分开始，这无疑会增加查找可用空闲分区时的开销。<br />
<br />
作为Cache的溢出数据文件作为Cache的交换区，显然我们希望数据文件越小越好，此时一般的选择是使用首次适应算法（First Fit）。然而虽然FF算法能尽可能多的利用数据文件低地址部分的磁盘空间以减少磁盘文件的大小，但是它的缺点也是明显的，而WF算法虽然效率很高，但是它很容易使数据文件膨胀导致磁盘利用率很低。因而EHCache的空闲磁盘去管理时采用了两种结合的方法：即空闲链（AA Tree链）的以磁盘地址顺序排列，树的每个节点包含一个字段用于纪录当前节点以及其所有子节点中的最大size，在查找时，以层级顺序遍历，并且优先选择左子树（磁盘地址低的Region）。<br />
<br />
EHCache将一个空闲磁盘区抽象成一个Region类，它包含start、end字段，用于纪录在当前该区域在磁盘文件中的其实位置；并且每个Region实例是AA Tree中的一个节点，因而它继承自AATreeSet.AbstractTreeNode类，即继承了该类的left、right、level字段；根据Region的比较算法，它大致上以Region所在磁盘文件的位置排序（而不是以Region的大小来排序），因而为了提升查找性能，它还包含了一个long类型的contiguous字段，该单词字面意思是&#8220;临近的、连续的&#8221;，用于表示该当前Region临近节点的区域的最大Region大小，即该字段表示当前Region以及其所有子节点的最大Region的大小，从而当在查找时，只有如果要查找的size比当前Region的contiguous字段要大的话，就可以不用继续查找其子节点了，并且通过该字段也实现了最坏适应算法。在每次更新左子节点和右子节点时都会调整contiguous的大小，在新创建一个Region节点时也会更新contiguous字段大小，从而保证当前Region中的contiguous始终是其所有子节点的最大size，对于叶子节点，其contiguous的值是当前Region的size。在计算size时，start，end值是闭合的。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;AbstractTreeNode&lt;E&gt;&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Node&lt;E&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;E&gt;&nbsp;left;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;E&gt;&nbsp;right;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;level;<br />
&nbsp; &nbsp; .....<br />
}<br />
<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Region&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;AATreeSet.AbstractTreeNode&lt;Comparable&gt;&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Comparable&lt;Comparable&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;start;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;end;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;contiguous;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Region(<span style="color: #0000FF; ">long</span>&nbsp;start,&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;end)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.start&nbsp;=&nbsp;start;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;=&nbsp;end;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;contiguous()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(getLeft().getPayload()&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>&nbsp;&amp;&amp;&nbsp;getRight().getPayload()&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;contiguous;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;updateContiguous()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Region&nbsp;left&nbsp;=&nbsp;(Region)&nbsp;getLeft().getPayload();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Region&nbsp;right&nbsp;=&nbsp;(Region)&nbsp;getRight().getPayload();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;leftContiguous&nbsp;=&nbsp;left&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>&nbsp;?&nbsp;0&nbsp;:&nbsp;left.contiguous();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;rightContiguous&nbsp;=&nbsp;right&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>&nbsp;?&nbsp;0&nbsp;:&nbsp;right.contiguous();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;contiguous&nbsp;=&nbsp;Math.max(size(),&nbsp;Math.max(leftContiguous,&nbsp;rightContiguous));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLeft(AATreeSet.Node&lt;Comparable&gt;&nbsp;l)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>.setLeft(l);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRight(AATreeSet.Node&lt;Comparable&gt;&nbsp;r)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>.setRight(r);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;size()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;since&nbsp;it&nbsp;is&nbsp;all&nbsp;inclusive</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;(isNull()&nbsp;?&nbsp;0&nbsp;:&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;-&nbsp;<span style="color: #0000FF; ">this</span>.start&nbsp;+&nbsp;1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">Region的比较算法，使Region在这棵AA&nbsp;Tree中大致保持其在数据文件中的排序顺序</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;compareTo(Comparable&nbsp;other)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(other&nbsp;<span style="color: #0000FF; ">instanceof</span>&nbsp;Region)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;compareTo((Region)&nbsp;other);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(other&nbsp;<span style="color: #0000FF; ">instanceof</span>&nbsp;Long)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;compareTo((Long)&nbsp;other);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;AssertionError("Unusual&nbsp;Type&nbsp;"&nbsp;+&nbsp;other.getClass());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;compareTo(Region&nbsp;r)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.start&nbsp;&gt;&nbsp;r.start&nbsp;||&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;&gt;&nbsp;r.end)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.start&nbsp;&lt;&nbsp;r.start&nbsp;||&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;&lt;&nbsp;r.end)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;-1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;compareTo(Long&nbsp;l)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(l.longValue()&nbsp;&gt;&nbsp;end)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;-1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(l.longValue()&nbsp;&lt;&nbsp;start)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
<strong style="color: orange;"><br />
EHCache中对空闲磁盘的分配与回收</strong><br />
在C语言中使用malloc和free来对内存的分配与回收，在C＋＋中使用new和delete，在Java中只有new，在EHCache中则将磁盘空间的分配与回收抽象成FileAllocationTree类，它提供alloc、free、mark等接口用于管理磁盘区的分配与回收。另外EHCache还增加了RegionSet类，它继承子AATreeSet类，用于表达它专门用于存储Region节点。这里吐槽一下，FileAllocationTree竟然设计成继承自RegionSet而不是组合。。。。。所有这些类的结构图如下： <br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/EHCacheODS.jpg" alt="" height="339" width="477" /><br />
<br />
<strong style="color: purple;">磁盘的分配</strong><br />
磁盘的分配分成一下几个步骤（逻辑比较简单，就不贴代码了）：<br />
<ol style="margin-top: 1px;">
     <li>根据传入的size，在AA Tree中查找到一个可以容纳传入size大小的Region节点，并将找到的Region的前size部分分配出一个新的Region并返回。<br />
     查找逻辑在RegionSet类中实现(find方法)，它从root节点向下查找，因为root节点的contiguous字段保存了整棵树的最大size，因而先检查root节点的contiguous，如果size比root的Contiguous要大，则抛异常，因为整棵树中已经没有比传入的size要大的Region。然后层级遍历AA树，如果当前节点的size要比传入的size大或相等，则找到足以容纳传入size大小的Region节点，以当前节点的size大小的前部分新创建一个Region返回；否则如果它的左子树的contiguous字段要比传入size大，则向左子树查找；否则如果它的右子树的contiguous字段要比传入的size大，则向右子树查找；否则，抛出异常，因为左右子树都找不到可以容纳size大小的Region。</li>
     <li>将新创建的Region实例mark成已经使用（这个新创建的Region的start和AA树中某个Region节点的start值一样，而end大小则不一定一样）。<br />
     因为这个新创建的Region实例是要从AA树中的某个节点分出部分空间，因而首先要将AA树中的那个节点从树中移除，然后如果树中移除的节点的end值和新创建的Region的end值一样，则直接移除就可以了，否则，要将树种移除的节点剩余部分的重新创建一个Region插回树中。从代码的角度，首先以新创建的Region的start值找到树中对应的Region（Region中接收Long作为参数的compare方法的存在以实现这种方式的查找），将其移除并返回移除后的Region（removeAndReturn方法，在RegionSet中重新拷贝一个Region实例是为了防止通过R返回的Region改变树内部的状态，因为Region即作为一个Node也作为payload存在，同时也可以给接下来的插入提供新的Region节点），然后把将新创建的Region从原树中的Region中移除，这里的移除逻辑假设新创建的Region可以是原树中的Region的前部分、中间部分以及末位部分，作为前部分和末位部分，因为Region是新创建的节点，因而直接更新当前节点即可，而如果是中间部分，则前部分作为当前节点，而后部分作为新节点返回。最后，如果原树中节点还剩余部分数据作为新的空闲磁盘区添回到空闲磁盘树中。最后，检查是否需要增加文件大小，这里只需要更新文件大小的字段即可而不需要实际增加文件的大小，因为文件在写入时会自动增加大小。Region中移除其中的部分Region代码如下：<br />
     <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
     <br />
     Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
     http://www.CodeHighlighter.com/<br />
     <br />
     -->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;Region&nbsp;remove(Region&nbsp;r)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IllegalArgumentException&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(r.start&nbsp;&lt;&nbsp;<span style="color: #0000FF; ">this</span>.start&nbsp;||&nbsp;r.end&nbsp;&gt;&nbsp;<span style="color: #0000FF; ">this</span>.end)&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;IllegalArgumentException("Ranges&nbsp;:&nbsp;Illegal&nbsp;value&nbsp;passed&nbsp;to&nbsp;remove&nbsp;:&nbsp;"&nbsp;+&nbsp;<span style="color: #0000FF; ">this</span>&nbsp;+&nbsp;"&nbsp;remove&nbsp;called&nbsp;for&nbsp;:&nbsp;"&nbsp;+&nbsp;r);<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.start&nbsp;==&nbsp;r.start)&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.start&nbsp;=&nbsp;r.end&nbsp;+&nbsp;1;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.end&nbsp;==&nbsp;r.end)&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;=&nbsp;r.start&nbsp;-&nbsp;1;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Region&nbsp;newRegion&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;Region(r.end&nbsp;+&nbsp;1,&nbsp;<span style="color: #0000FF; ">this</span>.end);<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;=&nbsp;r.start&nbsp;-&nbsp;1;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;newRegion;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
     &nbsp;&nbsp;&nbsp;&nbsp;}</div>
     </li>
     <li>最后将分配出来的Region实例返回，并纪录在DiskMarker中，在以后需要将磁盘中的数据重新读取到内存中时用于定位该数据在磁盘中的位置，并可以将该Regin回收。</li>
</ol>
<strong style="color: purple;">磁盘的回收<br />
</strong>磁盘的回收也分几个步骤来完成：<br />
<ol style="margin-top: 1px;">
     <li>对要回收的Region，查找在当前树中是否有一个Region节点其start和要回收的Region的(end - 1)值相等，如果有，则删除树中的原节点并返回，合并这两个节点，将合并后的节点重新插入到树中。Region中合并的代码逻辑如下：<br />
     <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
     <br />
     Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
     http://www.CodeHighlighter.com/<br />
     <br />
     -->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;merge(Region&nbsp;r)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IllegalArgumentException&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.start&nbsp;==&nbsp;r.end&nbsp;+&nbsp;1)&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.start&nbsp;=&nbsp;r.start;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(<span style="color: #0000FF; ">this</span>.end&nbsp;==&nbsp;r.start&nbsp;-&nbsp;1)&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.end&nbsp;=&nbsp;r.end;<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;IllegalArgumentException("Ranges&nbsp;:&nbsp;Merge&nbsp;called&nbsp;on&nbsp;non&nbsp;contiguous&nbsp;values&nbsp;:&nbsp;[this]:"&nbsp;+&nbsp;<span style="color: #0000FF; ">this</span>&nbsp;+&nbsp;"&nbsp;and&nbsp;"&nbsp;+&nbsp;r);<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateContiguous();<br />
     &nbsp;&nbsp;&nbsp;&nbsp;}</div>
     </li>
     <li>对要回收的Region（或合并后的Region），继续查找当前树是否有一个Region节点，其end和要回收的（或已合并的）Region的(start - 1)的值相等，如果有，则删除树中的原节点并返回，合并这两个节点，将合并后的节点继续插入树中。</li>
     <li>如果在树中找不到可以和回收的Region合并的Region节点，则只是将要合并的Region添加到树中。</li>
     <li>最后如果回收后数据文件可以减小，更新数据文件大小的字段，并将数据文件的缩小，从而保持数据文件处于尽量小的状态。<br />
     </li>
</ol>
最后我写了一个简单的测试程序，对磁盘分配与释放做了一些随机的模拟，以增加一些直观的感受(类似DiskStorageFactory，在创建FileAllocationTree时，给了Long.MAX_VALUE的初始值，我这也给这个值作为初始值，从而保证基本上在所有情况下，都能找到一个合适的Region节点，也就是说FileAllocationTree不用来控制数据文件的大小，数据文件的大小由其他逻辑来控制，这在后面会详细讲解)：&nbsp;<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;FileAllocationTreeTest&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;main(String[]&nbsp;args)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;count&nbsp;=&nbsp;5;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random&nbsp;random&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;Random();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileAllocationTree&nbsp;alloc&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;FileAllocationTree(Long.MAX_VALUE,&nbsp;<span style="color: #0000FF; ">null</span>);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Region&gt;&nbsp;allocated&nbsp;=&nbsp;Lists.newArrayList();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>(<span style="color: #0000FF; ">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;count;&nbsp;i++)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size&nbsp;=&nbsp;random.nextInt(1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Region&nbsp;region&nbsp;=&nbsp;alloc.alloc(size);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("new&nbsp;size:&nbsp;"&nbsp;+&nbsp;size&nbsp;+&nbsp;",&nbsp;"&nbsp;+&nbsp;toString(region)&nbsp;+&nbsp;",&nbsp;filesize:&nbsp;"&nbsp;+&nbsp;alloc.getFileSize()&nbsp;+&nbsp;",&nbsp;allocator:&nbsp;"&nbsp;+&nbsp;toString(alloc));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allocated.add(region);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>(<span style="color: #0000FF; ">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;count;&nbsp;i++)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size&nbsp;=&nbsp;random.nextInt(1000);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Region&nbsp;region&nbsp;=&nbsp;alloc.alloc(size);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("new&nbsp;size:&nbsp;"&nbsp;+&nbsp;size&nbsp;+&nbsp;",&nbsp;"&nbsp;+&nbsp;toString(region)&nbsp;+&nbsp;",&nbsp;filesize:&nbsp;"&nbsp;+&nbsp;alloc.getFileSize()&nbsp;+&nbsp;",&nbsp;allocator:&nbsp;"&nbsp;+&nbsp;toString(alloc));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allocated.add(region);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;region&nbsp;=&nbsp;allocated.get(random.nextInt(allocated.size()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alloc.free(region);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;allocated.remove(region);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Freed&nbsp;region:&nbsp;"&nbsp;+&nbsp;toString(region)&nbsp;+&nbsp;",&nbsp;after&nbsp;file&nbsp;size:&nbsp;"&nbsp;+&nbsp;alloc.getFileSize()&nbsp;+&nbsp;",&nbsp;allocator:&nbsp;"&nbsp;+&nbsp;toString(alloc));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;String&nbsp;toString(FileAllocationTree&nbsp;alloc)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;builder&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;StringBuilder("[");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>(Region&nbsp;region&nbsp;:&nbsp;alloc)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;builder.append(toString(region)).append(",&nbsp;");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;builder.replace(builder.length()&nbsp;-&nbsp;2,&nbsp;builder.length()&nbsp;-&nbsp;1,&nbsp;"]");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;builder.toString();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;String&nbsp;toString(Region&nbsp;region)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;"Regin("&nbsp;+&nbsp;region.start()&nbsp;+&nbsp;",&nbsp;"&nbsp;+&nbsp;region.end()&nbsp;+&nbsp;")";<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</div>
输出的随机结果如下：&nbsp;<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->new&nbsp;size:&nbsp;397,&nbsp;Regin(0,&nbsp;396),&nbsp;filesize:&nbsp;397,&nbsp;allocator:&nbsp;[Regin(397,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;175,&nbsp;Regin(397,&nbsp;571),&nbsp;filesize:&nbsp;572,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;210,&nbsp;Regin(572,&nbsp;781),&nbsp;filesize:&nbsp;782,&nbsp;allocator:&nbsp;[Regin(782,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;11,&nbsp;Regin(782,&nbsp;792),&nbsp;filesize:&nbsp;793,&nbsp;allocator:&nbsp;[Regin(793,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;432,&nbsp;Regin(793,&nbsp;1224),&nbsp;filesize:&nbsp;1225,&nbsp;allocator:&nbsp;[Regin(1225,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;226,&nbsp;Regin(1225,&nbsp;1450),&nbsp;filesize:&nbsp;1451,&nbsp;allocator:&nbsp;[Regin(1451,&nbsp;9223372036854775806)]&nbsp;<br />Freed&nbsp;region:&nbsp;Regin(572,&nbsp;781),&nbsp;after&nbsp;file&nbsp;size:&nbsp;1451,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(1451,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;500,&nbsp;Regin(1451,&nbsp;1950),&nbsp;filesize:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />Freed&nbsp;region:&nbsp;Regin(793,&nbsp;1224),&nbsp;after&nbsp;file&nbsp;size:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(793,&nbsp;1224),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;681,&nbsp;Regin(1951,&nbsp;2631),&nbsp;filesize:&nbsp;2632,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(793,&nbsp;1224),&nbsp;Regin(2632,&nbsp;9223372036854775806)]&nbsp;<br />Freed&nbsp;region:&nbsp;Regin(1951,&nbsp;2631),&nbsp;after&nbsp;file&nbsp;size:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(793,&nbsp;1224),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;23,&nbsp;Regin(793,&nbsp;815),&nbsp;filesize:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(572,&nbsp;781),&nbsp;Regin(816,&nbsp;1224),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />Freed&nbsp;region:&nbsp;Regin(0,&nbsp;396),&nbsp;after&nbsp;file&nbsp;size:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(0,&nbsp;396),&nbsp;Regin(572,&nbsp;781),&nbsp;Regin(816,&nbsp;1224),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />new&nbsp;size:&nbsp;109,&nbsp;Regin(816,&nbsp;924),&nbsp;filesize:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(0,&nbsp;396),&nbsp;Regin(572,&nbsp;781),&nbsp;Regin(925,&nbsp;1224),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;<br />Freed&nbsp;region:&nbsp;Regin(1225,&nbsp;1450),&nbsp;after&nbsp;file&nbsp;size:&nbsp;1951,&nbsp;allocator:&nbsp;[Regin(0,&nbsp;396),&nbsp;Regin(572,&nbsp;781),&nbsp;Regin(925,&nbsp;1450),&nbsp;Regin(1951,&nbsp;9223372036854775806)]&nbsp;</div><img src ="http://www.blogjava.net/DLevin/aggbug/405728.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-10-29 01:59 <a href="http://www.blogjava.net/DLevin/archive/2013/10/29/405728.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache-EHCache系列之AA-Tree实现溢出到磁盘的数据管理(1)</title><link>http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sun, 27 Oct 2013 11:13:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/405683.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/405683.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/405683.html</trackback:ping><description><![CDATA[在EHCache中，如果设置了overflowToDisk属性，当Cache中的数据超过限制时，EHCache会根据配置的溢出算法（先进先出（FIFO）、最近最少使用算法（LRU）等），选择部分实例，将这些实例的数据写入到磁盘文件中临时存储，已减少内存的负担，当内存中的实例数减少（因为超时或手工移除）或某些实例被使用到时，又可以将这些写入磁盘的数据重新加载到内存中。这个过程包含实例的序列化和反序列化，以及对磁盘文件不同区域的读写管理，这篇文章主要关注于磁盘文件不同区域的读写管理。<br />
<br />
<strong style="color: orange;">需求思考<br />
</strong>当Cache系统发现需要溢出某些实例到磁盘时，它需要把实例序列化后，将序列化后的二进制数据写入磁盘，并释放内存中的数据；当用户请求读取已经溢出到磁盘中的实例时，需要读取磁盘中的二进制数据，反序列化成内存实例，返回给用户，由于该实例最近一次被访问，因而根据程序的局部性（或许这里是数据的局部性）原理，Cache会将新读取的实例保存在内存中，并释放是磁盘中占用的空间；如果此时或者在将来的某个时候内存依然或变的非常紧张，Cache系统会根据溢出算法溢出根据算法得到的实例到磁盘，因而这里磁盘的读写是一个动态的、周而复始的行为。在把序列化的实例写入磁盘时，我们需要分配相同大小的磁盘空间，并纪录该实例的key、在磁盘文件中的起始位置、数据大小用于在后来的读回数据并反序列化成内存实例；当一个实例数据被读回内存时，需要释放它在磁盘中占用的内存，从而可以将它原本占用磁盘分配给其他溢出实例。这有点类似内存管理的味道：对一台机器来说，内存大小是有限的，而进程不断的产生、销毁，在启动一个进程时，需要为该进程分配一定的初始空间，并且随着进程的运行，需要不断的为其分配运行期间增长的内存空间；当一个进程销毁时，操作系统需要回收分配给它的空间，以实现循环利用。在操作系统中，内存管理方式有：<br />
1. 固定分区分配，操作系统把内存分成固定大小的相等的或不等的内存分区，每个进程只能分配给这几个固定大小的分区。这是一种最简单的内存管理算法，它需要提前直到一个进程最大会使用的内存大小，并且需要合理分配内存分区的大小，以防止有些进程因为使用内存过多而找不到对应大小的空闲分区而无法载入，虽然此时可能内存还有很多其他比较小的空闲分区，它也要防止内存分区过大，从而在载入过小进程时浪费内存。对这种内存管理方式，只需要一张分区表即可，每个表项纪录了分区号、起始位置、大小、分配状态等。如果表项不多，可以直接用数组表示，实现简单，查找效率影响也不大，如果表项很多，则可以考虑在其上层建立一颗空闲分区红黑树，已提升查找、插入、删除效率。<br />
2. 相对于固定分区分配，自然会存在动态内存分配，已处理不同内存需要不同大小的内存的需求，即操作系统根据不同进程所需的不同内存大小分配内存。可以使用三种数据结构来管理分区：空闲分区表、空闲分区链以及空闲分区红黑树，对于空闲分区表和空闲分区红黑树，每一个节点都纪录了空闲分区号、起始位置、大小等，不同的是分区表组成一个数组，而空闲分区红黑树组成一颗树已提升查找、插入、删除等性能，对空闲分区链，它不象空闲分区表和空闲分区红黑树，使用额外的内存中间存储信息，它直接使用在空闲内存的头和尾存储额外的分区链信息。而对分区分配算法，可以使用：首次适应算法、循环首次适应算法、最佳适应算法、最坏适应算法等。在分配内存时，首先根据一定的算法找到可以装载该进程的空闲分区，然后根据当前分区的大小和进程所需要的内存大小以及最小可存在的内存分区的大小（防止过多的过小的内存碎片产生）选择将当前空闲分区的部分分区划分给该新进程或将当前整个分区分配给新进程，如果只是将当前空闲分区的部分分区划分给新进程，只需调整当前空闲分区项的基本信息即可，否则需要将该空闲进程节点从空闲分区表、链、树中移除。内存回首时，根据不同的算法将该内存区创建出一个空闲分区节点，将它插入到原有空闲分区表、链、树中，在插入前，如果出现相邻的空闲分区表，则需要将所有的相邻空闲分区合并。<br />
3. 伙伴系统。<br />
4. 可重定位分区分配。<br />
貌似有点离题了，作为Cache的溢出数据磁盘文件管理为了简单和效率的考虑，不会也没必要象内存管理一样实现的那么复杂，因而伙伴系统和可重定为分区分配不细讲了，需要进一步了解的可以参考任意一本的操作系统教材。其实Cache的溢出数据磁盘文件管理更类似于内存溢出到磁盘的交换区管理，即操作系统会将暂时不用的进程或数据调换到外存中，已腾出内存供其他需要进程使用，已提升内存使用率。一般进程或内存交换出的数据在交换区中的停留时间比较段，而交换操作又比较频繁，因而交换区中的空间管理主要为了提高进程或数据换入和换出的速度，为此，一般采用的是连续分配方式，较少考虑外存碎片的问题，而对其管理的数据结构和上述的数据结构类似，不再赘述。 <br />
<br />
<strong style="color: orange;">EHCache使用AA Tree数据结构来管理磁盘空间</strong><br />
在EHCache中，采用AA Tree数据结构来纪录空闲磁盘块，因而首先了解一下AA Tree的实现（AATreeSet类）。AA Tree出自Arne Andersson在1993年的论文&#8220;Balanced Search Trees Made Simple&#8221;中提出的对红黑树的改进，因而它是红黑树的变种，但是比红黑树实现要简单。不同于红黑树采用颜色（红色或黑色）来纪录一个节点的状态以调整节点的位置来保持其平衡性，AA Tree采用level值来纪录当前节点的状态以保持它的平衡性，level值相当于红黑树中的黑节点高度。AA Tree的定义如下：<br />
1. AA Tree是红黑树的变种，因而它也是一颗二叉排序树。<br />
2. 每个叶子节点的level是1。<br />
3. 每个左孩子的level是其父节点level值减1。<br />
4. 每个右孩子的level和其父节点的level相等或是其父节点的level减1。<br />
5. 每个右孙子的level一定比其祖父节点的level要小。<br />
6. 每个level大于1的节点有两个孩子。<br />
类似红黑树，AA Tree通过对level值的判断来调整节点位置以保证当前Tree维持以上定义。由于AA Tree采用level来纪录每个节点的状态，并且每个右孩子的level和其父节点的level值相等或是其父节点的level减1，因而一般AA Tree采用如下方式表达（图片来自：http://blog.csdn.net/zhaojinjia/article/details/8121156）：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree事例.jpg" alt="" height="169" width="588" /><br />
根据以上AA Tree的定义，AA Tree禁止以下两种情况的出现：<br />
1. 禁止出现连续两个水平方向链（horizontal link），所谓horizontal link是指一个结点跟它的右孩子结点的level相同（左孩子结点永远比它的父结点level小1）。这个规定其实相当于RB树中不能出现两个连续的红色结点。即需要满足定义5。<br />
2. 禁止出现向左的水平方向链（left horizontal link），也就是说一个结点最多只能出现一次向右的水平方向链。在AA Tree中水平链相当于红黑树中的红孩子，根据定义3，AA Tree不允许左孩子出现&#8220;红节点&#8221;，即AA Tree的左孩子的level值总是要比父节点的level值小1。<br />
对不允许的情况如下图所示（图片同样来自：http://blog.csdn.net/zhaojinjia/article/details/8121156）：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree不允许的事例.jpg" alt="" height="207" border="0" width="592" /><br />
<br />
<span style="color: orange;"><strong>AA Tree的节点调整</strong></span><br />
在AA Tree中，只需要当出现以上禁止的两种情况下才需要调整，因而它比红黑树的逻辑要简单的多。同红黑树，AA Tree也通过旋转节点来调整节点以使当前树始终保持AA Tree的特点，然而AA Tree创建了自己的术语：skew和split。skew用于处理出现向左的水平方向链的情况；split用于处理连续两个水平方向链的情况。<br />
skew相当于一个右旋操作：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATreeSkew操作.gif" alt="" height="202" width="292" /><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;&lt;T&gt;&nbsp;Node&lt;T&gt;&nbsp;skew(Node&lt;T&gt;&nbsp;top)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top.getLeft().getLevel()&nbsp;==&nbsp;top.getLevel()&nbsp;&amp;&amp;&nbsp;top.getLevel()&nbsp;!=&nbsp;0)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Node&lt;T&gt;&nbsp;save&nbsp;=&nbsp;top.getLeft();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setLeft(save.getRight());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;save.setRight(top);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;save;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;top;<br />
&nbsp;&nbsp;}</div>
split相当于一个左旋操作，并且它还会使父节点的level加1：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATreeSplit操作.gif" alt="" height="204" width="292" /><br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;&lt;T&gt;&nbsp;Node&lt;T&gt;&nbsp;split(Node&lt;T&gt;&nbsp;top)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top.getRight().getRight().getLevel()&nbsp;==&nbsp;top.getLevel()&nbsp;&amp;&amp;&nbsp;top.getLevel()&nbsp;!=&nbsp;0)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Node&lt;T&gt;&nbsp;save&nbsp;=&nbsp;top.getRight();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setRight(save.getLeft());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;save.setLeft(top);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;save;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.incrementLevel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;top;<br />
&nbsp;&nbsp;}</div>当skew之前已经有一个右孩子的level跟当前结点的level相同，skew 操作之后可能引起两个连续水平方向链，这时需要配合使用split操作。split操作会新的子树的根节点level增加1（原节点的右孩子），当原节点作为其父结点的右孩子而且其父结点跟祖父结点的level相同时，在split操作后会引起两个连续水平方向链，此时需要进一步的split操作，而当原节点作为其父节点的左孩子时，在split操作后会引起向左方向的水平链，此时需要配合使用skew操作；因而AA Tree在做调整时，需要skew和split一起配合操作。
<br /><br />
<span style="color: orange;"><strong>AA Tree节点定义</strong></span><br />
AA Tree节点定义和红黑树节点定义类似，只是它没有了color字段，而使用level字段替代。在EHCache中采用Node接口表达，在该接口中它还定义了compare()方法以比较两个节点之间的大小关系：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Node&lt;E&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;compareTo(E&nbsp;data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLeft(Node&lt;E&gt;&nbsp;node);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRight(Node&lt;E&gt;&nbsp;node);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Node&lt;E&gt;&nbsp;getLeft();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Node&lt;E&gt;&nbsp;getRight();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getLevel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLevel(<span style="color: #0000FF; ">int</span>&nbsp;value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;decrementLevel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;incrementLevel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;swapPayload(Node&lt;E&gt;&nbsp;with);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;E&nbsp;getPayload();<br />
&nbsp;&nbsp;}</div>
对该接口的实现也比较简单：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;AbstractTreeNode&lt;E&gt;&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Node&lt;E&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;E&gt;&nbsp;left;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;E&gt;&nbsp;right;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;level;<br />
&nbsp; &nbsp; .....<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;TreeNode&lt;E&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Comparable&gt;&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;AbstractTreeNode&lt;E&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;E&nbsp;payload;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;swapPayload(Node&lt;E&gt;&nbsp;node)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(node&nbsp;<span style="color: #0000FF; ">instanceof</span>&nbsp;TreeNode&lt;?&gt;)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TreeNode&lt;E&gt;&nbsp;treeNode&nbsp;=&nbsp;(TreeNode&lt;E&gt;)&nbsp;node;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E&nbsp;temp&nbsp;=&nbsp;treeNode.payload;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;treeNode.payload&nbsp;=&nbsp;<span style="color: #0000FF; ">this</span>.payload;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.payload&nbsp;=&nbsp;temp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;IllegalArgumentException();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp; &nbsp; .....<br />
&nbsp; }</div>
<br />
<strong style="color: orange;">AA Tree的插入操作</strong><br />
同红黑树，作为一颗二叉排序树，AA Tree先以二叉排序树的算法插入一个新节点，然后对插入的节点做skew和split调整，以维持AA Tree的定义，它是一个递归操作，因而可以保证在当前一次调整完成后还需要再调整时，在上一个递归栈中可以继续调整（这里比较使用当前节点和传入数据做比较，因而当direction大于0时表示当前节点要比传入数据大，因而应该插入它的左子树，我刚开始没仔细看，还以为这是一颗倒序树，囧。。。。）：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;T&gt;&nbsp;insert(Node&lt;T&gt;&nbsp;top,&nbsp;T&nbsp;data)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top&nbsp;==&nbsp;terminal())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mutated&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;createNode(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;direction&nbsp;=&nbsp;top.compareTo(data);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(direction&nbsp;&gt;&nbsp;0)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setLeft(insert(top.getLeft(),&nbsp;data));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(direction&nbsp;&lt;&nbsp;0)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setRight(insert(top.getRight(),&nbsp;data));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;top;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;skew(top);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;split(top);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;top;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}</div>
<br />
<strong style="color: orange;">AA Tree的删除操作</strong><br />AA Tree节点的删除也只需考虑两种情况：<br />1. 如果删除节点的后继节点不是叶子节点，将后继节点的值赋给当前节点，后继节点右孩子的值赋给后继节点，删除后继节点。<br />如下图，要删除30，其后继节点是35，删除时，将30和35节点交换，将35和40节点交换，后删除40节点（以下图片都出自：http://blog.csdn.net/zhaojinjia/article/details/8121156）：<br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree删除节点的后继节点不是叶子节点删除前.jpg" alt="" height="187" width="615" /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree删除节点的后继节点不是叶子节点删除后.jpg" alt="" height="182" width="612" /><br />2. 如果删除节点的后继节点是叶子节点，后继节点的值赋给当前节点，并将后继节点的父节点level值减1，如果出现向左的水平链或向右的连续水平链，则做skew和split调整。<br />如下面两图分别为删除50节点和删除15节点的事例图：<br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree删除节点后继节点为叶子节点删除前.jpg" alt="" height="122" width="441" /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree删除节点后继节点为叶子节点删除后.jpg" alt="" height="116" width="419" /><br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/AATree删除节点后继节点为叶子节点事例2.jpg" alt="" height="361" width="613" /><br />EHCache AATreeSet中删除的代码（这里的比较也是使用当前节点和传入数据比较，因而当direction大于0时，应该向左走，要特别留意）：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Node&lt;T&gt;&nbsp;remove(Node&lt;T&gt;&nbsp;top,&nbsp;T&nbsp;data)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top&nbsp;!=&nbsp;terminal())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;direction&nbsp;=&nbsp;top.compareTo(data);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;heir&nbsp;=&nbsp;top;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(direction&nbsp;&gt;&nbsp;0)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setLeft(remove(top.getLeft(),&nbsp;data));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item&nbsp;=&nbsp;top;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setRight(remove(top.getRight(),&nbsp;data));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top&nbsp;==&nbsp;heir)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(item&nbsp;!=&nbsp;terminal()&nbsp;&amp;&amp;&nbsp;item.compareTo(data)&nbsp;==&nbsp;0)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mutated&nbsp;=&nbsp;<span style="color: #0000FF; ">true</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.swapPayload(top);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;removed&nbsp;=&nbsp;top.getPayload();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;top.getRight();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top.getLeft().getLevel()&nbsp;&lt;&nbsp;top.getLevel()&nbsp;-&nbsp;1&nbsp;||&nbsp;top.getRight().getLevel()&nbsp;&lt;&nbsp;top.getLevel()&nbsp;-&nbsp;1)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(top.getRight().getLevel()&nbsp;&gt;&nbsp;top.decrementLevel())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.getRight().setLevel(top.getLevel());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;skew(top);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setRight(skew(top.getRight()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.getRight().setRight(skew(top.getRight().getRight()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top&nbsp;=&nbsp;split(top);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top.setRight(split(top.getRight()));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;top;<br />&nbsp;&nbsp;}</div><br />
参考文章：<br />
<div>http://blog.csdn.net/zhaojinjia/article/details/8121156</div>
<div>http://www.cnblogs.com/ljsspace/archive/2011/06/16/2082240.html<br />
<div>http://baike.baidu.com/view/10841337.htm</div>
</div><img src ="http://www.blogjava.net/DLevin/aggbug/405683.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-10-27 19:13 <a href="http://www.blogjava.net/DLevin/archive/2013/10/27/405683.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache系列之Cache概述和Simple Cache</title><link>http://www.blogjava.net/DLevin/archive/2013/10/15/404770.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Tue, 15 Oct 2013 15:46:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/10/15/404770.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/404770.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/10/15/404770.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/404770.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/404770.html</trackback:ping><description><![CDATA[前记：最近公司在做的项目完全基于Cache（Gemfire）构建了一个类数据库的系统，自己做的一个小项目里用过Guava的Cache，以前做过的项目中使用过EHCache，既然和Cache那么有缘，那就趁这个机会好好研究一下Java中的Cache库。在Java社区中已经提供了很多Cache库实现，具体可以参考http://www.open-open.com/13.htm，这里只关注自己用到的几个Cache库而且这几个库都比较具有代表性：Guava中提供的Cache是基于单JVM的简单实现；EHCache出自Hibernate，也是基于单JVM的实现，是对单JVM Cache比较完善的实现；而Gemfire则提供了对分布式Cache的完善实现。这一系列的文章主要关注在这几个Cache系统的实现上，因而步探讨关于Cache的好处、何时用Cache等问题，由于他们都是基于内存的Cache，因而也仅局限于这种类型的Cache（说实话，我不知道有没有其他的Cache系统，比如基于文件？囧）。<br />
<br />
记得我最早接触Cache是在大学学计算机组成原理的时候，由于CPU的速度要远大于内存的读取速度，为了提高CPU的效率，CPU会在内部提供缓存区，该缓存区的读取速度和CPU的处理速度类似，CPU可以直接从缓存区中读取数据，从而解决CPU的处理速度和内存读取速度不匹配的问题。缓存之所以能解决这个问题是基于程序的局部性原理，即&#8221;程序在执行时呈现出局部性规律，即在一段时间内，整个程序的执行仅限于程序中的某一部分。相应地，执行所访问的存储空间也局限于某个内存区域。局部性原理又表现为：时间局部性和空间局部性。时间局部性是指如果程序中的某条指令一旦执行，则不久之后该指令可能再次被执行；如果某数据被访问，则不久之后该数据可能再次被访问。空间局部性是指一旦程序访问了某个存储单元，则不久之后。其附近的存储单元也将被访问。&#8221;在实际工作中，CPU先向缓存区读取数据，如果缓存区已存在，则读取缓存中的数据（命中），否则（失效），将内存中相应数据块载入缓存中，以提高接下来的访问速度。由于成本和CPU大小的限制，CPU只能提供有限的缓存区，因而缓存区的大小是衡量CPU性能的重要指标之一。<br />
<br />
使用缓存，在CPU向内存更新数据时需要处理一个问题（写回策略问题），即CPU在更新数据时只更新缓存的数据（write back，写回，当缓存需要被替换时才将缓存中更新的值写回内存），还是CPU在更新数据时同时更新缓存中和内存中的数据（write through，写通）。在写回策略中，为了减少内存写操作，缓存块通常还设有一个脏位（dirty bit），用以标识该块在被载入之后是否发生过更新。如果一个缓存块在被置换回内存之前从未被写入过，则可以免去回写操作；写回的优点是节省了大量的写操作。这主要是因为，对一个数据块内不同单元的更新仅需一次写操作即可完成。这种内存带宽上的节省进一步降低了能耗，因此颇适用于嵌入式系统。写通策略由于要经常和内存交互（有些CPU设计会在中间提供写缓冲器以缓解性能），因而性能较差，但是它实现简单，而且能简单的维持数据一致性。<br />
<br />
在软件的缓存系统中，一般是为了解决内存的访问速率和磁盘、网络、数据库（属于磁盘或网络访问，单独列出来因为它的应用比较广泛）等访问速率不匹配的问题（对于内存缓存系统来说）。但是由于内存大小和成本的限制，我们又不能把所有的数据先加载进内存来。因而如CPU中的缓存，我们只能先将一部分数据保存在缓存中。此时，对于缓存，我们一般要解决如下需求：
<ol style="margin-top:1px">
     <li>使用给定Key从Cache中读取Value值。CPU是通过内存地址来定位内存已获取相应内存中的值，类似的在软件Cache中，需要通过某个Key值来标识相关的值。因而可以简单的认为软件中的Cache是一个存储键值对的Map，比如Gemfire中的Region就继承自Map，只是Cache的实现更加复杂。</li>
     <li>当给定的Key在当前Cache不存在时，程序员可以通过指定相应的逻辑从其他源（如数据库、网络等源）中加载该Key对应的Value值，同时将该值返回。在CPU中，基于程序局部性原理，一般是默认的加载接下来的一段内存块，然而在软件中，不同的需求有不同的加载逻辑，因而需要用户自己指定对应的加载逻辑，而且一般来说也很难预知接下来要读取的数据，所以只能一次只加载一条纪录（对可预知的场景下当然可以批量加载数据，只是此时需要权衡当前操作的响应时间问题）。</li>
     <li>可以向Cache中写入Key－Value键值对（新增的纪录或对原有的键值对的更新）。就像CPU的写回策略中有写回和写通策略，有些Cache系统提供了写通接口。如果没有提供写通接口，程序员需要额外的逻辑处理写通策略。也可以如CPU中的Cache一样，只当相应的键值对移出Cache以后，再将值写回到数据源，可以提供一个标记位以决定要不要写回（不过感觉这种实现比较复杂，代码的的耦合度也比较高，如果为提升写的速度，采用异步写回即可，为防止数据丢失，可以使用Queue来存储）。</li>
     <li>将给定Key的键值对移出Cache（或给定多个Key以批量移除，甚至清除整个Cache）。</li>
     <li>配置Cache的最大使用率，当Cache超过该使用率时，可配置溢出策略
     <ol><li>直接移除溢出的键值对。在移除时决定是否要写回已更新的数据到数据源。</li><li>将溢出的溢出的键值对写到磁盘中。在写磁盘时需要解决如何序列化键值对，如何存储序列化后的数据到磁盘中，如何布局磁盘存储，如何解决磁盘碎片问题，如何从磁盘中找回相应的键值对，如何读取磁盘中的数据并方序列化，如何处理磁盘溢出等问题。</li><li>在溢出策略中，除了如何处理溢出的键值对问题，还需要处理如何选择溢出的键值对问题。这有点类似内存的页面置换算法（其实内存也可以看作是对磁盘的Cache），一般使用的算法有：先进先出（FIFO）、最近最少使用（LRU）、最少使用（LFU）、Clock置换（类LRU）、工作集等算法。</li></ol><ul>
     </ul>
     </li>
     <li>对Cache中的键值对，可以配置其生存时间，以处理某些键值对在长时间不被使用，但是又没能溢出的问题（因为溢出策略的选择或者Cache没有到溢出阶段），以提前释放内存。</li>
     <li>对某些特定的键值对，我们希望它能一直留在内存中不被溢出，有些Cache系统提供PIN配置（动态或静态），以确保该键值对不会被溢出。</li>
     <li>提供Cache状态、命中率等统计信息，如磁盘大小、Cache大小、平均查询时间、每秒查询次数、内存命中次数、磁盘命中次数等信息。</li>
     <li>提供注册Cache相关的事件处理器，如Cache的创建、Cache的销毁、一条键值对的添加、一条键值对的更新、键值对溢出等事件。</li>
     <li>由于引入Cache的目的就是为了提升程序的读写性能，而且一般Cache都需要在多线程环境下工作，因而在实现时一般需要保证线程安全，以及提供高效的读写性能。</li>
</ol>
在Java中，Map是最简单的Cache，为了高效的在多线程环境中使用，可以使用ConcurrentHashMap，这也正是我之前参与的一个项目中最开始的实现（后来引入EHCache）。为了语意更加清晰、保持接口的简单，下面我实现了一个基于Map的最简单的Cache系统，用以演示Cache的基本使用方式。用户可以向它提供数据、查询数据、判断给定Key的存在性、移除给定的Key(s)、清除整个Cache等操作。以下是Cache的接口定义。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Cache&lt;K,&nbsp;V&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;V&nbsp;get(K&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;getAll(Iterator&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K&gt;&nbsp;keys);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isPresent(K&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;putAll(Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;entries);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidate(K&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll(Iterator&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K&gt;&nbsp;keys);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isEmpty();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;clear();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;asMap();<br />
}</div>
这个简单的Cache实现只是对HashMap的封装，之所以选择HashMap而不是ConcurrentHashMap是因为在ConcurrentHashMap无法实现getAll()方法；并且这里所有的操作我都加锁了，因而也不需要ConcurrentHashMap来保证线程安全问题；为了提升性能，我使用了读写锁，以提升并发查询性能。因为代码比较简单，所以把所有代码都贴上了（懒得整理了。。。。）。<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;CacheImpl&lt;K,&nbsp;V&gt;&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Cache&lt;K,&nbsp;V&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;HashMap&lt;K,&nbsp;V&gt;&nbsp;cache;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;ReadWriteLock&nbsp;lock&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ReentrantReadWriteLock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Lock&nbsp;readLock&nbsp;=&nbsp;lock.readLock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Lock&nbsp;writeLock&nbsp;=&nbsp;lock.writeLock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;CacheImpl(String&nbsp;name)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.name&nbsp;=&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;K,&nbsp;V&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;CacheImpl(String&nbsp;name,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;initialCapacity)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.name&nbsp;=&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;K,&nbsp;V&gt;(initialCapacity);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getName()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;V&nbsp;get(K&nbsp;key)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;cache.get(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;getAll(Iterator&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K&gt;&nbsp;keys)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Map&lt;K,&nbsp;V&gt;&nbsp;map&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;K,&nbsp;V&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;K&gt;&nbsp;noEntryKeys&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ArrayList&lt;K&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">while</span>(keys.hasNext())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;key&nbsp;=&nbsp;keys.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(isPresent(key))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(key,&nbsp;cache.get(key));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;noEntryKeys.add(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(!noEntryKeys.isEmpty())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CacheEntriesNotExistException(<span style="color: #0000FF; ">this</span>,&nbsp;noEntryKeys);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;map;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isPresent(K&nbsp;key)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;cache.containsKey(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.put(key,&nbsp;value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;putAll(Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;entries)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.putAll(entries);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidate(K&nbsp;key)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(!isPresent(key))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CacheEntryNotExistsException(<span style="color: #0000FF; ">this</span>,&nbsp;key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.remove(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll(Iterator&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K&gt;&nbsp;keys)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;K&gt;&nbsp;noEntryKeys&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ArrayList&lt;K&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">while</span>(keys.hasNext())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;key&nbsp;=&nbsp;keys.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(!isPresent(key))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;noEntryKeys.add(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(!noEntryKeys.isEmpty())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CacheEntriesNotExistException(<span style="color: #0000FF; ">this</span>,&nbsp;noEntryKeys);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">while</span>(keys.hasNext())&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;key&nbsp;=&nbsp;keys.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;invalidate(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.clear();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;cache.size();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;clear()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.clear();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;asMap()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ConcurrentHashMap&lt;K,&nbsp;V&gt;(cache);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isEmpty()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.lock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;cache.isEmpty();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;readLock.unlock();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}</div>
其简单的使用用例如下：
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp; @Test<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;testCacheSimpleUsage()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Book&nbsp;uml&nbsp;=&nbsp;bookFactory.createUMLDistilled();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Book&nbsp;derivatives&nbsp;=&nbsp;bookFactory.createDerivatives();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;umlBookISBN&nbsp;=&nbsp;uml.getIsbn();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;derivativesBookISBN&nbsp;=&nbsp;derivatives.getIsbn();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cache&lt;String,&nbsp;Book&gt;&nbsp;cache&nbsp;=&nbsp;cacheFactory.create("book-cache");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.put(umlBookISBN,&nbsp;uml);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.put(derivativesBookISBN,&nbsp;derivatives);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Book&nbsp;fetchedBackUml&nbsp;=&nbsp;cache.get(umlBookISBN);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fetchedBackUml);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Book&nbsp;fetchedBackDerivatives&nbsp;=&nbsp;cache.get(derivativesBookISBN);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fetchedBackDerivatives);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div><img src ="http://www.blogjava.net/DLevin/aggbug/404770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-10-15 23:46 <a href="http://www.blogjava.net/DLevin/archive/2013/10/15/404770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>