11个简单的Java性能调优技巧,傻瓜都能学会!

大多数开发人员理所固然地觉得性能优化很复杂,须要大量的经验和知识。好吧,不能说这是彻底错误的。优化应用程序以得到最佳性能不是一件容易的事情。可是,这并不意味着若是你不具有这些知识,就不能作任何事情。程序员

这里有11个易于遵循的建议和最佳实践能够帮助你建立一个性能良好的应用程序。算法

大部分建议是针对Java的。但也有若干建议是与语言无关的,能够应用于全部应用程序和编程语言。在讨论专门针对Java的性能调优技巧以前,让咱们先来看看通用技巧。数据库

1.在你知道必要以前不要优化编程

这多是最重要的性能调整技巧之一。你应该遵循常见的最佳实践作法并尝试高效地实现用例。可是,这并不意味着在你证实必要以前,你应该更换任何标准库或构建复杂的优化。缓存

在大多数状况下,过早优化不但会占用大量时间,并且会使代码变得难以阅读和维护。更糟糕的是,这些优化一般不会带来任何好处,由于你花费大量时间来优化的是应用程序的非关键部分。安全

那么,你如何证实你须要优化一些东西呢?性能优化

首先,你须要定义应用程序代码的速度得多快,例如,为全部API调用指定最大响应时间,或者指定在特定时间范围内要导入的记录数量。在完成这些以后,你就能够测量应用程序的哪些部分太慢须要改进。而后,接着看第二个技巧。bash

2.使用分析器查找真正的瓶颈app

在你遵循第一个建议并肯定了应用程序的某些部分须要改进后,那么从哪里开始呢?框架

你能够用两种方法来解决问题:

  • 查看你的代码,并从看起来可疑或者你以为可能会产生问题的部分开始。

  • 或者使用分析器并获取有关代码每一个部分的行为和性能的详细信息。

但愿不须要我解释为何应该始终遵循第二种方法的缘由。

很明显,基于分析器的方法可让你更好地理解代码的性能影响,并使你可以专一于最关键的部分。若是你曾使用过度析器,那么你必定记得曾经你是多么惊讶于一下就找到了代码的哪些部分产生了性能问题。老实说,我第一次的猜想不止一次地致使我走错了方向。

3.为整个应用程序建立性能测试套件

这是另外一个通用技巧,能够帮助你避免在将性能改进部署到生产后常常会发生的许多意外问题。你应该老是定义一个测试整个应用程序的性能测试套件,并在性能改进以前和以后运行它。

这些额外的测试运行将帮助你识别更改的功能和性能反作用,并确保不会致使弊大于利的更新。若是你工做于被应用程序若干不一样部分使用的组件,如数据库或缓存,那么这一点就尤为重要。

4.首先处理最大的瓶颈

在建立测试套件并使用分析器分析应用程序以后,你能够列出一系列须要解决以提升性能的问题。这很好,但它仍然不能回答你应该从哪里开始的问题。你能够专一于速效方案,或从最重要的问题开始。这个你也必须会。

速效方案一开始可能会颇有吸引力,由于你能够很快显示第一个成果。但有时,可能须要你说服其余团队成员或管理层认为性能分析是值得的——由于暂时看不到效果。

但总的来讲,我建议首先处理最重要的性能问题。这将为你提供最大的性能改进,并且可能不再须要去解决其中一些为了知足性能需求的问题。

常见的性能调整技巧到此结束。下面让咱们仔细看看一些特定于Java的技巧。

5.使用StringBuilder以编程方式链接String

有不少不一样的选项来链接Java中的String。例如,你可使用简单的+或+ =,以及StringBuffer或StringBuilder。

那么,你应该选择哪一种方法?

答案取决于链接String的代码。若是你是以编程方式添加新内容到String中,例如在for循环中,那么你应该使用StringBuilder。它很容易使用,并提供比StringBuffer更好的性能。但请记住,与StringBuffer相比,StringBuilder不是线程安全的,可能不适合全部用例。这个你必须清楚。

你只须要实例化一个新的StringBuilder并调用append方法来向String中添加一个新的部分。在你添加了全部的部分以后,你就能够调用toString()方法来检索链接的String。

下面的代码片断显示了一个简单的例子。在每次迭代期间,这个循环将i转换为一个String,并将它与一个空格一块儿添加到StringBuilder sb中。因此,最后,这段代码将在日志文件中写入“This is a test0 1 2 3 4 5 6 7 8 9”。

StringBuilder sb = new StringBuilder(“This is a test”);
for (int i=0; i<10; i++) {
sb.append(i);
sb.append(” “);
}
log.info(sb.toString());
复制代码

正如在代码片断中看到的那样,你能够将String的第一个元素提供给构造方法。这将建立一个新的StringBuilder,新的StringBuilder包含提供的String和16个额外字符的容量。当你向StringBuilder添加更多字符时,JVM将动态增长StringBuilder的大小。

