11个简单的Java性能调优技巧

想要保持程序高效运行?您可以采取一些步骤来消除瓶颈,缓存提示以及其他性能调整建议。
大多数开发人员期望性能优化是一个复杂的主题,需要大量的经验和知识。好的,那不是完全错误的。优化应用程序以获得最佳性能并非易事。但这并不意味着如果您没有获得该知识就不会做任何事情。有一些易于遵循的建议和最佳实践,可以帮助您创建性能良好的应用程序。
这些建议大多数都是特定于Java的。但是,还有几种独立于语言的语言,可以将其应用于所有应用程序和编程语言。在获得特定于Java的性能调优技巧之前,让我们先讨论其中的一些通用技巧。

1.在知道必要之前不要进行优化

这可能是最重要的性能调优技巧之一。您应该遵循常见的最佳做法,并尝试有效地实现用例。但这并不意味着您在证明有必要之前就应该替换任何标准库或构建复杂的优化。
在大多数情况下,过早的优化会占用大量时间,并使代码难以阅读和维护。更糟的是,这些优化通常不会带来任何好处,因为您要花费大量时间来优化应用程序的非关键部分。
那么,如何证明需要优化某些东西?
首先,您需要定义应用程序代码的速度,例如,通过指定所有API调用的最大响应时间或要在指定时间范围内导入的记录数来定义应用程序代码。完成此操作后,您可以衡量应用程序的哪些部分太慢并且需要改进。完成之后,您应该看看第二个技巧。

2.使用探查器查找真正的瓶颈

在遵循了第一条建议并确定了需要改进的应用程序部分之后,请问自己从哪里开始?
您可以通过两种方式解决此问题:
您可以看一下代码,从看起来可疑的部分开始,或者在可能会造成问题的地方开始。
或者,您使用探查器并获取有关代码各部分的行为和性能的详细信息。
我希望我不需要解释为什么您应该始终遵循第二种方法。
显然,基于事件探查器的方法可以使您更好地了解代码的性能含义,并使您可以专注于最关键的部分。而且,如果您曾经使用过探查器,您会记得一些情况,您对代码的哪些部分造成了性能问题感到惊讶。我的第一次猜测不止一次会把我引向错误的方向。

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

这是另一个通用技巧,可帮助您避免在将性能改进部署到生产后经常发生的许多意外问题。您应该始终定义一个性能测试套件,以测试整个应用程序,并在进行性能改进之前和之后运行它。
这些额外的测试运行将帮助您确定所做更改的功能和性能方面的副作用,并确保您不会发布造成弊大于利的更新。如果您要处理应用程序的多个不同部分所使用的组件(例如数据库或缓存),则这一点尤其重要。

4.首先解决最大的瓶颈

创建测试套件并使用事件探查器分析应用程序之后,您将获得要解决的问题列表,以提高性能。很好,但是仍然无法回答应该从哪里开始的问题。您可以专注于快速获胜,或者从最重要的问题开始。
从快速获胜开始可能会很诱人,因为您很快就可以显示首个结果。有时,可能有必要说服其他团队成员或您的管理层认为进行性能分析是值得的。
但总的来说,我建议从头开始,并首先着手处理最重要的性能问题。这将为您提供最大的性能改进,并且您可能不需要解决多个问题即可满足您的性能要求。
足够了解一般的性能调优技巧。让我们仔细看看一些特定于Java的代码。

5.使用StringBuilder以编程方式连接字符串

在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,其中包含提供的String以及16个其他字符的容量。当您向StringBuilder添加更多字符时,JVM将动态增加StringBuilder的大小。
如果您已经知道String包含多少个字符,则可以将该数字提供给不同的构造方法,以实例化具有定义容量的StringBuilder。由于它不需要动态扩展其容量,因此进一步提高了效率。

6.使用+在一个语句中连接字符串

当您使用Java实现第一个应用程序时,可能有人告诉您,不应将String与+串联在一起。如果在应用程序逻辑中串联String,那是正确的。String是不可变的,每个String串联的结果存储在新的String对象中。这需要额外的内存并减慢您的应用程序的速度,尤其是当您在循环中串联多个String时。
在这些情况下,应遵循技巧5并使用StringBuilder。
但是,如果您只是将String分成多行以提高代码的可读性,则不是这种情况。
Query q = em.createQuery(“SELECT a.id, a.firstName, a.lastName ”

  • “FROM Author a ”
  • “WHERE a.id = :id”);

在这种情况下,应将String与简单的+串联在一起。您的Java编译器将对此进行优化,并在编译时执行串联。因此,在运行时,您的代码将仅使用1 String,并且不需要连接。

7.尽可能使用基元

避免任何开销并提高应用程序性能的另一种快速简便的方法是使用原始类型而不是其包装器类。因此,最好使用int而不是Integer,或者使用double而不是Double。这使你的JVM来 的值存储在堆栈,而不是堆的,以减少内存消耗和更有效的整体处理。

8.尽量避免使用BigInteger和BigDecimal

正如我们已经在讨论数据类型一样,我们还应该快速浏览一下BigInteger和BigDecimal。尤其是后者由于其精确性而受欢迎。但这是有代价的。  
与简单的long或double相比,BigInteger和BigDecimal需要更多的内存,从而大大降低了所有计算的速度。因此,如果您需要更高的精度,或者您的数字将超出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在 他最近的一篇博客文章中所描述的那样,它的性能大大优于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并不太昂贵,但是它的使用频率很高,因此缓存最常用的值可以提供性能收益。
但是,当您考虑缓存时,请记住,缓存的实现还会增加开销。您需要花费更多的内存来存储可重用的资源,并且可能需要管理缓存以使资源可访问或删除过时的资源。
因此,在开始缓存任何资源之前,请确保您经常使用它们以超过缓存实现的开销。
摘要
如您所见,有时不需要太多工作即可提高应用程序的性能。这篇文章中的大多数建议只需要少量的额外工作即可将它们应用于您的代码。
但是像往常一样,最重要的建议是与语言无关的:
在知道必要之前不要进行优化
使用探查器找到真正的瓶颈
首先解决最大的瓶颈
最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。在这里插入图片描述

发布了98 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhaozihao594/article/details/104295281
今日推荐