在提高Java性能时应该注意什么?

时间:2009-07-06 18:13:02

标签: java performance

我在一个大项目的维护团队工作(大约7k +班级),我的日常工作主要是修复错误。但有时候,我没有工作的错误。当发生这种情况时,我花了大部分时间来寻找代码中的性能差距。事实上,我有超过7千个课程,这意味着找到这些差距并不明显。

所以我想知道在尝试提高系统性能时应该寻找哪些简单的东西?

我不是在询问具体的代码技术,而是一般的代码技术。例如:

  • 我已经查找了String a = new String("")之类的所有代码,并更改为StringBuilder a = new StringBuilder();
  • 我已经更改了对数据库的所有访问权限(如果适用),以使用PreparedStatement
  • 删除了所有Debug日志记录,并在可能的情况下删除了Finest日志记录

正如您所看到的,可以轻松地进行这些更改,因为它们不需要测量系统性能 - 我唯一需要做的就是使用Eclipse中的搜索工具。

26 个答案:

答案 0 :(得分:56)

Laudable目标,但您需要关注实际可证明的性能问题 - 而不是您认为的“性能问题”。

花时间在探查器中查找真正的问题......然后从那里开始。否则,您只是在不知道自己是否会产生可衡量的影响的情况下搅拌代码。

即使您有一份“在不衡量系统性能的情况下需要改变的事项”清单,您真的相信它们适合您的情况吗?

在您的情况下,我建议您花时间构建测试工具/性能仪器,这样您就可以看到哪里可以获得最大的收益。

编辑: 为了解决关于“我知道使用PreparedStatement更快”的下行和情绪 - 而不是要求获得银子弹,在面对这个问题时要问一个更好的问题是“我应该如何最有成效地利用我的空闲时间来让事情变得更好?“ OP显然希望改善这种状况 - 这很好......但是如果没有测量“它受伤的地方”,他就会在黑暗中进行射击。 PreparedStatement更快吗?当然 - 但如果真正的性能gremlin在其他地方,那么为什么花时间“修复”数据库代码,当你可以通过追求实际的痛点来产生真正的影响?

另一件事:在OP所描述的稳定系统中,由于引入了风险,在没有良好的可量化理由的情况下进行代码更改通常被认为是不好的做法。在这种稳定的系统中,风险/回报的问题是必须考虑任何代码变更。风险很大:许多“简单,无法破解”的变化已经导致发布时间表/引入重大问题。奖励?不确定,因为您实际上并不知道您的更改是否对性能提升负责。因此,我们要确保我们正在改进重要的代码。

答案 1 :(得分:12)

您可能希望使用以下某些静态分析工具来识别代码中的任何错误/潜在错误,并修复这些错误,而不是关注性能。这些有时有助于识别性能问题:

这两个都包括Eclipse插件。

答案 2 :(得分:8)

不要盲目改变,因为它们“看起来”像是可行的。

想一想:

 logger.debug("Initializing object state " + object.initialize() );

如果您只是盲目地删除该语句,则该对象将不会被初始化。

当然这样的陈述首先是错误的,但是,相信我他们存在!!!!如果发生类似这样的事情,你的生活会变得悲惨。

最好使用分析器,找出哪些对象/方法/调用消耗更多时间/内存/等等,并尝试识别瓶颈。

如果你有> 7k类很可能你只修复了一堆根本没用过的代码。

配置文件!!!!

答案 3 :(得分:5)

一般情况下,只需查看代码库并尝试通过查找某些内容来提高性能,并不能保证您可以获得可衡量的性能提升。

通常情况下,使用分析器查找哪些代码段运行最多,或者相反,哪些代码运行时间最长,然后查看优化这些区域的方法更为有利。这将产生最大的好处。

答案 4 :(得分:4)

听起来像是在寻找性能反模式。这很好,但除非你把它们放在你的应用程序的上下文中,否则它只是感觉良好的编码

  

我直接用该方法调用了3秒钟。我不在乎它每12年只在一次批处理作业中运行一次。

如果您真的关心性能和用户感知的内容,无论他们是直接用户,运营团队还是黄金捐赠者,这都是您在提高绩效时应该关注的内容:

调用树

Invocation tree

内存监视器

Memory monitor

调用图

Call graph

请,Google "Java profiler"

答案 5 :(得分:4)

不要只是环顾代码并改变一切。 正如其他人所说,在你已经证明它们的位置之前,不要解决性能问题。

I use this simple method.

有7000多个课程,我敢打赌你的系统设计过度,并且你有太多层抽象门的性能问题。

简单的函数和方法调用看起来是无辜的,事件处理代码被认为是“前沿”,但如果你运行它并等到它变慢,那么“暂停”它几次,你看到这样的事情:

  • 修改数据库
  • 由于初始化数据结构
  • 在创建/破坏窗口的过程中
  • 在更改树浏览器中的连接的过程中
  • 由于重新排列列表中的元素
  • 由于剪切/粘贴操作
  • 由于有人设置了属性
  • 由于有人设置了“修改”位
  • blah,blah,blah ......

