Java性能提示

时间:2009-06-02 09:35:57

标签: java profiling performance

我有一个从C移植到Java的程序。两个应用程序都使用快速排序来订购一些分区数据(基因组坐标)。

Java版本运行速度很快,但我想让它更接近C版本。我正在使用Sun JDK v6u14。

显然我无法与C应用程序保持一致,但我想了解我可以做些什么来尽可能地提高性能(在环境范围内)。

我可以做些什么来测试应用程序的不同部分,内存使用情况等的性能?具体来说,我该怎么做?

此外,我可以实现哪些技巧(通常)来更改类和变量的属性和组织,减少内存使用并提高速度?

编辑:我使用Eclipse,显然更喜欢任何第三方工具的免费选项。谢谢!

14 个答案:

答案 0 :(得分:32)

不要试图超越jvm。

特别是:

  • 不要试图避免创建对象 为了表现

  • 使用不可变对象 适用。

  • 使用对象的范围 正确的,这样GC就可以做到 工作

  • 在你指的地方使用原语 原语(例如,非可空的int 与可空的整数相比)

  • 使用内置算法和数据结构

  • 处理并发时使用java.util.concurrent包。

  • 对性能的正确性。首先做好,然后测量,然后用剖析器进行测量,然后进行优化。

答案 1 :(得分:11)

显然,个人资料资料个人资料。对于Eclipse,有TPTP。这是关于TPTP plugin for Eclipse的文章。 Netbeans有自己的profilerjvisualvm作为独立工具很不错。 (整个dev.java.net服务器目前似乎已经停止,但它非常活跃。)

首先要做的是使用库排序例程Collections.sort;这将要求您的数据对象为Comparable。这可能足够快,肯定会提供良好的基线。

