Posted on 2010-06-05 10:13
dennis 阅读(4504)
评论(5) 编辑 收藏 所属分类:
java 、
数据库技术 、
涂鸦 、
数据结构与算法
Java的垃圾收集算法是分代的,因为根据2/8原则,80%的Java对象都是速生速灭的,因此将Java Heap划分为new和old,对两个区域采用不同的垃圾回收算法,在new代存活下来的对象转移到old区,这样一来大大提高了Java GC的效率。
类似分代的思想在很多地方可以用到,分代的本质是根据对象生命周期的不同做区别处理,而不是采取一刀切的方式来提高系统的处理效率。推而广之,比如缓存的使用,现在很多web应用都采用了类似memcached这样的缓存挡在数据库前面分担负载,那么你可以将memcached理解成new代,而数据库是old代。memcached中存储的是查询的热点数据,新鲜火热,但是易失,并且在数据更新的时候被移除;而数据库保存了所有的数据,当缓存没有命中的时候才去查询数据库并存储到缓存。new和old这只是简单的二代划分,事实上现在越来越多的系统是多级缓存,页面缓存、memcached缓存、JVM内部缓存、查询缓存等等直到数据库,从web页面到后端是一个越来越old的过程,缓存对象持续的生命周期逐渐增长直到persist状态。
具体到JVM内部缓存,我们通常使用LRU算法来实现一个安全有限的缓存,如直接继承LinkedHashMap将可以实现一个简单的LRUMap。基于内存使用上的考虑,我们会给LRUMap设定一个最大的capacity,当数据量超过这个capacity的时候将最近最少访问的元素移除来容纳新的元素。这样处理产生的问题是这个capactity不能设置得太大,因为怕内存不够用,但是不够大的结果可能是命中率没有那么高(跟你的场景相关),那么如何在保存内存安全的前提下更进一步缓存更多的对象呢?答案也是分代。LRUMap默认存储的都是对象的强引用,我们知道Java还有其他3种引用类:soft,weak和
phantom。其中Soft引用是在jvm内存不够的时候进行回收,weak引用是在垃圾回收碰到的时候会被回收,显然weak->soft->strong三类引用的生命周期可以划分为3个代,我们将可以实现一个可以容纳更多对象的LRUMap:LRUMap设置两个阀值,一个是强引用的最大阀值,这个不能太大,一个是软引用的最大数目,当超过第一个阀值的时候,不是将LRU替换出来的对象移除,而是替代转换为软引用存储;如果软引用的数目也超过阀值,那么可以将软引用这个Map里的对象LRU替换成Weak引用存储或者简单移除。处理元素查询的时候,多了一个步骤,在查询强引用未果的情况下,需要再去查询软引用集合,不过都是O(1)复杂度的查询,不会成为明显的瓶颈。通过将缓存对象分代,我们实现了容难更多缓存对象的目标,大部分对象以强引用的形式存储,被LRU替换出去最近最少访问的元素以软引用存储,它们在内存不够的时候被垃圾回收,保证了内存使用上的安全性。我们在系统中采用了类似这样的缓存,缓存的命中率有了明显的提高。
题目是《缓存的分代》,其实谈的是分代这种常见的设计或者说技巧,在需要处理大量对象的场景中,不采用一刀切的方式,而是根据对象的特点进行适当的分代处理,带来的效率提升可能是惊人的。
PS.关于这个
招聘罗嗦两句,我是这个小组的成员,有人质疑我的目的是为了赚推荐费,这个不能说没有,不过主要目的还是招人,我们很缺人。那么多要求可以归结为一句话:我们找Java基础良好、对并发通信有丰富实践经验、写代码相对靠谱、为人相对靠谱的人。那些要求并非硬性,如果你觉的合适,尽管投简历,谢谢。我们小组做的东西我认为还是有价值的,也很有挑战,淘宝内部的很多应用都在使用,如果你希望你做的产品被成千上万的人每天使用,欢迎加入。