若是你已经知道你的String将包含多少个字符,则能够将该数字提供给不一样的构造方法以实例化具备定义容量的StringBuilder。这进一步提升了效率,由于它不须要动态扩展其容量。

6.使用+链接一个语句中的String

当你用Java实现你的第一个应用程序时,可能有人告诉过你不该该用+来链接String。若是你是在应用程序逻辑中链接字符串,这是正确的。字符串是不可变的,每一个字符串的链接结果都存储在一个新的String对象中。这须要额外的内存,会减慢你的应用程序,特别是若是你在一个循环内链接多个字符串的话。

在这些状况下,你应该遵循技巧5并使用StringBuilder。

可是,若是你只是将字符串分红多行来改善代码的可读性,那状况就不同了。

Query q = em.createQuery(“SELECT a.id, a.firstName, a.lastName ”
+ “FROM Author a ”
+ “WHERE a.id = :id”);
复制代码

在这些状况下,你应该用一个简单的+来链接你的字符串。Java编译器会对此优化并在编译时执行链接。因此,在运行时,你的代码将只使用1个String,不须要链接。

7.尽量使用基元

避免任何开销并提升应用程序性能的另外一个简便而快速的方法是使用基本类型而不是其包装类。因此,最好使用int来代替Integer,使用double来代替Double。这容许JVM将值存储在堆栈而不是堆中以减小内存消耗,并做出更有效的处理。

8.试着避免BigInteger和BigDecimal

既然咱们在讨论数据类型,那么咱们也快速浏览一下BigInteger和BigDecimal吧。尤为是后者因其精确性而受到你们的欢迎。可是这是有代价的。

BigInteger和BigDecimal比简单的long或double须要更多的内存,而且会显著减慢全部计算。因此,你若是须要额外的精度,或者数字将超过long的范围,那么最好三思然后行。这多是你须要更改以解决性能问题的惟一方法,特别是在实现数学算法的时候。这个你了解下。

9.首先检查当前日志级别

这个建议应该是显而易见的,但不幸的是,不少程序员在写代码的时候都会大多会忽略它。在你建立调试消息以前,始终应该首先检查当前日志级别。不然,你可能会建立一个以后会被忽略的日志消息字符串。

这里有两个反面例子。

// don’t do this
log.debug(“User [” + userName + “] called method X with [” + i + “]”);

// or this
log.debug(String.format(“User [%s] called method X with [%d]”, userName, i));
复制代码

在上面两种状况中,你都将执行建立日志消息全部必需的步骤,在不知道日志框架是否将使用日志消息的前提下。所以在建立调试消息以前,最好先检查当前的日志级别。

// do this
if (log.isDebugEnabled()) { 
  log.debug(“User [” + userName + “] called method X with [” + i + “]”);
}
复制代码

10.使用Apache Commons StringUtils.Replace而不是String.replace

通常来讲,String.replace方法工做正常,效率很高,尤为是在使用Java 9的状况下。可是,若是你的应用程序须要大量的替换操做,而且没有更新到最新的Java版本,那么咱们依然有必要查找更快和更有效的替代品。

有一个备选答案是Apache Commons Lang的StringUtils.replace方法。正如Lukas Eder在他最近的一篇博客文章中所描述的,StringUtils.replace方法远胜Java 8的String.replace方法。

并且它只须要很小的改动。即添加Apache Commons Lang项目的Maven依赖项到应用程序pom.xml中,并将String.replace方法的全部调用替换为StringUtils.replace方法。

// replace this
test.replace(“test”, “simple test”);

// with this
StringUtils.replace(test, “test”, “simple test”);
复制代码

11.缓存昂贵的资源,如数据库链接

缓存是避免重复执行昂贵或经常使用代码片断的流行解决方案。总的思路很简单:重复使用这些资源比反复建立新的资源要便宜。

一个典型的例子是缓存池中的数据库链接。新链接的建立须要时间,若是你重用现有链接,则能够避免这种状况。

你还能够在Java语言自己找到其余例子。例如,Integer类的valueOf方法缓存了-128到127之间的值。你可能会说建立一个新的Integer并非太昂贵,可是因为它常常被使用,以致于缓存最经常使用的值也能够提供性能优点。

可是,当你考虑缓存时,请记住缓存实现也会产生开销。你须要花费额外的内存来存储可重用资源,所以你可能须要管理缓存以使资源可访问,以及删除过期的资源。

因此,在开始缓存任何资源以前,请确保实施缓存是值得的,也就是说必须足够多地使用它们。

总结 正如你所看到的,有时不须要太多工做就能够提升应用程序的性能。本文中的大部分建议只须要你稍做努力就能够将它们应用于你的代码。

可是,最重要的仍是那些与是什么编程语言无关的技巧:

  • 在你知道必要以前不要优化
  • 使用分析器查找真正的瓶颈
  • 首先处理最大的瓶颈
相关文章
相关标签/搜索