Sky's blog

我和我追逐的梦

常用链接

统计

其他链接

友情链接

最新评论

编码最佳实践(3)--尽量重用昂贵的初始化对象

    这里将要讲述的是一系列的类似案例,都是在各个产品进行performance tuning时被发现的,非常具有普适性。可以说在日常开发中,有非常大的概率遇到相同或者类似的情形,因此需要对其保持警惕以便避免陷入类似的性能问题。 
    我们从JAXBContext这个对象开始,JAXBContext 是JAXB API的入口,典型的代码实现如下: 
    
private void unmarshal() {  
    JAXBContext context = JAXBContext.newInstance(DirectoryConstants.JAXB_CONTEXT_CLASS);  
    Unmarshaller u = context.createUnmarshaller();  
    Object obj = u.unmarshall(...);  
}  
    这个是标准使用流程了,首先初始化JAXBContext对象,通过JAXBContext对象创建Unmarshaller对象,然后使用Unmarshaller对象来进行unmarshal操作。 
    这个写法在功能实现上没有任何问题,但是如果一旦进行压力测试,就会暴露性能问题。JAXBContext对象的初始化是个资源消耗非常大的操作,我们可以通过threaddump进行分析,会发现很多工作线程都在执行JAXBContext.newInstance()这个方法,而不是我们期待的u.unmarshal()。而事实上JAXBContext对象的Sun实现是线程安全的,即容许多线程同时调用一个JAXBContext对象的createUnmarshaller(),因此完全没有必要为每个xml的 marshaling 和 unmarshalling 操作去初始化一次JAXBContext对象。可以参考这里的说明:https://jaxb.dev.java.net/faq/index.html#threadSafety 
    简单点说,这个一个初始化代价昂贵,却又可重复使用的对象。 
    因此,我们只需要初始化JAXBContext对象一次并保存起来,然后重复使用这个JAXBContext对象即可。保存JAXBContext对象的方式可以有很多种,比如cache / threadlocal,或者使用一个单例来维持这个对象。比如下面的代码示例: 
private void unmarshal() {  
    JAXBContext context = JAXBContextHolder.get();  
    ...
}  
  
public class JAXBContextHolder {  
    private static final JAXBContext instance = JAXBContext.newInstance(DirectoryConstants.JAXB_CONTEXT_CLASS);  
 
    public static JAXBContext get() {  
        return instance;  
    }  
}  
    在实际项目中, 通过上面的简单改进之后,我们当时得到了一个非常巨大的回报:TPS (transactions per second 每秒事务处理量)直接*3 !! 
    这个案例从技术上讲非常的简单,道理很浅显,相信每个人都能轻松理解。或者说,这是一个“知道了就简单,不知道就容易犯错而不自知”的地方,因此依然有些东西需要注意: 
(1) 有哪些对象有类似的特性 
    目前发现的类似对象有 
    1. 刚刚上面讲到的JAXB API中的 JAXBContext 对象 
    2. SOAP API中的javax.xml.soap.SOAPFactory 
    3. CFX client 
    通常情况下,在使用各种api或者工具类库时,如果发现调用代码中有类似的初始化语句,都应该稍加注意(除非明确当前代码对性能完全没有要求),可以去查一下这个类对象的javdoc或者直接看源码,如果发现满足上面所说的特性,则应该考虑进行上述的性能优化。 
    根据经验,类似的初始化对象通常的命名规则都是***Context/***Factory之类,或者***client,遇到类似名字时需要提高警惕。 
(2)假设问题已经存在,如果才能在performance tuning中迅速发现问题的代码? 
    通常的办法就是用thread dump,一般连续dump个3-5次,然后通过分析thread dump信息 (推荐使用eclipse插件 lockness),看当前请求的线程(通常是一个线程池)都在干什么。一般初始化昂贵都昂贵在类似文件IO操作或者加锁之类的地方,很容易在thread dump中被发现。 

posted on 2012-06-17 23:02 sky ao 阅读(2666) 评论(0)  编辑  收藏 所属分类: java


只有注册用户登录后才能发表评论。


网站导航: