`
javaG
  • 浏览: 550149 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

关于大量缓存对象回收的思考

    博客分类:
  • java
阅读更多

前几天面试,被问到了一个问题,如果当前有数亿条记录,但是缓存最多放100万条记录,怎么保证缓存很好的被利用(大概是个这个意思,具体的问题记得不太清楚了).当时一下没回答出来,就随便说了一下,利用类似java虚拟机的垃圾回收算法来实现缓存的回收.
回来后苦思冥想,相出一个简单的方案.
首先,利用HaspMap(考虑用ConcurrentHashMap,并发下这个map效率高)来存放缓存对象,每次被缓存的对象用一个CacheObject封装.封装的目的主要是给这个个缓存对象添加一些属性信息,如果被命中的次数,过期的之间之类的.

public class CacheObject {
    private Object cache;//被cache的对象
    private int hit;//命中次数
    private long expire;//过期时间
  
    public CacheObject(Object object){
        this.cache = object;
    }
            
    public Object getCache() {
        return cache;
    }
    
    public void setCache(Object cache) {
        this.cache = cache;
    }
    //这里我就不实现如何生成hashCode的策略了
    public Integer getHashCode(){
        return null;
    }

    public int getHit() {
        return hit;
    }
    
    public void setHit(int hit) {
        this.hit = hit;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }


然后在建立几个LinkedList对象,来模拟jvm回收中的分代机制.下面是jvm分代示意图:

Heap = {Old + NEW = { Eden , from, to } }

 

JVM 内存模型中分两大块,一块是 NEW Generation, 另一块是 Old Generation. New Generation 中,有一个叫 Eden 的空间,主要是用来存放新生的对象,还有两个 Survivor Spaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象。在 Old Generation 中,主要存放应用程序中生命周期长的内存对象,还有个 Permanent Generation ,主要用来放 JVM 自己的反射对象,比如类对象和方法对象等。

垃圾回收描述:
现在我们用LinkedList对象来模拟JVM中的Old,Eden , from, to对象,来实现我们的缓存垃圾回收.新被添加进缓存的对象(CacheObject)的引用都被存放在Eden对象的队列的头部里,如果要回收缓存对象时,从Eden队列的尾部开始扫描.如果扫描时发现这个CacheObject没有被命中,就把这个CacheObject对应在缓存中的数据删掉,并把这个CacheObject从Eden中删掉;如果发现这个CacheObject被命中了,就把它挪到from的队列的首部.最后命中次数多的CacheObject会被移动到Old队列里面.
回收的时候我们可以模拟JVM里面的回收分级,例如:0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾;1级或以上为部分垃圾回收,只会回收NEW中的垃圾.
这里的Old,Eden , from, to在垃圾回收的时候都是FIFO模式,新的对象放队首,回收的时候从队尾开始回收.

我这里就给个简单的思路,希望大侠们指正.

===========
原来的写文章时候对缓存的写法理解不够深刻,其实没必要把算法搞的如此复杂,只需要用一个链表实现就好.然后再用以下清理策略来清理.
1.设置一个自动清理的线程,定时启动检查.
2.设置一个缓存数目最大值,超过这个最大值就启动清理(优先清理命中率低的).
3.设置一个最大超时时间,超过这个时间,必须回收此缓存.
4.设置一个清理比率a,每次清理到缓存数目  最大值*(1-a) 就停止清理.
5.命中一次就把此缓存丢到链表的尾部,每次新的元素都丢到链表的尾部.
6.清理时,先遍历整个缓存,把超时的全部清理掉.如果清理完超时的还不够,就从头部开始往后删除元素超过范围的元素.(因为是链表,直接算出要删除多少条数据,然后把头部直接指向那条记录就完成了删除,效率很高)
分享到:
评论
1 楼 ll648322446 2012-04-26  
楼主对java的Garbage理解很深哈,只是这种关于缓存的利用率要结合具体的访问模式和对象属性吧

相关推荐

Global site tag (gtag.js) - Google Analytics