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>

http://www.cnblogs.com/zhenjing/p/Ehcache_BigMemory.html

时间: 2024-12-07 04:32:36

Ehcache BigMemory: 摆脱GC困扰(转)的相关文章

EHCache学习笔记

介绍: EHCache 是一个快速的.轻量级的.易于使用的.进程内的缓存.它支持 read-only 和 read/write 缓存,内存和磁盘缓存.是一个非常轻量级的缓存实现,而且从 1.2 之后就支持了集群. 配置: EHCache的配置非常灵活,可以在声明里配置,也可以在xml.程序中.构造函数中配置.下面在程序中动态的改变Cache的配置,如下: Cache cache = manager.getCache("sampleCache");CacheConfiguration c

成为Java GC专家(4)—Apache的MaxClients参数详解及其在Tomcat执行FullGC时的影响

本文作者: ImportNew - 王晓杰 未经许可,禁止转载! 这是“成为Java GC专家系列文章”的第四篇. 在第一篇文章 成为JavaGC专家Part I — 深入浅出Java垃圾回收机制 中我们学习了不同GC算法的执行过程,GC如何工作,新生代及老年代的基本概念,在JDK7中你应该了解的5种GC类型以及他们的性能如何. 在第二篇文章 成为JavaGC专家Part II — 如何监控Java垃圾回收机制 中我们学到了JVM到底是如何执行垃圾回收,我们如何监控GC,以及那些工具可以使得监控

CSDN日报20170311——《程序员每天累成狗,是为了什么》

[程序人生]程序员每天累成狗,是为了什么 作者:郭小北 程序员可以投入的资本就是:身体和脑力,说白了都是出卖劳动力换取回报,也就是钱.我们大部分人都是凡人,或许当初是基于兴趣和理想去做一件事,入一门行,但随着阅历的丰富,年龄的增长,责任感的叠加你工作就是为了钱啊,因为在这个物质的社会,你连家都养不了,何来生活的更好? [物联网]Android Things --SDK框架 作者:王玉成 物联网应用开发与手机和平板的应用开发有一些区别,那么Android Things与Android又有哪些差别呢

在.net中读写config文件的各种方法(自定义config节点)

http://www.cnblogs.com/fish-li/archive/2011/12/18/2292037.html 阅读目录 开始 config文件 - 自定义配置节点 config文件 - Property config文件 - Element config文件 - CDATA config文件 - Collection config文件 - 读与写 读写 .net framework中已经定义的节点 xml配置文件 xml配置文件 - CDATA xml文件读写注意事项 配置参数的

国庆随笔之一

一个页面的复杂程度不断的增加,其中最为头疼的就是页面的DOM操作,而angular JS则可以让我摆脱这种困扰,在angularJS 中我们可以使用$http来向服务器发送请求,$http服务只是简单的封装了原生的XMLHttpRequest对象. $http服务是只能接受一个参数的函数,这个参数是一个对象,包含了用来生成HTTP请求的配置内容.这个函数返回一个promise对象,具有success和error两个方法. $http({url:'data.json',method:'GET'})

惠州那个医院做包皮好--友好泌尿男科

[惠州友好泌尿医院] [惠州口碑男科医院 打造专业男科典范] 惠州友好泌尿医院坚守诚信服务为核心.以"诚"立院.通过医疗服务质量的监督.检查机制得以完善.力求为病人提供完善.标准.规范的诚信服务."一切为了病人.一切服务于病人".这是惠州友好泌尿医院推行人文化医疗服务的出发点和落脚点.多年来.医院不断简化就医程序.实行"人文化.人性化.人本化"的医疗设计.开通网络在线咨询服务.设置一人一医一诊室的私密就诊环境.努力满足病人个性化的医疗需求.为病人

惠州那家医院害包皮好--友好泌尿男科

[惠州友好泌尿医院] [惠州口碑男科医院 打造专业男科典范] 惠州友好泌尿医院坚守诚信服务为核心.以"诚"立院.通过医疗服务质量的监督.检查机制得以完善.力求为病人提供完善.标准.规范的诚信服务."一切为了病人.一切服务于病人".这是惠州友好泌尿医院推行人文化医疗服务的出发点和落脚点.多年来.医院不断简化就医程序.实行"人文化.人性化.人本化"的医疗设计.开通网络在线咨询服务.设置一人一医一诊室的私密就诊环境.努力满足病人个性化的医疗需求.为病人

湮没在先秦的【文士道】精神

道墨结合:道借墨之行,墨享道之思.(下) [思想]湮没在先秦的[文士道]精神--                         士道:做一个独立声音型知性贵族(时代的紧迫需要) 结尾我们来简单介绍一下我个人对[文士道]的肤浅理解. 1:什么是士?什么是士道? 士,是指古代职业政治家.士是由一些秉承思想贵族精神的知识分子组成.现代社会即使也存在着职业政治家,但是其意境与气度以及气节不可同日语.这就是仕人与士人的微妙区别--士人是狼独立而自由:仕人是犬傍依于屋檐. 士道,则是士人的人格理想与行为规

WPF程序中App.Config文件的读与写

原文:WPF程序中App.Config文件的读与写 WPF程序中的App.Config文件是我们应用程序中经常使用的一种配置文件,System.Configuration.dll文件中提供了大量的读写的配置,所以它是一种高效的程序配置方式,那么今天我就这个部分来做一次系统性的总结. App.Config文件是系统默认的应用程序配置文件,在我们使用后进行编译时会生成"程序集名称+.exe.config"文件,其本质上也是一个XML文件,在我们的应用程序中添加应用程序配置文件后,默认生成下