Java性能调优的11个实用技巧
大多数开发人员认为性能优化是个比较复杂的问题,需要大量的经验和知识。是的,这并不没有错。诚然,优化应用程序以获得最好的性能并不是一件容易的事情,但这并不意味着你在没有获得这些经验和知识之前就不能做任何事。下面有几个很容易遵循的建议和最佳实践能够帮你创建一个性能良好的应用程序。
这些建议中的大多数都是基于Java的,但是也不一定,也有一些是可以应用于所有的应用程序和编程语言的。在我们分享基于Java的性能调优技巧之前,让我们先讨论一下这些通用的性能调优技巧。
1. 在必要之前,先不要优化
这可能是最最重要的性能调优技巧之一。你应该遵循常见的最佳实践,并尝试有效地实现你的用例。但这并不意味着在证明它是必要之前,替换任何标准库或构建复杂的优化。
在大多数情况下,过早的优化占用了大量的时间,使得代码难以读取和维护。更糟糕的是,这些优化通常不会带来任何好处,因为你花费了大量时间来优化应用程序的非关键部分。
那么,你如何证明你需要优化某些东西呢?
首先,你需要确定应用程序代码的速度,例如,为所有API调用指定一个最大响应时间,或者指定在特定时间范围内导入的记录数量。完成之后,你可以度量应用程序的哪些部分太慢而需要改进。当这样做之后,那么请继续看第二个调优技巧。
2. 使用分析器来找到真正的瓶颈
在你遵循第一条建议,并确定你的应用程序的某些部分的确需要改进之后,问自己从哪里开始?
你可以用两种方法来解决这个问题:
- 你可以看一下你的代码,从看起来可疑或者你觉得它可能会产生问题的部分开始。
- 或者使用分析器,获取代码中每个部分的行为和性能的详细信息。
至于为什么应该总是遵循第二种方法。
答案应该很明显,基于分析器的方法能让你更好地理解代码的性能含义,并允许你关注最关键的部分。如果你曾经使用过分析器,你将会惊讶于代码的哪些部分造成了性能问题。然而,很多时候,你的第一次猜想会把你引向错误的方向。
3. 为整个应用程序创建性能测试套件
这是另一个帮助你避免许多意想不到问题的一般技巧,这些问题通常发生在性能改进部署到生产环境之后。你应该经常定义测试整个应用程序的性能测试套件,并在你完成性能改进之前和之后运行它。
这些额外的测试运行将帮助你识别更改的功能和性能方面的影响,并确保你不会发布一个弊大于利的更新。如果你的任务运行于应用程序的多个不同部分比如数据库或缓存,这一点尤其重要。
4. 首先解决最大的瓶颈问题
在创建了测试套件并使用分析器对应用程序进行分析之后,你就有了一个需要提高性能的问题列表,这很好,但它仍然不能回答你应该从哪里开始的问题。你可以从那些可以快速搞定的开始,亦或者从最重要的问题开始。
当然前者很诱人,因为这很快就能出结果。有时,可能需要说服其他团队成员或你的管理层,性能分析是值得的。
但总的来说,我建议首先着手处理最重要的性能问题。这将为你提供最大的性能改进,而且你可能只需要修复这些问题中的几个就可以解决你的性能需求。
在了解通用性能调优技巧之后,让我们再来仔细看看一些特定于Java的调优技巧。
5. 使用StringBuilder以编程方式连接字符串
在Java中有许多不同的连接字符串的选项。例如,可以使用一个简单的+或+ =、老的StringBuffer或StringBuilder。
那么,你应该选择哪种方法呢?
答案取决于连接字符串的代码。如果你以编程方式向字符串中添加新内容,例如,在for循环中,你应该使用StringBuilder。它比StringBuffer更容易使用和提供更好的性能。但是请记住,StringBuilder与StringBuffer不同,它不是线程安全的,而且可能不适合所有用例。
你只需要实例化一个新的StringBuilder,并调用append方法在字符串中添加一个新的部分。当你添加了所有的部分后,可以调用toString()方法来检索连接字符串。
下面的代码片段展示了一个简单的示例。在每次迭代过程中,这个循环将i转换成一个字符串,并将其添加到StringBuilder sb的空间中,因此到最后,这段代码写入“this is test0123456789”到日志文件。
StringBuilder sb = new StringBuilder(“This is a test”); for (int i=0; i<10; i++) { sb.append(i); sb.append(” “); } log.info(sb.toString());
正如在代码片段中看到的,你可以为构造函数方法提供字符串的第一个元素。这将创建一个新的StringBuilder,其中包含提供的字符串和16个额外字符的容量。当你向StringBuilder中添加更多字符时,JVM将动态地改变StringBuilder的大小。
如果你已经知道自己的字符串包含多少字符,那么你可以向不同的构造函数方法提供这个数字,以实例化一个具有被定义容量的StringBuilder。这进一步提高了它的效率,因为它不需要动态扩展它的容量。
6. 在声明中使用+连接字符串
当你在Java中实现第一个应用程序时,可能有人告诉你不应该用+来连接字符串。如果在应用程序逻辑中连接字符串这是正确的。字符串是不可变的,每个字符串连接的结果存储在一个新的字符串对象中。这需要额外的内存,并降低应用程序的速度,特别是在循环中连接多个字符串时。
在这些情况下,你应该遵循tip 5并使用StringBuilder。
但如果你只是将一个字符串分解成多行来提高代码的可读性,那就不是这样了。
Query q = em.createQuery(“SELECT a.id, a.firstName, a.lastName ” + “FROM Author a ” + “WHERE a.id = :id”);
在这些情况下,你应该用一个简单的+来连接你的字符串。Java编译器将优化它并在编译时执行连接。因此,在运行时,代码只使用1个字符,不需要连接。
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’s StringUtils.replace(https://commons.apache.org/proper/commons-lang/)方法。正如Lukas Eder在他最近的一篇博客文章(https://blog.jooq.org/2017/10/11/benchmarking-jdk-string-replace-vs-apache-commons-stringutils-replace/)中所描述的那样,它大大超过了Java 8的String.replace 方法。
它只需要很小的改变。你只需要为Apache’s Commons Lang 项目增加一个Maven依赖项到你的应用pom.xml,并用StringUtils.replace方法替换所有String.replace方法的调用。
// replace this test.replace(“test”, “simple test”); // with this StringUtils.replace(test, “test”, “simple test”);
11. 缓存昂贵的资源,比如数据库连接
缓存是一种流行的解决方案来避免重复执行昂贵或频繁使用的代码片段。一般的想法很简单:重复使用这些资源比一次又一次地创建一个新的资源要便宜得多。
一个典型的例子就是在池中缓存数据库连接。创建新连接需要时间,如果重用现有连接,则可以避免。
还可以在Java语言本身中找到其他示例。例如,Integer类的valueOf方法缓存了- 128和127之间的值。你可能会说,创建一个新整数并不太贵,但它经常使用,缓存最常用的值提供了性能方面的好处。
但当你考虑缓存时,请记住,缓存实现也会产生开销。你需要花费额外的内存来存储可重用资源,因此可能需要管理你的缓存,以使资源能够访问或删除过时的资源。
因此,在你开始缓存任何资源之前,请确保是经常使用。
总结
正如你所看到的,提高应用程序的性能有时不需要做大量的工作。这篇文章中的大多数建议,其实只需要稍微的努力就可以将它们应用到代码中。
但通常最重要的建议是很编程语言无关的:
- 在你知道有必要之前,不要优化
- 使用分析器来找到真正的瓶颈
- 首先解决最大的瓶颈问题
Java性能调优的11个实用技巧相关推荐
- Java 性能调优的 11 个实用技巧
大多数开发人员理所当然地以为性能优化很复杂,需要大量的经验和知识.好吧,不能说这是完全错误的.优化应用程序以获得最佳性能不是一件容易的事情.但是,这并不意味着如果你不具备这些知识,就不能做任何事情.这 ...
- 关于 Java 性能调优的 11个简单技巧,多少人知道?
相信大多数的小伙伴都会觉着性能优化是一件很复杂的事,那么是不是呢?优化应用程序获得最佳性能并不是一件容易的事.它需要一定的经验和知识. 那么,我们现阶段还不具备这样的经验和知识,是不是就做不到了呢?也 ...
- 关于Java性能调优的11个简单技巧
点击上方关注 "终端研发部" 设为"星标",和你一起掌握更多数据库知识 源 / Java联盟 相信大多数的小伙伴都会觉着性能优化是一件很复杂的事,那么是不是呢? ...
- 11 个简练的 Java 性能调优技巧
转载自 11 个简练的 Java 性能调优技巧 想要让你的项目一直高性能运作吗?以下有一些技巧你可以拿去消除缓存瓶颈,还有一些其他的性能调优建议. 大多数开发者认为性能优化是一个复杂的话题,它需要大量 ...
- 为什么对 Java 性能调优最后都像在调 you?
不知道你有没有发现,优化Java,或者任何其他语言的代码性能经常被当做是一种暗黑艺术. 性能分析有种神秘感.画面类似是这样的:一个「黑客」经过多年练就的手艺,能够快速深入了解某个系统,并提出神奇的解决 ...
- java 性能调优_Java性能调优调查结果(第三部分)
java 性能调优 这是本系列文章的第三篇,我们将分析2014年10月进行的调查的结果.如果您尚未这样做,我建议从本系列的前两篇文章开始: 问题严重性分析和监视域分析 . 这篇文章着重于故障排除/根本 ...
- java 性能调优_Java性能调优调查结果(第二部分)
java 性能调优 这是系列文章的第二篇,我们将分析2014年10月进行的性能调整调查的结果.如果您尚未阅读第一部分,我们建议从此处开始 . 第二部分将重点监视Java应用程序的性能问题. 特别是,我 ...
- java 性能调优_Java性能调优调查结果(第一部分)
java 性能调优 我们在2014年10月进行了Java性能调优调查.该调查的主要目的是收集对Java性能世界的见解,以改善Plumbr产品. 但是,我们也很高兴与您分享有趣的结果. 我们收集的数据为 ...
- java必读书籍_最佳5本Java性能调优书籍–精选,必读
java必读书籍 为什么Java开发人员应该阅读有关性能调优的书? 当我很久以前第一次面对这个问题时,我以为以后会做,但是我很长一段时间都没有回过头来. 仅当我在用Java编写的任务关键型服务器端财务 ...
- Java性能调优调查结果(第一部分)
我们在2014年10月进行了Java性能调优调查.该调查的主要目的是收集对Java性能世界的见解,以改进Plumbr产品. 但是,我们也很高兴与您分享有趣的结果. 我们收集的数据为进行冗长的分析提供了 ...
最新文章
- Java 线程 知识
- vue.js crud_如何使用VS Code和ADO.NET使用ASP.NET Core执行CRUD操作
- 七大Github机器学习热门项目
- 幼儿园计算机教案 妈妈的爱,幼儿园大班教案《妈妈的爱》(通用)
- 自己动手实现操作系统引导程序(OS bootloader)——借助QEMU/GDB/losetup/dd等工具
- pycharm安装怎么选_客厅窗帘怎么选 客厅窗帘怎么安装好看
- 序列化对象C++对象的JSON序列化与反序列化探索
- 三网融合试点,是一个商机还是烫山芋?
- 20 Excellent AJAX Effects You Should Know
- Meta Learning | 加了元学习之后,少样本学习竟然可以变得这么简单!
- python join 和 split的常用使用方法
- 《图书管理系统》—需求分析报告
- 处理 ps cs3 打开提示 产品已停止 故障
- web前端开发初学者,html的介绍及常用标签
- java判断时间是否在某个时间段内
- GSM模块_GSM设备和网络错误代码
- android 图片虚化代码,Android模糊图片技术
- 知识点 - 快速沃尔什变换
- 零信任时代,企业如何提升自身安全?
- 中国汽车使用保险行业市场供需与战略研究报告
热门文章
- drawLine()方法画粗线
- LiquidCrystal_I2C 显示不正常 只显示第一个首字符!
- 基于python flask的网上商城源码 mysql数据库
- 软件开发团队中各个成员的英文简称
- 根服务器修改密钥,更改 SQL Server 实例的服务主密钥
- 《白帽子讲web安全》读书笔记_2021-07-16
- 【笔试题】刺客信条/Assassin’s Creed (阿里巴巴2020.08.26笔试第二题)
- RESTClient 使用
- wifi频率和zigbee干扰_怎样设置Zigbee和wifi信道避免干扰?
- TCP和UDP区别?如何改进TCP