一般提示:

  • 避免使用您不需要的锁(您的JVM可能已经优化了这些锁)
  • 使用StringBuilder(不是StringBuffer,因为我刚才提到的那个锁定事物)而不是连接String个对象
  • 尽你所能final;如果可能的话,让你的课程完全不可改变
  • 如果您没有在循环中更改变量的值,请尝试将其挂出并查看它是否有所不同(JVM可能已经为您完成此操作)
  • 尝试使用ArrayList(甚至是数组),因此您访问的内存是连续的,而不是像LinkedList
  • 那样可能存在碎片
  • Quicksort可以并行化;考虑这样做(见quicksort parallelization
  • 尽可能减少数据的可见性和实时时间(但不要扭曲算法,除非分析显示它是一个巨大的胜利)。

答案 2 :(得分:5)

使用分析器:

使用提供商提供的最新版本的JVM。顺便提一下,Sun的Java 6更新14确实带来了performance improvements

测量GC吞吐量和pick the best garbage collector for your workload

答案 3 :(得分:4)

不要过早优化。

衡量绩效,然后进行优化。

尽可能使用最终变量。它不仅允许JVM     优化更多,也让你的     代码更易于阅读和维护。

如果使对象不可变,则不必克隆它们。

首先通过更改算法进行优化,然后通过更改实现进行优化。

有时您需要采用旧式技术,例如循环展开或缓存预先计算的值。记住它们,即使它们看起来不好看,它们也很有用。

答案 4 :(得分:3)

另外尝试调整VM的运行时参数 - 例如,VM的最新版本包含以下标志,可以在某些情况下提高性能。

-XX:+DoEscapeAnalysis 

答案 5 :(得分:2)

jvisualvm现在附带JDK 6 - 这就是上面引用的链接不起作用的原因。只需输入“jvisualvm< pid>”,其中< pid>是您要跟踪的进程的ID。你将会看到如何使用堆,但你不会看到填充它的是什么。

如果这是一个长时间运行的过程,您可以在运行时打开-server选项。您可以使用许多调整选项;那只是一个。

答案 6 :(得分:2)

首先需要注意的是 - 在开始任何优化工作之前,请确保已完成适当的分析或基准测试。结果通常可以启发你,并且几乎总能为你节省大量精力来优化无关紧要的事情。

假设您确实需要它,那么您可以在Java中获得与C相当的性能,但需要花费一些精力。您需要知道JVM在哪里做“额外工作”并避免这些。

特别是:

  • 避免不必要的对象创建。虽然JVM堆和GC非常快速和高效(可能是世界上最好的,并且几乎肯定比你在C中可以自己推出的任何东西更好),但它仍然是堆分配,并且将通过避免第一个堆中的堆来打败地方(堆叠或注册分配)
  • 避免使用盒装基元。您希望使用double而不是Double
  • 使用原始数组可用于任何大块数据。 Java原始数组基本上和C / C ++数组一样快(它们确实有额外的边界检查,但这通常是无关紧要的)
  • 避免任何同步 - Java线程相当不错,但它仍然是您可能不需要的开销。为每个线程提供自己的数据。
  • 利用并发性 - Java的并发支持非常好。您也可以使用所有核心!这是一个很大的话题,但有很多好书/教程可供选择。
  • 如果您有某些非常具体的要求,请使用专门的收集类来处理某些类型的数据,例如:支持一些专门的排序/搜索算法。您可能需要自己动手,但也有一些好的库可以提供高性能的集合类,可以满足您的需求 - 例如, Javoltion
  • 避免使用大课程 - 这是性能代码中的设计气味。每一层抽象都会花费你的成本。非常快的Java代码通常最终看起来像C ....
  • 使用静态方法 - JIT可以非常好地优化这些方法。它通常会内联它们。
  • 使用最终具体类 - 再次,JIT可以通过避免虚函数调用来优化这些。
  • 生成您自己的字节码 - 如果其他所有方法都失败了,如果您想要从JVM中获得绝对最大性能,这可能是一个可行的选择。如果您需要编译自己的DSL,特别有用。使用类似ASM的内容。

答案 7 :(得分:1)

如果您的算法占用大量CPU,您可能需要考虑利用并行化。您可以在多个线程中进行排序,并在以后合并结果。

然而,这并不是一个轻率的决定,因为编写并发代码很难。

答案 8 :(得分:1)

您不能使用Java库中包含的排序函数吗?

您至少可以查看两个排序函数之间的速度差异。

答案 9 :(得分:0)

方法论上,您必须对应用程序进行概要分析,然后了解程序的哪些组件是时间和内存密集型的:然后仔细查看这些组件,以提高其性能(请参阅{{3} })。

从纯粹的技术POV,你可以使用一些java-to-nativecode编译器,比如Excelsior的jet,但我要注意最近的JVM非常快,所以VM不应该以有意义的方式影响。

答案 10 :(得分:0)

您的排序代码只执行一次,例如在命令行实用程序中,只需对其进行排序或多次,例如一个响应某些用户输入而进行排序的webapp?

有可能在代码执行几次后性能会显着提高,因为如果HotSpot VM决定您的代码是热点,它可能会积极优化。

与C / C ++相比,这是一个很大的优势。

VM在运行时优化了经常使用的代码,并且它做得非常好。因此,性能实际上可以超过C / C ++。真。 ;)

但是,您的自定义比较器可能是一个优化的地方。

尝试在更昂贵的东西(例如字符串比较)之前首先检查廉价的东西(例如int比较)。我不确定这些提示是否适用,因为我不知道你的比较器。

使用Collections.sort(列表,比较器)或Arrays.sort(数组,比较器)。数组变体会更快一些,请参阅相应的文档。

正如安德烈亚斯之前所说:不要试图超越虚拟机。

答案 11 :(得分:0)

除了代码的微优化之外,可能还存在其他性能增强路径。如何使用不同的算法来实现您希望程序执行的操作?可能是一个不同的数据结构?

或者交换一些磁盘/ ram空间以提高速度,或者如果你可以在加载程序期间预先放弃一些时间,你可以预先计算查找表而不是进行计算 - 这样,处理速度很快。即,可以对其他资源进行一些权衡。

答案 12 :(得分:0)

Here's what I would do, in any language.如果样本显示您的排序比较例程在很长一段时间内处于活动状态,您可能会找到一种简化它的方法。但也许时间在其他地方。在修复任何事情之前先诊断,看看有什么坏了。如果你解决了最重要的事情,那么很有可能,那么其​​他事情将是最重要的事情,等等,直到你真正获得了相当不错的加速。

答案 13 :(得分:0)

配置并调整您的java程序和主机。大多数代码遵循80/20规则。这是20%的代码80%的时间,所以找到20%并尽可能快地完成。例如,文章Tuning Java Servers(http://www.infoq.com/articles/Tuning-Java-Servers)提供了从命令行向下钻取的描述,然后使用Java Flight recorder,Eclipse Memory Analyzer和JProfiler等工具隔离问题。