`

编码最佳实践(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.unmarshal(...);
}


    这个是标准使用流程了,首先初始化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中被发现。

分享到:
评论

相关推荐

    35 个 Java 代码性能优化总结

    2. **尽量重用对象** - **解释**:频繁创建和销毁对象会消耗大量系统资源,尤其是在Java虚拟机中。使用可变对象如`StringBuilder`来替代不可变对象`String`可以减少对象创建次数。 - **实践**:对于经常变化的数据...

    JAVA数据结构与算法

    - **数组**:详细阐述了数组的声明、初始化以及访问方式。 - **Java的面向对象特性** - **类与对象**:解释了如何定义类以及如何创建对象,并探讨了类与对象之间的关系。 - **继承**:介绍了一种机制,允许一个...

    realmethods框架手册

    - 描述了如何初始化安全管理器,并设置必要的安全策略。 2. **Framework的JAAS安全管理** - **基于JAAS的验证**: 利用Java Authentication and Authorization Service(JAAS)提供的API来进行用户的认证和授权。 -...

    软件工程-填空题.pdf

    数据流图(DFD)和数据字典(DD)是结构化分析方法的核心,而继承是面向对象编程中的核心概念,支持代码重用。 软件详细设计涉及图形、表格和语言工具,同时,软件质量保证通过复审、测试和证明来确保。大型软件...

    MWS - ModularWebSystem-开源

    4. **成本效益**:对于企业和个人开发者来说,开源软件降低了初始开发成本,因为无需购买昂贵的商业许可证。 ### 文件结构分析 在提供的压缩包中,`mwsf` 可能是 MWS 框架的主文件或文件夹,包含了框架的核心组件...

    more Effective C# 改善C#的50个具体办法(中文版)

    15. **使用Lazy实现延迟初始化**:延迟初始化可以避免不必要的资源消耗,直到真正需要时才创建实例。 16. **理解并使用属性(Property)**:属性提供数据封装,同时支持自定义get和set行为,如验证和计算属性。 17...

    自定义的mysql接口类

    通过预先初始化一定数量的连接,当应用程序需要时可以从池中获取,使用完毕后归还,而不是直接关闭。这个自定义的MySQL接口类实现了连接池,意味着它能够优化数据库操作的性能,提高系统的响应速度和并发能力。 ...

Global site tag (gtag.js) - Google Analytics