EhCache使用过多的堆内存来存储值

时间:2016-03-23 12:30:59

标签: java ehcache

我使用EhCache来缓存2.5 Gb的数据。我有32个不同大小的文件,但总数据量为2.5 Gb。我使用12G堆内存运行我的代码,但是整个数据集无法容纳12G数据,它会溢出到磁盘。你能否告诉我配置有什么问题,或者EhCache总是占用那么多内存。所有的缓存我把Integer作为键,List作为值(分隔文件的记录)

JVM参数使用%JAVA_HOME%\bin\java.exe -server -d64 -Xms12G -Xmx12G -XX:+UseG1GC -XX:-OmitStackTraceInFastThrow

以下是我用于缓存

的代码段
//Spring Configuration
@Bean(destroyMethod = "shutdown",name="batchCache")
public net.sf.ehcache.CacheManager ehCacheManager() {
    DiskStoreConfiguration diskStoreConfiguration = new DiskStoreConfiguration();
    diskStoreConfiguration.setPath("C:\\DiskCache");

    net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
    config.setName("NAV_BATCH");
    config.addDiskStore(diskStoreConfiguration);
    //config.setMaxBytesLocalHeap(MemoryUnit.GIGABYTES.toBytes(10));
    net.sf.ehcache.CacheManager mgr = net.sf.ehcache.CacheManager.newInstance(config);
    mgr.clearAll();
    return mgr;
}

//Code to Obtain Cache manager
private CacheManager cacheManager = (CacheManager) ApplicationContextProvider.getApplicationContext().getBean("batchCache");

//Class level copy of Cache
private Cache cache;

protected Cache getCache(){
    if(null == cache){
        Cache managerCache = cacheManager.getCache(cacheName);
        if(null == managerCache){
            cache = createCache();
        }else{
            cache = managerCache;
        }
    }
    return cache;
}

//Key is Integer and Values is List of String always
protected void putListRecordsInCache(Object key,List<Object> values){
    Element element = new Element(key, values);
    getCache().put(element);
}

public List<T> getValues(Object key){
    Element e  = getCache().get(key);
    List<T> dataList = new LinkedList<>();
    if(null == e){
        return dataList;
    }

    List<String> lines = (List<String>) e.getObjectValue();

    for(String line:lines){
        T t = getMapper().convertValuesToObject(line, null);
        dataList.add(t);
    }

    return dataList;
}

private Cache createCache(){
    Cache managerCache = cacheManager.getCache(cacheName);
    if(null == managerCache){
        managerCache = new Cache(cacheConfig(cacheName) );
        //managerCache = (Cache) cacheManager.addCacheIfAbsent(managerCache);
        cacheManager.addCache(managerCache);
    }
    return managerCache;
}

private CacheConfiguration cacheConfig(String name) {
    CacheConfiguration config = new CacheConfiguration();
    config.name(name)

    .memoryStoreEvictionPolicy("LRU")
    .eternal(true)
    .pinning(new PinningConfiguration().store(Store.LOCALMEMORY))
    .logging(false)
    .sizeOfPolicy(new SizeOfPolicyConfiguration().maxDepth(100000).maxDepthExceededBehavior("CONTINUE"))
    .persistence(new PersistenceConfiguration().strategy(Strategy.LOCALTEMPSWAP))
    .statistics(true);
    ;

    long size = -1;
    try {
        size = Files.size(FileSystems.getDefault().getPath(getDataFileLocation(), getFileName()));
        //System.out.println(new Date()+",Size of file "+getDataFileLocation()+"/"+getFileName()+" is "+size+" bytes");
    } catch (IOException e) {
        e.printStackTrace();
    }

    if(size > 0){
        long cachesize = size*4;
        config.maxBytesLocalHeap(cachesize, MemoryUnit.BYTES);
    }else{

        if(isValueGloballyCached){
            config.maxBytesLocalHeap(100, MemoryUnit.MEGABYTES);
        }else{
            config.maxBytesLocalHeap(500, MemoryUnit.MEGABYTES);
        }
    }


    return config;
}
  

以下是内存和CPU配置文件截图

enter image description here

enter image description here

3 个答案:

答案 0 :(得分:2)

问题不是&#34; Ehcache正在使用很多记忆&#34;。问题更像是&#34;以有效的方式在存储器中缓存大文件&#34;。

此外,您希望随机访问文件的每一行。

那么Ehcache就这么贪婪了吗? java字符串对象为您要存储的每个字符串添加38个字节+为Integer键添加16个字节(8个用于houskeeping + 4个用于int = rounds到16个字节)。这增加了ca.对于32位环境,550mb到您最大的文件。这在64位环境中变得更糟。

我猜,你的594 mb文件增加了大约1GB的对象开销,我没有考虑ehcache使用的Element对象。如果你研究那个对象,你会很清楚内存被浪费的地方。

所以,我想我已经明确了为什么你的2.5 GB文件使用了这么多内存。

可能的解决方案:假设您可以使用Singleton来缓存数据。

我会将文件的文本存储在一个String对象中,并创建一个单独的int[](非Integer[])数组,该数组保存每行的偏移量。

因此,获取第1000行的文字将是:

//    text of the the file
String text;
//    int array of offset.
int[] offset;
// todo: check if there is line 1000

int start = offset[999];
int end = offset.length > 1000 ? offset[1000] : text.length();
String line1000 = text.substring(start, end);

如果您遵循该方法,则会为每个文件获取字符串和int数组。 String对象保存文本,数组保存偏移量。

答案 1 :(得分:1)

我在使用(方式)太多内存以及非常简单的测试示例中的“MappedByteBufferSource Async Flush Thread”错误时也遇到了麻烦。根据您需要的功能,我个人会使用issue tracker。它可以像常规ArrayList一样使用,并使用LRU策略为您处理从磁盘到内存的所有I / O.您可以将对象列表存储为BigArrayList中的元素,并像处理普通ArrayList一样对其进行操作。您可能需要尝试在内存中存储多少元素,具体取决于元素的大小和可用内存。优点是它易于使用且实际上有效。

答案 2 :(得分:0)

我认为ehcache.xml中有一个属性是maxBytesLocalOffheap,这可能对你的情况有用。

请完成 http://blog.terracotta.org/2015/04/13/ehcache-storage-tier-model-with-offheap/

如果您的ehcache版本低于2.3,则需要运行手动线程来查看过期内容,以便在堆中有足够的空间。

iebackground线程定期执行getKeysWithExpiryCheck()。