代码之家  ›  专栏  ›  技术社区  ›  wishi

通过代码实现Java性能?

  •  2
  • wishi  · 技术社区  · 15 年前

    首先,我应该提到的是,我知道性能优化可以是非常特定于项目的。我现在基本上没有面临这些特殊问题。我面临着JVM本身的一系列性能问题。

    我想知道:

    • 哪种代码优化有意义 从编译器的角度来看: 支持垃圾的示例 收集器I声明变量为 最终-非常关注PMD Eclipse的建议。
    • 最佳实践有哪些:vmargs, 堆和其他东西传递给 用于初始化的JVM。我如何得到 正确的价值观?有没有 公式还是尝试错误?

    Java自动化很多,在字节码级和很多方面做了很多优化。然而,我认为大部分都必须由开发人员计划才能工作。

    那么,如何加快Java中的程序呢?:)

    8 回复  |  直到 15 年前
        1
  •  5
  •   Mike Dunlavey    15 年前

    我经常看到这个。顺序通常是:

    1. 思考性能是关于编译器优化、big-o等等。

    2. 使用建议的思想设计软件、大量类、双向链接列表、具有上、下、左、右指针的树、哈希集、字典、调用其他属性的属性、调用其他事件处理程序的事件处理程序、XML编写、解析、压缩和解压缩等。

    3. 因为所有这些数据结构都像O(1)一样,编译器正在优化它的内部结构,所以应用程序应该是“高效的”,对吧?那么,这个小小的声音告诉我们,启动很慢,关闭很慢,加载和卸载速度可能更快,为什么UI如此缓慢?

    4. 交给“性能专家”。幸运的是,那个人发现,所有这些事情都是以推荐的方式完成的,但这就是为什么它要发疯的原因。它做所有的事情,因为它是做事情的推荐方式, 不是因为它是必需的 .

    5. 幸运的是,人们有机会重新设计其中的一些东西,使其简单化,并逐渐消除“瓶颈”。我说,“祝你好运”,因为这通常是不可能的,所以开发依赖下一代更快的处理器来消除痛苦。

    这种现象发生在每种语言中,但在爪哇,C,C,其中抽象已被推向极端。因此,无论如何,要了解最佳实践,同时也要了解 简单软件 是。通常,它包括将这些最佳实践保存到真正需要它们的环境中。

        2
  •  6
  •   Stephen C    15 年前

    从编译器的角度来看,哪种代码优化是有意义的:例如,为了支持垃圾收集器,我将变量声明为最终变量——非常遵循PMD在Eclipse中的建议。

    假设您正在讨论可以对代码进行的潜在微观优化,那么答案几乎为零。提高应用程序性能的最佳方法是运行一个探查器,找出性能瓶颈在哪里,然后找出是否有任何方法可以加快它们的速度。

    在大多数情况下,所有的经典技巧,比如声明类、变量和方法、重新组织循环、更改基元类型,都是浪费精力的。JIT编译器通常比您能做的更好。例如,最近的JIT编译器将分析所有加载的类,以确定哪些方法调用不受重载影响,而无需将类或方法声明为 final . 然后它将使用一个更快的调用序列,甚至内联方法体。

    实际上,Sun专家说,一些程序员尝试优化失败,因为它们实际上使JIT编译器难以应用它所知道的优化。

    另一方面,更高层次的算法优化是值得的…前提是探查器告诉您,您的应用程序在该代码区域花费了大量时间。

    在不常见的情况下,使用数组而不是集合是一种有价值的优化,在极少数情况下,使用对象池也是如此。但是这些优化1)会使代码更复杂,更容易出错;2)如果使用不当,会降低应用程序的速度。这些优化只能作为最后的手段来尝试。例如,如果您的分析显示 HashMap<Integer,Integer> 是CPU瓶颈还是内存占用,那么最好寻找现有的专门化 Map 地图 -喜欢库类,而不是尝试使用数组自己实现映射。换句话说,在高水平上进行优化。

    如果您花费足够长的时间或者您的应用程序足够小,那么仔细的微优化可能会使您的应用程序(在给定的JVM版本/硬件平台上)比仅仅依赖于JIT编译器更快。如果你在Java中实现一个小规模的应用程序来进行大规模的数字压缩,那么微优化的回报可能是相当可观的。但这显然不是典型的情况!对于典型的Java应用程序来说,工作量足够大,性能差异小到微优化是不值得的。

    (顺便说一下,我不知道如何声明一个变量可以对GC性能产生任何可能的影响。每次遇到变量时,GC都必须跟踪它,不管它是否是最终变量。此外,在某些情况下,最终变量实际上可以更改是一个公开的秘密,因此GC认为最终变量不会更改是不安全的。不安全,如“创建一个悬空指针导致JVM崩溃”。)

        3
  •  3
  •   SyntaxT3rr0r    15 年前

    哪种代码优化有意义 从编译器的角度来看?

    编译器无法推理的所有编译器,因为编译器非常愚蠢,Java没有“按合同设计”(因此,不能帮助哑编译器对代码进行解释)。

    例如,如果您正在处理数据并使用int[]或long[]数组,那么您可能知道有关数据的一些信息,即 不可能的 对于编译器来说,您可以使用低级的位打包/压缩来改进代码中该部分的引用位置。

    到了那里,看到了巨大的加速。对于“超级智能编译器”来说就这么多了。

    这只是一个例子。有很多这样的案例。

    记住编译器是 真的? 笨蛋:不知道 如果(Math.Abs(42)>0) 将永远返回真。

    对于那些认为编译器是“聪明”的人来说,这应该给人们一些想法。(如果Java有DbC的话,情况就不同了,但它没有。)

    有哪些最佳实践可用于: 传递给 用于初始化的JVM。我该怎么办 在这里得到正确的值?有 有什么公式,或者它是试错法?

    真正的答案是:不应该有。不幸的是,由于Java部分的严重失败,这种情况是如此的悲惨以至于需要如此低级的黑客行为。哦,还有一个“小”细节:使用vm微调只适用于服务器端应用程序。它不适用于桌面应用程序。

    任何在数百台或数千台机器上安装的Java桌面应用程序上的人,都知道什么是好的。 这个 问题是:完全GC暂停,使您的应用程序看起来像是坏了。苹果操作系统10.4上的虚拟机出现在我的脑海中,因为它是特别的afwul,但是 所有 合资公司应遵守该问题。

    更糟糕的是:当您的应用程序将在数百/数千种不同的配置上运行时,不可能在不同的OSE/VMS/内存配置中“微调”GC的参数。

    但这并不坏:首先,您不应该关心超低级别的细节,比如GC“微调”。这一事实被暗示是对Java有一个主要问题的一个领域的证明。

    但这显然是错误的。Trove的“tintingthashmap”围绕hashmap<integer,integer>循环运行是有原因的。

    -xxgc多道刻痕 那个 毕竟在庆功会上很棒。

    为了回答你的问题:你如何加快Java程序?简单,就像特洛伊人做的那样:停止不必要地创建大量的对象,停止不必要的自动(联合国)拳击原语,因为它们 取消应用程序的性能。

    TintIntHashMap 拥有 默认的hashmap<integer,integer>原因:与我的应用程序现在的原因相同 许多的 比以前快。

    我不再相信垃圾 “对象创建成本为零” “GC是超级优化的,不用担心” .

    我用Java来压缩数据(我知道,我有点疯狂),使我的应用程序更快的一件事就是停止相信围绕“便宜的对象创建”和“惊人的快速GC”的所有宣传。

    事实是: 与其尝试微调GC设置,不如先停止创建那么多垃圾。 . 这可以这样说:如果改变GC设置彻底改变了应用程序的运行方式,那么可能是时候怀疑是否真的需要你创建的所有不必要的垃圾对象了。

    哦,你知道吗,我敢打赌,我们会看到越来越多的发行说明,解释为什么Java版本X.Y.Z的GC比X.Y.Z-1的GC更快。

        4
  •  2
  •   Thorbjørn Ravn Andersen    15 年前

    通常有两种性能优化需要与Java进行:

    • 算法优化。选择一个行为符合需要的算法。例如,一个简单的算法可能对小数据集的性能最好,但是准备一个更智能的算法的开销可能首先会为更大的数据集带来回报。

    • 瓶颈识别。这里您需要熟悉一个分析器,它可以告诉您问题是什么(人类总是猜测错误)-内存泄漏?慢法?等。。。一个好的开始是visualvm,它可以附加到一个正在运行的程序,并在最新的sun jdk中提供。当你知道这个问题时,你可以解决它。

        5
  •  1
  •   Robert Roland    15 年前

    很难回答得太彻底,因为你甚至没有提到你在谈论的是什么样的项目。它是桌面应用程序吗?服务器端应用程序?

    桌面应用程序有利于应用程序的启动时间,因此Hotspot客户机VM是一个很好的开始。客户机应用程序不一定总是需要所有的堆空间,所以在开始堆和最大堆之间保持良好的平衡是有用的。(例如,可能是-xms128m-xmx512m)

    服务器应用程序支持整体吞吐量,这是Hotspot服务器虚拟机所需要的。在服务器应用程序上,您应该始终分配相同的最小和最大堆大小。在垃圾收集过程中,在系统级别上需要花费malloc()和free()来增加成本。使用类似-xms1024m-xmx1024m的内容。

    还有几个不同的垃圾收集器,它们被调到不同的应用程序类型。

    阅读 http://java.sun.com/performance/reference/whitepapers/6_performance.html 如果您想从Java 6获取更多关于垃圾收集器和其他与性能相关的项目的信息。

        6
  •  1
  •   Durandal    15 年前

    今天的JVM在性能方面惊人地强大。实际上,在所有情况下,您可以应用的任何微优化对性能的影响都非常小。如果您看看典型的语言结构(例如for vs while)是如何转换为字节码的,这很容易理解——它们几乎是不可区分的。 使方法/变量最终化对一个合适的JIT'dJVM的性能绝对没有影响。JIT将跟踪哪些方法确实是多态的,并尽可能优化动态调度。静态方法仍然可以更快,因为它们没有这样的引用=少一个局部变量(同时限制了它们的应用)。最有效的微优化不是Java特定的,例如,由于处理器的分支错误预测,带有大量条件语句的代码可能变得非常慢。有时条件可以被其他连续的代码流构造(通常以可读性为代价)替换,从而减少预测失误的分支数(这适用于以某种方式编译为本机代码的所有语言)。

    请注意,分析程序往往会夸大花费在短时间内(通常称为方法)的时间。这是因为分析程序需要检测代码以跟踪调用——这会干扰JIT内联这些方法的能力(并且检测开销明显大于实际执行方法体所花费的时间)。手动内联,虽然在剖析器下明显的性能提升在大多数情况下在“真实世界”条件下没有效果。不要纯粹依赖于分析程序的结果,请验证您所做的优化在实际运行时条件下也具有实际影响。

    显著的性能提升只能通过减少完成的工作量、更适合缓存的数据布局或更高级的算法来实现。Java部分限制了缓存友好数据布局的可能性,因为您无法控制构成数据结构的部分(数组/对象)将在内存中彼此相关。尽管如此,在为工作选择正确的数据结构时还是有很多机会可以产生巨大的差异(例如,ArrayList和LinkedList)。

    你几乎无能为力去帮助垃圾收集器。然而,值得注意的一点是,虽然Java中的对象分配非常快,但仍然存在对象初始化的成本(主要是在您的控制之下)。创建大量(短寿命)对象的应用程序的性能差更可能是由于缓存利用率差而不是垃圾收集器的工作造成的。

    不同的应用程序类型需要不同的优化策略-因此,在询问特定的优化之前,先了解应用程序真正花在哪里。

        7
  •  1
  •   Mike Tunnicliffe    15 年前

    如果您的应用程序遇到性能问题,您应该认真考虑尝试一些分析(例如:hprof),以查看问题本质上是否是算法问题,并检查GC性能日志(例如:verbose:gc),以查看是否可以从调优JVM GC选项中获益。

        8
  •  1
  •   Peter Lawrey    15 年前

    值得注意的是,编译器几乎没有优化,而JVM也没有在字节代码级别进行优化。大多数优化是由jvm中的jit执行的,它选择如何将代码转换为本机代码。

    优化代码的最佳方法是使用事件探查器,它可以测量应用程序在提供实际数据集时使用的时间和资源量。没有这些信息,你只是猜测,你可以改变大量的代码,在那里它真的,真的不重要,并发现你在这个过程中添加了错误。

    许多人得出的结论是,选择代码是不值得的,甚至会适得其反,因为这样做会浪费时间并引入错误,我认为95%以上的代码都是这样的。但是,有了Approviler,您可以测量关键的代码片段,并优化价值5%的优化,并仔细完成,您不会因为尝试优化代码而遇到太多问题。