有时深度为20-30级。

如果可以避免多个样本中出现的任何这些层,将节省大部分执行时间。

答案 6 :(得分:4)

如果出现性能问题,那么@ DarkSquid和@ AlbertoPL的建议是正确的。但是,如果没有,也许您的时间会更好地用于准备未来修改的代码。就像分析测试覆盖率一样,特别是单元测试覆盖率,例如评估圈复杂度或仅查看报告错误(或最大类或其他一些简单指标)的类。像这样的主动分析可以在那个时候使维护更容易。

答案 7 :(得分:3)

我建议使用质量工具。我的商店使用声纳。它可以帮助你:

  • 找到重复的代码
  • 找到复杂的代码区域
  • 查找代码规则违规和潜在错误
  • 查看代码覆盖率
  • 查看未记录的代码

http://sonar.codehaus.org/

答案 8 :(得分:3)

您可以使用FindBugs等静态分析工具。 javac编译器已经尝试优化一些东西。诸如字符串连接之类的东西已经由编译器优化并转换为字符串构建器。

如果有疑问,没有什么比分析器更好,但要注意过早优化。

答案 9 :(得分:3)

在性能方面,确保首先遇到真正的问题,如果有,请使用TPTP或JMeter等分析器[编辑:HPJMeter曾经是一个通用的Java性能工具,但现在它是特定于HP / UX的]。直觉是一个非常糟糕的指南。

务必描述真实的测试场景。然后将注意力集中在统计数据顶部显示的方法上。泡沫,冲洗,重复。还要预先确定何时停止:当性能令人满意时,您不希望浪费时间进行微观优化,使您的代码更加模糊。

寻找算法优化,而不仅仅是低级Java编码调整。它们会产生巨大的影响。

请务必阅读Java Performance Tuning了解策略和想法。

请注意,随着应用程序的升温,它将运行得更快(例如,它不再需要进行类加载,一次初始化,JIT编译)。

我曾经花了几个月的时间将基于Java的VoiceXML浏览器的性能提高了三倍,以降低使用它的网站的硬件成本。我一次又一次地惊讶于热点所在的地方。所以@DarkSquid建议,不要猜测,测量。

答案 10 :(得分:2)

优化规则

  1. 不要这样做。
  2. 测量两次。
  3. 你不应该来这里。
  4. 不要微观优化。寻找像您使用的容器一样的算法复杂性。

答案 11 :(得分:1)

一般来说:

  1. 查看复杂的代码部分,看看是否可以清理它们

  2. 特别注意循环,看看是否可以改善其中的任何一个

  3. 递归调用通常也是性能上的重要提示。确保正确处理递归并确保任何递归都是合理的。

答案 12 :(得分:1)

使用分析器,让它告诉您最常使用的位置和花费的时间。然后你肯定知道什么需要注意,并且可以迭代地改进系统的目标区域。

答案 13 :(得分:1)

所有最佳性能改进都将是算法改进。

答案 14 :(得分:1)

我同意其他人只会优化代码SHOWN变慢(通过分析器,使用Java 6 u 10以及之后的jvisualvm非常容易上手)。

如果你还需要做其他事情,这里有一些我非常肯定没有完成的事情:

  • 所有课程的好javadoc。这有助于未来的维护者更快地理解代码。在此过程中允许简单的重构。
  • javadoc页面的官方位置(因此开发人员可以将代码链接到它们,允许像Eclipse中的Shift-F2一样轻松导航)
  • 测试库函数。基本上这是ALSO文档,因为它清楚地演示了如何使用库以及可以预期哪些边框。
  • 找出一种运行测试并自动重新生成javadoc的方法。
  • 找出一种自动运行/测试/压力应用程序的方法。 (鼠标单击GUI,在Web服务器上调用大量请求等)。

这些项目符号中的任何一个都会改进代码库,而不会不必要地实际更改代码。

答案 15 :(得分:1)

正如之前的一些人所说,你可以使用FindBugs来消除最明显的与性能相关的“错误”。您可以快速识别许多麻烦的代码片段。

您可以查看FindBugs网站http://findbugs.sourceforge.net/bugDescriptions.html上的列表。

答案 16 :(得分:1)

也许一个好的起点是尝试查看应用程序可能会破坏或违反任何SLA的位置。如果没有任何关于性能的具体投诉,请尝试提高性能要求并查看代码的哪些部分会导致问题。

如果您具有时间敏感功能,请尝试在更大的系统负载或更严格的限制下进行测试。如果您有大空间要求,请增加数据大小或限制堆空间。如果您在这些场景中遇到问题,请修复这些热点。

虽然这可能不会对您日常的日常表现产生影响,但它会确保您的系统在系统负载或输入达到峰值时仍然可用。

答案 17 :(得分:1)

Java编译器也擅长嗅探性能改进,可能比任何一个人都好。因此,虽然有一些显而易见的地方可以改进,但也很有可能让编译器更难以优化。在编译之后分析和识别的瓶颈要好得多,并专注于那些。然后,解决方案可能是算法而不是简单地更改类名。总而言之,寻找一个好的剖析器。 :)

