问题

使用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>

Ehcache BigMemory: 摆脱GC困扰的更多相关文章

  1. Ehcache BigMemory: 摆脱GC困扰(转)

    问题 使用java开源项目经常需要调优jvm,以优化gc.对于gc,如果对象都是短时对象,那么jvm相对容易优化,假如碰上像solr使用自带java cache的项目,那么gc严重受限于cache,因 ...

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

    下面我们看一下Apache的 MaxClients 参数在Full GC 发生时是如何影响系统的. 大部分开发人员都知道在由于GC发生而导致的”停止世界现象(STW) “(详细请参见Understan ...

  3. 【Unity优化】构建一个拒绝GC的List

    版权声明:本文为博主原创文章,欢迎转载.请保留博主链接:http://blog.csdn.net/andrewfan 上篇文章<[Unity优化]Unity中究竟能不能使用foreach?> ...

  4. 在.net中读写config文件的各种方法

    阅读目录 开始 config文件 - 自定义配置节点 config文件 - Property config文件 - Element config文件 - CDATA config文件 - Collec ...

  5. SpringMVC 学习-返回字符串中文乱码问题解决

    一.使用 SpringMVC 框架时,如果 HTTP 请求资源返回的是中文字符串,则会出现乱码.原因如下: SpringMVC 框架可以使用 @RequestBody 和 @ResponseBody ...

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

    WPF程序中的App.Config文件是我们应用程序中经常使用的一种配置文件,System.Configuration.dll文件中提供了大量的读写的配置,所以它是一种高效的程序配置方式,那么今天我就 ...

  7. 通用订单搜索的API设计得失录

    先把 Joshua Bloch 大神的 API PDF 放在这里膜拜下:"How to Design a Good API and Why it Matters.pdf" 总述 在 ...

  8. Spring:面向切片编程

    在之前我们记录Spring的随笔当中,都是记录的Spring如何对对象进行注入,如何对对象的属性值进行注入,即我们讲解的很大部分都是Spring的其中一个核心概念——依赖注入(或者说是控制翻转,IOC ...

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

    http://www.cnblogs.com/fish-li/archive/2011/12/18/2292037.html 阅读目录 开始 config文件 - 自定义配置节点 config文件 - ...

随机推荐

  1. 用datagrid实现完整的一个页面

    打怪升级真的好难,记录一点一滴,一滴一点,先上效果图. 1.想完成一个界面,先得有界面.界面是在WebRoot下的根目录文件中新建的zjqktj.jsp中建立的,再通过java在后台调用数据库取出数据 ...

  2. [C#基础实例]指定地址解析图片并下载

    需求:查找页面图片并下载至本地: 实现: 首先:读取通过网络html内容,并用正则表达式查找图片地下. 其次:使用WebRequest.Create创建图片请求. 最后:把获取图片网络流数据通过Fil ...

  3. 封装ios静态库碰到的一些问题(二)

    在静态库建立好了之后呢,于是应用程序就引用它,加上拷贝的h文件,但是引用之后Build之后提示很多sybmbol 重复 于是进行检查,确实由于是从其他工程修改过来的,很多基础库都引用了,删除之,最后就 ...

  4. Qt 4.x调试器问题,缺失调试助手。

    之前项目开发需要用到4.x环境,固定多少版本避免团队开发不协调,然后拿了同事的开发包安装[注:我本子上原来就有4.x版本跟5.x版本,只是对应的4.x跟需求的不一样] creator是2.4.1的,同 ...

  5. Python小练习四

    # 使用给定的宽度打印格式化后的价格列表 width = (int)(input('Please enter width:')) price_width = 10 item_width = width ...

  6. 区间型DP

    区间型DP是一类经典的动态规划问题,主要特征是可以先将大区间拆分成小区间求解最后由小区间的解得到大区间的解. 有三道例题 一.石子合并 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆. ...

  7. MAC PRO 的网关在哪里

    mac pro的网关就是路由器地址. 1.路由器在系统偏好设置里 2.双击点开此图标 选中1.然后点击高级设置 3.选中TCP/IP,然后查看自己的路由器后边的数字,就是你的mac网关号 4.IPV4 ...

  8. Webservice 65535 错误

    修改配置项: <system.serviceModel> <behaviors> <endpointBehaviors> <behavior name=&qu ...

  9. EditPlus 3.1

    User:GNU Serial:918A8-20DD8-44ZA1-B0W4A-13T66

  10. 【转】Python中的赋值、浅拷贝、深拷贝介绍

    这篇文章主要介绍了Python中的赋值.浅拷贝.深拷贝介绍,Python中也分为简单赋值.浅拷贝.深拷贝这几种"拷贝"方式,需要的朋友可以参考下   和很多语言一样,Python中 ...