转载请注明出处哈:http://carlosfu.iteye.com/blog/2237511
问题目录:
- 一、Ehcache、BigMemory Go和BigMemory Max的关系
- 二、copyOnRead配置分析
- 三、BigMemory的sizeOf问题:
- 四、timeToLive和timeToIdle配置分析
- 五、Ehcache的统计配置和说明:
- 六、Ehcache的常用的eviction算法:
- 七、MaxEntries和MaxBytes配置选择问题:
- 八、哪些配置参数可以运行时动态修改
- 九、Ehcache批量操作优化:
- 十、是否要使用磁盘
- 十一、Element生命周期
- 十二、序列化问题
一、Ehcache、BigMemory Go和BigMemory Max的关系
(1) terracotta收购了Ehcache,在Ehcache基础上开发了商业版的BigMemory Go(单机版:有试用期)和BigMemory Max(集群版:收费)。
(2) BigMemory支持使用堆外内存,有效利用本机内存并有效防止GC。
(3) Ehcache和BigMemory的API几乎完全一致。
二、 copyOnRead配置分析:
1. Ehcache进行cache.get()操作时,内存模型示意图
2. copyOnRead配置的几点说明:
(1) copyOnRead=false是默认值
(2) copyOnRead=false的话,当调用cache.get(key)时,引用的(o1-o4)都是同一个对象,
也就是说,如果有其他线程对该Element其进行update时,o1-o4也会使用新的value。
(3) copyOnRead=true的话,可以看右边的示意图,很明显(o1-o4)引用是copy的新对象(每个一份新的copy)。
也就是说,如果有其他线程对该Element其进行update时,o1-o4仍然会使用老value。
(4) 优缺点:
copyOnRead=false | 节省空间 | 线程不安全 |
copyOnRead=true | 线程安全 | 浪费空间 |
3. copyOnRead测试实验:
(0) cache.put()一个element, 然后循环get,观察在copyOnRead=false|true时,cache.get(key)打印值
package com.sohu.tv.ehcache.config; import net.sf.ehcache.Element; import org.junit.Test; import com.sohu.tv.ehcache.base.BaseTest; /** * ehcache中copyOnRead测试 * * @author leifu * @Date 2015-8-16 * @Time 下午9:02:56 */ public class EhcacheCopyOnReadTest extends BaseTest { @Test public void testCopyOnRead() throws InterruptedException { //写 String key = "bigKey"; byte[] bytes = new byte[1024 * 1024]; Element element = new Element(key, bytes); cache.put(element); //循环get for (int i = 0; i < 10; i++) { logger.info(cache.get(key).toString()); } } }
(1) 当copyOnRead=false:为同一个value
(2) 当copyOnRead=true,为不同value
设置jvm运行参数:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
package com.sohu.tv.ehcache.config; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import net.sf.ehcache.Element; import org.junit.Test; import com.sohu.tv.ehcache.base.BaseTest; /** * ehcache中copyOnRead测试 * vm:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError * * @author leifu * @Date 2015-8-16 * @Time 下午9:02:56 */ public class EhcacheCopyOnReadTest extends BaseTest { @Test public void testCopyOnReadHeap() throws InterruptedException { String key = "bigKey"; byte[] bytes = new byte[1024 * 1024]; Element element = new Element(key, bytes); cache.put(element); List<byte[]> list = new ArrayList<byte[]>(); for (int i = 0; i < 10; i++) { Element e = cache.get(key); byte[] aa = (byte[]) e.getObjectValue(); list.add(aa); } } }
运行会报错,发生了Java heap space,进一步证明了copyOnRead=true时获取的是复制品。
4. copyOnRead生产环境的选择:
选择依据业务的需要,根据第二节中copyOnRead的配置说明,来进行选择。
三、BigMemory的sizeOf问题:(具体参考BigMemroy系列文章--11. BigMemory中的SizeOf问题)
几点总结:
(1) Bigmemory的主要开销:序列化+sizeOf计算
(2)对sizeOf引擎友好的对象:尽量使用不深/不广的对象:深(继承树) 广( bigPojo,ArrayList,HashMap等)
(3)采用预前序列化的方式,bigMemory只存序列化后的byte数组, 就不会出现sizeof问题
四、timeToLive和timeToIdle配置分析:
1. 先看下官方文档的解释:
timeToLive – The maximum number of seconds an element can exist in the cache regardless of access.
The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).
timeToIdle – The maximum number of seconds an element can exist in the cache without being accessed.
The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime).
2. timeToLive和timeToIdle理解
英语不太好,上面除了红色的不一样,解释是一样的,一开始没理解这个配置的意思,通过测试终于明白了。
timeToLive(TTL): 可以理解成过期时间,不管key是否在过期时间内被访问了,到了过期时间就从cache中移除(get()不到了,实际通过测试发现没有立即删除,应该是ehcache有一些过期删除策略)
timeToIdle(TTI): 可以理解成最大闲置时间,如果key在此期间被访问,那么TTI将恢复原值。
两者比较:TTL是到点一定过期,TTI是到点不一定过期(期间被访问了)
3. timeToLive和timeToIdle测试实验
(1) timeToLive:
(a) 配置新加timeToLiveSeconds="10"
<?xml version="1.0" encoding="UTF-8" ?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <cache name="firstEhcache" maxElementsInMemory="10000" timeToLiveSeconds="10"> </cache> </ehcache>
(b)测试代码
package com.sohu.tv.ehcache.ttl; import java.util.concurrent.TimeUnit; import net.sf.ehcache.Element; import org.junit.Test; import com.sohu.tv.ehcache.base.BaseTest; /** * 测试timeToLive配置 * @author leifu * @Date 2015-8-17 * @Time 下午8:12:10 */ public class EhcacheTimeToLiveTest extends BaseTest { @Test public void testTimeToLive() throws InterruptedException { // put一个Element,ehcache.xml设置 timeToLiveSeconds="10" String key = "ttlKey"; byte[] bytes = new byte[1024]; Element element = new Element(key, bytes); cache.put(element); logger.info("after put, ehcache object size: " + cache.getSize()); // 等待10秒过期 for (int i = 10; i > 0; i--) { Element e = cache.get(key); long remainSecondsToExpire = 0; try { remainSecondsToExpire = (e.getExpirationTime() - System.currentTimeMillis()) / 1000; } catch (Exception exception) { } logger.info("key {} remain {} seconds to expire", key, remainSecondsToExpire); TimeUnit.SECONDS.sleep(1); } // 过期后再次获取 Element e = cache.get(key); logger.info("exceed ttl get element is {}", e); logger.info("At final, ehcache object size: " + cache.getSize()); } }
(c) 输出:
14:12:40.066 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - after put, ehcache object size: 1
14:12:40.067 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 9 seconds to expire
14:12:41.067 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 8 seconds to expire
14:12:42.068 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 7 seconds to expire
14:12:43.070 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 6 seconds to expire
14:12:44.070 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 5 seconds to expire
14:12:45.070 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 4 seconds to expire
14:12:46.070 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 3 seconds to expire
14:12:47.071 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 2 seconds to expire
14:12:48.071 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 1 seconds to expire
14:12:49.071 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - key ttlKey remain 0 seconds to expire
14:12:50.072 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - exceed ttl get element is null
14:12:50.072 [main] INFO c.s.t.e.ttl.EhcacheTimeToLiveTest - At final, ehcache object size: 0
(2) timeToIdle:
(a) 新加配置timeToIdleSeconds="10"
<?xml version="1.0" encoding="UTF-8" ?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <cache name="firstEhcache" maxElementsInMemory="10000" timeToIdleSeconds="10"> </cache> </ehcache>
(b) 测试代码
package com.sohu.tv.ehcache.ttl; import java.util.concurrent.TimeUnit; import net.sf.ehcache.Element; import org.junit.Test; import com.sohu.tv.ehcache.base.BaseTest; /** * ehcache TimeToIdle测试 * * @author leifu * @Date 2015-8-17 * @Time 下午8:12:10 */ public class EhcacheTimeToIdleTest extends BaseTest { @Test public void testTimeToIdle() throws InterruptedException { // put一个Element,ehcache.xml设置 timeToIdleSeconds="10" String key = "ttiKey"; byte[] bytes = new byte[1024]; Element element = new Element(key, bytes); cache.put(element); logger.info("after put, ehcache object size: " + cache.getSize()); Element e = null; // 等待10秒过期 for (int i = 10; i > 0; i--) { logger.info("sleep {} seconds wait expire", i); if (i == 2) { logger.info("ehcache get key {}", key); e = cache.get(key); } TimeUnit.SECONDS.sleep(1); } logger.info("element is {}", e); long remainSecondsToExpire = (e.getExpirationTime() - System.currentTimeMillis()) / 1000; // 推测还有8秒(10-2)过期 logger.info("key {} remain {} seconds to expire", key, remainSecondsToExpire); // 再次get,重置过期时间 e = cache.get(key); remainSecondsToExpire = (e.getExpirationTime() - System.currentTimeMillis()) / 1000; // 推测重置成10秒过期 logger.info("key {} remain {} seconds to expire", key, remainSecondsToExpire); logger.info("At final, ehcache object size: " + cache.getSize()); } }