答案 18 :(得分:1)

我会改变

  

String a = new String(“”);

     

     

字符串a =“”;

找到重新创建对象的所有位置,并找出是否可以返回相同的值。

阅读有效的Java第2版专用章节。

答案 19 :(得分:1)

首先,只尝试在您知道性能耗尽的位置提供性能增强功能。这只能使用分析器确定。在这方面,有一些不错的tools可能会有所帮助。

其次,用StringBuilders替换Strings并不能提高性能。事实上,在许多情况下,这可能会导致经济放缓。当你将一个大字符串构建为一个循环的一部分时,你应该只使用stringbuilders - 甚至只作为一个更大的运行循环的一部分。在所有其他情况下,简单连接通常更快。

答案 20 :(得分:1)

您应该尝试专注于应用程序的“不良”部分,而不是关注应用程序的“慢”部分。

有一个自动工具可以帮助您找到where your code behaves like CRAP

CRAP真的是该工具的首字母缩写!它做了一些有用的事情,比如检查圈复杂度,但确实让你看了10英尺的代码

另外一个好的java分析器可以帮助你找到瓶颈,如果真的有。

答案 21 :(得分:0)

虽然通常只花时间优化实际上存在问题的代码通常是一个好主意,但是你可以做一些事情。

规范示例在C中。考虑循环

for (int ct=0; ct<strlen(str); ++ct) { ... }

此处strlen实际扫描char数组,查找NUL终结符字符。这是一个O(n)操作,使整个循环为O(n ^ 2)。因此,在C中,将strlen外部带const int len = strlen(str);。你可以在Java中做同样的事情(我这样做),但它对性能的影响微不足道。根据品味编码。

回到真正适用于Java的事情:

  • 写好代码。严重的是,代码混合在一起往往会隐藏性能问题。如果您首先进行优化,那么您将无法轻松地看到有用的优化。
  • 了解并使用这些库。他们可能很快,而且更有可能被预热。
  • 注意选择数据结构(这实际上并不是有用的建议,但需要说明。)
  • 考虑数据结构正在使用的内存量。这通常会被微基准测试隐藏起来并影响非本地系统性能。
  • 使用ArrayList代替LinkedList。您可以在此处和其他地方找到详细信息,但即使您认为速度很快,LinkedList也会很慢。
  • 考虑应用程序性能的哪个部分至关重要。例如,响应鼠标移动可能总是很好,但打开一个对话框可能会有明显的延迟(在共享服务器上不那么重要)。
  • 请记住,CPU比缓存更快,主内存更快,比本地网络/磁盘更快,比互联网更快,比网络服务更快。
  • 考虑悲伤的情况以及幸福的情况。

答案 22 :(得分:0)

如果您的代码使用多个关键数据结构,尤其是map / sets /涉及大量查找的任何内容,您可能需要确保以最佳方式使用它们。

例如,如果您使用的是基于散列的集合,那么在生成统一分布方面,您的散列函数有多好?这通常不是问题,但在少数情况下,它可以添加一个很好的保存,因为您的哈希表可能基本上作为链表执行。

此外,请记住计算哈希和相等并不是免费的。检查计算哈希的效率。假设您有多个数字字段和多个字符串字段;仅根据数字字段计算散列可能不会产生很好的散列,但可以节省散列整个字符串的成本。同样,看看你是否可以重新排序你的等于检查以便更便宜并且更有可能首先失败测试,​​特别是如果你的等于是自动生成的。

同样的问题扩展到基于比较的集合(例如,TreeSet),看看你是否可以优化比较函数可能是有意义的。例如,您可以先进行更便宜的比较吗? (假设您只使用比较功能)。

答案 23 :(得分:0)

我要说的是寻找系统性能缓慢的区域。除了提高性能之外,我天生就有点怀疑天真改善性能的尝试。我会说你在代码中寻找适当的位置要好得多好吗重构(这听起来像是你的一些人;但我并不想迂腐;重构是一件非常好的事情,但它不是也不应该与性能改进相混淆;重构可以提供引入性能改进的机会,但它们是不同的东西。)

  

“过早优化是其中的根源   所有邪恶。“ - Donald Knuth

答案 24 :(得分:0)

我首先要看看更新/更快的硬件是否可以解决您的问题。尽管人们可能希望优化代码,但将软件移动到更快的服务器通常更经济。我喜欢使用DaCapo bennchmarking工具来比较硬件上的Java性能。我还保留了hardware which I've tested的运行列表。

答案 25 :(得分:0)

您可以查看FindBugs等工具,但他们不会修复您的代码。我建议你试试免费的IntelliJ社区版。这将找到可能的性能问题,并为您提供快速修复。

但是,最好的方法是使用性能分析器。描述您的程序的实际样本,这通常指向您可以做的简单的事情来提高性能。也就是说,只有在剖析器中找到的顶级东西才值得优化,剩下的就不够了,值得改变。