编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。

翻译自 deepakgaikwad。如需转载本文,请先参见文章末尾处的转载要求。

许多应用程序在压力测试阶段或生产环境中往往会遭遇性能瓶颈。深入分析性能问题的根源,会发现很多是由数据处理不当造成的。尤其在面对大数据量场景时,数据处理方式至关重要。以下是一些实用的数据处理技巧,有助于显著提升 Java 应用程序的性能。

减少数据传输

在任何 Java 应用程序中,方法调用要么是为调用方完成某项任务,要么是对输入数据进行处理。这两个目标都需要在调用者和处理方法之间进行数据交换。关于最小化方法输入输出数据量,有一条经验法则:传输的数据越少越好。例如,数据量越小,处理开销越低,需要清理的对象越少,内存占用也更少。程序设计应当努力减少不同方法、层次和应用程序之间,甚至组织之间的数据传递。通过下文将要讨论的“源端数据处理”策略可以达成这个目标。

延迟加载

延迟加载(Lazy Loading)是指直到实际需要数据的最后一刻才从数据存储中获取数据。在面对重量级对象时,这一策略非常有益。例如,有一个存储在数据库中的文件实体,该文件包含 Blob 数据以及其他属性。Blob 的大小可以从几 KB 到几十兆不等,而中间层逻辑在显示文件内容之前通常只依赖于其他属性。此时,可以使用延迟加载来处理这个 Blob 属性,避免不必要的加载开销。

避免重复数据调用

当进行远程调用时,反复从数据提供者处获取数据会严重影响性能,例如数据库调用、Web 服务调用或者其他编解码调用。这种情况下,可以使用 Facade(外观)模式一次获得所有所需的数据,尽可能减小连接成本和在网络上传输数据的成本。

高速缓冲

经常使用但不经常变化的数据可以进行缓存,通常需要缓存的是静态数据或者服务端数据。如今,对应用事务数据也有很高的性能要求,这些数据也需要加入缓存。作为一个简单的规则,在设计应用程序时需要识别这类实体并缓存到合适的位置,通常这些实体不会频繁改变甚至根本不会变化。刷新对象时也可以采用这个规则。

在源端处理数据

在数据源或存储位置本身进行处理是一个好习惯。将大量数据发送给客户端然后再进行处理不仅需要传输成本,而且有时客户端的处理逻辑会改变数据格式。例如在 Oracle 数据库中过滤数据比较简单,只要为查询添加一个 WHERE 子句即可。而在 Java 程序中过滤这些数据,需要先获取记录再逐行挨个属性进行比较。因此,用 Java 进行过滤可能不是最佳实现。

减少数据转换与避免类型误用

将数据从一种格式转换到其他格式需要付出转换成本。对单个值进行转换消耗的性能可能微不足道,但如果记录个数成千上万,性能问题就十分明显。例如,选择 String 数据类型的值,并把它转换为 double 或其他基本类型,反之亦然,都应尽量避免不必要的转换。

选择合适的集合类型

从性能的角度来看,这是一个非常重要的考虑因素。Java 提供了不同的集合以满足不同的需求。例如有几种基础的集合,比如 ArrayList,你可以不断向 ArrayList 添加数据并且集合本身不会对数据进行任何同步操作。再比如 Vector 提供了同步的操作。我需要在插入数据时保持同步吗?这是一个很好的问题。如果不需要同步,那么就不要使用 Vector,使用 ArrayList 或根据要求选择其他集合。

优化数据处理算法

有时候性能问题是数据处理算法或实现逻辑造成的。实现逻辑应该将性能作为要求达到的一个目标,像是内存占用等。通过以下几个方法可以使算法进一步优化:

  • 优化开销很大的调用和数据库循环调用
  • 优化循环里的对象声明
  • 避免不必要的嵌套循环
  • 将对象存储到多个集合

重型数据对象的生命周期管理

我们不能保证会立即执行垃圾收集(Garbage Collection),但是将对象引用置为 null 是一个很好的做法,有助于垃圾回收器更早地回收内存。

利用技术特性优化

许多技术都有助于数据处理,其中包含了上面提到的一些要点。下面是几个例子:

  • Oracle 的 PreparedStatement
  • 缓存框架,例如 Hibernate 的一级缓存和二级缓存
  • Hibernate 框架的延迟加载机制

数据序列化与反序列化

在设计时需要避免性能代价过高的操作。如果没有这样的操作,还可以把数据转换减到最小。例如在 Web 应用程序中,可以尽量减少会话(Session)的输出数据。

并行处理数据

如果需要处理的数据量很大,那么可以并行处理不相关的数据以减少总的处理时间。

对象重用

对于重量级对象不要从头创建,可以对现有对象进行克隆并且只修改必要的属性,尽可能重用现有的信息。通过浅拷贝和深拷贝来控制重用的数量。

说明:本文部分建议(如 finalize 机制、Vector 的使用等)基于较早的 Java 版本实践。在现代 JDK 版本中,finalize 方法已被 deprecated 甚至移除,建议优先使用 try-with-resourcesCleaner 机制管理资源;集合类请优先根据并发需求选择 ArrayList 或并发包下的集合类。具体实现请以当前官方文档为准。