zoukankan      html  css  js  c++  java
  • Ehcache BigMemory: 摆脱GC困扰

    问题

    使用java开源项目经常需要调优jvm,以优化gc。对于gc,如果对象都是短时对象,那么jvm相对容易优化,假如碰上像solr使用自带java cache的项目,那么gc严重受限于cache,因为cache对象并非短时对象,以至于young gc常常伴有大量的内存对象拷贝,严重影响gc性能。

    Ehcache BigMemory

    Java的内存管理机制极其不适用于cache,最好的办法是使用jni实现的cache系统。另一种通用办法:Ehcache BigMemory(http://ehcache.org/)。BigMemory extends Ehcache's' capabilities with an off-heap store that frees you from GC’s constraints. 

    对于BigMemory,直接下载免费的32G限制的版本(注: 每个jvm进程最多使用32G的off-heap空间,对大多数应用已足够)。

    关于如何使用,参见官方文档: http://terracotta.org/documentation/4.0/bigmemorygo/get-started

    使用示例可参考代码中自带的样例:bigmemory-go-4.0.0/code-samples/src/main/java/com/bigmemory/samples

    样例代码缺少编译配置build.xml, 将下面的 build.xml 放在 bigmemory-go-4.0.0/code-samples 即可使用ant 编译示例代码:

    <project name="bigmemory" basedir=".">
            <property name="build.dir" value="${basedir}/build" />
            <property name="src.class.dir" value="${build.dir}" />
            <property name="src.dir" value="${basedir}/src" />
            <property name="lib.dir" value="${basedir}/../lib" />
            <property name="config.dir" value="${basedir}/config" />
            <path id="base.classpath">
                    <pathelement location="${src.class.dir}" />
                    <pathelement location="${config.dir}" />
                    <fileset dir="${lib.dir}">
                            <include name="**/*.jar" />
                    </fileset>
            </path>
            <path id="classpath">
                    <path refid="base.classpath" />
                    <fileset dir="${lib.dir}">
                            <include name="**/*.jar" />
                    </fileset>
            </path>
            <path id="build.src.path">
                    <pathelement location="${src.class.dir}" />
            </path>
            <target name="clean" description="clean">
                    <delete dir="${build.dir}" />
            </target>
            <target name="compile" depends="clean" description="compile">
                    <mkdir dir="${src.class.dir}" />
                    <javac srcdir="${src.dir}" destdir="${src.class.dir}" source="1.6" debug="on" encoding="utf-8" includeantruntime="false">
                            <classpath refid="base.classpath" />
                    </javac>
            </target>
            <target name="jar" depends="compile" description="jar">
                    <jar destfile="${build.dir}/bigmemory.jar">
                            <fileset dir="${src.class.dir}">
                                    <exclude name="**/timer/**" />
                            </fileset>
                    </jar>
            </target>
    </project>

    配置说明:bigmemory-go-4.0.0/config-samples/ehcache.xml 详细说明了配置参数。

    限制:

    1、存储对象全部使用 java.io.Serializable 做序列化和反序列化,性能有损失。

    2、off-heap空间一经分配不可调整。

    solr缓存

    引入Ehcache bigmemory是为了优化solr的缓存。下面代码是基于solr cache基类实现的ehcache缓存类,使用上同于solr.FastLRUCache,需要ehcache的外部配置文件。

    package org.apache.solr.search;
    
    import org.apache.solr.common.SolrException;
    import org.apache.solr.common.util.NamedList;
    import org.apache.solr.common.util.SimpleOrderedMap;
    import org.apache.solr.core.SolrCore;
    
    import java.util.*;
    import java.util.concurrent.atomic.AtomicLong;
    import java.io.IOException;
    import java.net.URL;
    
    import net.sf.ehcache.Cache;
    import net.sf.ehcache.CacheManager;
    import net.sf.ehcache.Element;
    import net.sf.ehcache.config.CacheConfiguration;
    import net.sf.ehcache.config.Configuration;
    import net.sf.ehcache.config.MemoryUnit;
    
    
    
    /**
     * @version $Id: EhCacheWrapper.java 2013-03-27  zhenjing chen $
     */
    public class EhCacheWrapper implements SolrCache {
    
      /* An instance of this class will be shared across multiple instances
       * of an LRUCache at the same time.  Make sure everything is thread safe.
       */
      private static class CumulativeStats {
        AtomicLong lookups = new AtomicLong();
        AtomicLong hits = new AtomicLong();
        AtomicLong inserts = new AtomicLong();
        AtomicLong evictions = new AtomicLong();
      }
    
      private CumulativeStats stats;
    
      // per instance stats.  The synchronization used for the map will also be
      // used for updating these statistics (and hence they are not AtomicLongs
      private long lookups;
      private long hits;
      private long inserts;
      private long evictions;
    
      private long warmupTime = 0;
    
      private CacheManager manager = null;
      private Cache map;
      private String name;
      private String cache_name;
      private int autowarmCount;
      private State state;
      private CacheRegenerator regenerator;
      private String description="Eh LRU Cache";
    
      private static int cache_index = 0;
      private static Map<String, CacheManager> managerPool = null;
      private static Map<String, Integer> managerFlag = null;
      private static CacheManager managerTemplate = null;
      static{
        managerPool = new HashMap<String, CacheManager>();
        managerFlag = new HashMap<String, Integer>();
        managerTemplate = new CacheManager("/data/conf/ehcache.xml");
      }
      
      private Cache GetCache() {
        
        // use cache pool
        Set<String> set = managerFlag.keySet();
        Iterator<String> it = set.iterator();
        while(it.hasNext()) {
            String cacheName = it.next();
            if( managerFlag.get(cacheName) == 0 ) {  // not used
                manager = managerPool.get(cacheName);
        
                System.out.println("EhCacheWrapper Cache Name(Pool): " + cacheName);
                
                managerFlag.put(cacheName, 1);
                cache_name = cacheName;
                return manager.getCache(cacheName);
            }
        }
    
        // add zhenjing
        String cacheName = name + cache_index;
        System.out.println("EhCacheWrapper Cache Name: " + cacheName);
         
        // create Cache from template
        Cache orig = managerTemplate.getCache(name);
        CacheConfiguration configTmp = orig.getCacheConfiguration();
        configTmp.setName(cacheName);
        Configuration managerConfiguration = new Configuration();
        managerConfiguration.setName(cacheName);
        manager = new CacheManager(managerConfiguration.cache(configTmp));
    
        // put to cache pool
        managerFlag.put(cacheName, 1);
        managerPool.put(cacheName, manager);
    
        // get cache
        cache_index++;
        cache_name = cacheName;
        return manager.getCache(cacheName);
      }
    
      public Object init(Map args, Object persistence, CacheRegenerator regenerator) {
        state=State.CREATED;
        this.regenerator = regenerator;
        name = (String)args.get("name");
        String str = (String)args.get("size");
        final int limit = str==null ? 1024 : Integer.parseInt(str);
        str = (String)args.get("initialSize");
        final int initialSize = Math.min(str==null ? 1024 : Integer.parseInt(str), limit);
        str = (String)args.get("autowarmCount");
        autowarmCount = str==null ? 0 : Integer.parseInt(str);
    
        // get cache
        map = GetCache();
        CacheConfiguration config = map.getCacheConfiguration();
    
        description = "Eh LRU Cache(MaxBytesLocalOffHeap=" + config.getMaxBytesLocalOffHeap() + ", MaxBytesLocalHeap=" + config.getMaxBytesLocalHeap() + ", MaxEntriesLocalHeap=" + config.getMaxEntriesLocalHeap() + ")";
        
        if (persistence==null) {
          // must be the first time a cache of this type is being created
          persistence = new CumulativeStats();
        }
    
        stats = (CumulativeStats)persistence;
    
        return persistence;
      }
    
      public String name() {
        return name;
      }
    
      public int size() {
        synchronized(map) {
          return map.getSize();
        }
      }
    
      public Object put(Object key, Object value) {
        synchronized (map) {
          if (state == State.LIVE) {
            stats.inserts.incrementAndGet();
          }
    
          // increment local inserts regardless of state???
          // it does make it more consistent with the current size...
          inserts++;
          map.put(new Element(key,value));
          return null;  // fake the previous value associated with key.
        }
      }
    
      public Object get(Object key) {
        synchronized (map) {
          Element val = map.get(key);
          if (state == State.LIVE) {
            // only increment lookups and hits if we are live.
            lookups++;
            stats.lookups.incrementAndGet();
            if (val!=null) {
              hits++;
              stats.hits.incrementAndGet();
              //System.out.println(name + " EH Cache HIT. key=" + key.toString());
            }
          }
          if( val == null)  return null;
          return val.getObjectValue();
        }
      }
    
      public void clear() {
        synchronized(map) {
          map.removeAll();
        }
      }
    
      public void setState(State state) {
        this.state = state;
      }
    
      public State getState() {
        return state;
      }
    
      public void warm(SolrIndexSearcher searcher, SolrCache old) throws IOException {
        return;
      }
    
    
      public void close() {
        clear();
        // flag un-used 
        managerFlag.put(cache_name, 0);
        System.out.println("EhCacheWrapper Cache Name(Reuse): " + cache_name);
      }
    
    
      //////////////////////// SolrInfoMBeans methods //////////////////////
    
    
      public String getName() {
        return EhCacheWrapper.class.getName();
      }
    
      public String getVersion() {
        return SolrCore.version;
      }
    
      public String getDescription() {
        return description;
      }
    
      public Category getCategory() {
        return Category.CACHE;
      }
    
      public String getSourceId() {
        return " NULL ";
      }
    
      public String getSource() {
        return " NULL ";
      }
    
      public URL[] getDocs() {
        return null;
      }
    
    
      // returns a ratio, not a percent.
      private static String calcHitRatio(long lookups, long hits) {
        if (lookups==0) return "0.00";
        if (lookups==hits) return "1.00";
        int hundredths = (int)(hits*100/lookups);   // rounded down
        if (hundredths < 10) return "0.0" + hundredths;
        return "0." + hundredths;
    
        /*** code to produce a percent, if we want it...
        int ones = (int)(hits*100 / lookups);
        int tenths = (int)(hits*1000 / lookups) - ones*10;
        return Integer.toString(ones) + '.' + tenths;
        ***/
      }
    
      public NamedList getStatistics() {
        NamedList lst = new SimpleOrderedMap();
        synchronized (map) {
          lst.add("lookups", lookups);
          lst.add("hits", hits);
          lst.add("hitratio", calcHitRatio(lookups,hits));
          lst.add("inserts", inserts);
          lst.add("evictions", evictions);
          lst.add("size", map.getSize());
        }
    
        lst.add("warmupTime", warmupTime);
    
        long clookups = stats.lookups.get();
        long chits = stats.hits.get();
        lst.add("cumulative_lookups", clookups);
        lst.add("cumulative_hits", chits);
        lst.add("cumulative_hitratio", calcHitRatio(clookups,chits));
        lst.add("cumulative_inserts", stats.inserts.get());
        lst.add("cumulative_evictions", stats.evictions.get());
    
        return lst;
      }
    
      public String toString() {
        return name + getStatistics().toString();
      }
    }

    外部ehcache.xml配置:

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
             updateCheck="false" monitoring="autodetect"
             dynamicConfig="true" name="config">
    
        <!--
        <cache name="filterCache"
             maxEntriesLocalHeap="1024"
             eternal="true"
             overflowToOffHeap="true"
             maxBytesLocalOffHeap="1g">
        </cache>
    
        <cache name="fieldValueCache"
             maxEntriesLocalHeap="1024"
             eternal="true"
             overflowToOffHeap="true"
             maxBytesLocalOffHeap="1g">
        </cache>
        -->
    
        <cache name="queryResultCache"
             maxEntriesLocalHeap="1"
             eternal="true"
             overflowToOffHeap="true"
             maxBytesLocalOffHeap="800m">
        </cache>
    
        <!-- ehcache not support documentCache, encoding format error.
        <cache name="documentCache"
             maxEntriesLocalHeap="1024"
             eternal="true"
             overflowToOffHeap="true"
             maxBytesLocalOffHeap="1g">
        </cache>
        -->
    
    </ehcache>
  • 相关阅读:
    Trie
    [转]Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
    [Bzoj2242]常见数值算法
    JavaScript百炼成仙(记录笔记)
    tcpreplay重放报文,tcpdump能抓到包,应用程序收不到包
    用jquery tools来实现选项卡
    ExtNet第二篇
    线性表最简单的表示
    c语言中线性表基本操作方法
    ExtNet第一篇
  • 原文地址:https://www.cnblogs.com/zhenjing/p/Ehcache_BigMemory.html
Copyright © 2011-2022 走看看