(注:相关代码请参考 com.ql.util.express.instruction.OperateDataCacheManager类,
QLExpress 源代码下载地址:http://code.taobao.org/p/QLExpress/src/ )
几乎所有的动态脚本语言在运行期都需要频繁的创建对象,并且强烈依赖语言的底层垃圾回收(java中称作GC ,可以参考这篇 《Java GC 》http://bruce-ko.iteye.com/blog/420262 )。
在多线程运行的情况下,cpu和内存的消耗很有可能被这个过程消耗殆尽。
近期,QlExpress在这方面做了较大的优化,在实际使用过程中,也确实效果明显。
本篇将具体介绍QlExpress创建对象过程的优化实现原理,也欢迎广大技术码农共同交流探讨。
public class OperateDataCacheManager { private static ThreadLocal<IOperateDataCache> m_OperateDataObjectCache = new ThreadLocal<IOperateDataCache>(){ protected IOperateDataCache initialValue() { return new OperateDataCacheImpl(30); } }; //获取ThreadLocal成员变量 public static IOperateDataCache getOperateDataCache(){ return m_OperateDataObjectCache.get(); } //其他代码逻辑 }
ThreadLocal成员变量可以保证多线程情况下数据的独立性、安全性,并且尽量保持高并发性,工作原理可以参考
《理解ThreadLocal 》 http://blog.csdn.net/qjyong/article/details/2158097
具体方案请看上图的数据结构示意图,(点击可以看大图)
在原来每次需要
new OperateDataField(Object aFieldObject,String aFieldName) 的时候,
每一次转而调用
OperateDataCacheManager.fetchOperateDataField(Object aFieldObject,String aFieldName),
这个时候会从缓冲区域获取到一个OperateDataField,成员变量被赋值。
而每次脚本运行完资源回收的时候,会把 OperateDataField【0..field】全部显示置为null,使所有的外部链接对象被java虚拟机马上释放。
注意:如果超过len长度, fetchOperateDataField 会直接调用new OperateDataField(Object aFieldObject,String aFieldName)返回,回收内存也就只能等jvm定期去gc了。
通常,我们的web应用服务器都会使用线程池来作为多线程并发的管理。
常驻内存的线程使用类似qlExpress的内存缓冲区管理方式, 减少频繁对象创建 可以很大程度上的带来的 CPU开销,内存被重复利用,也会稳定很多。
经过优化的方案在我们实际使用中也确实看到了效果,cpu 的load减少了近一倍。