Java,JIT和垃圾收集器的效率

时间:2011-04-09 18:37:53

标签: java garbage-collection performance jit

我想了解Java的效率以及Java虚拟机和Android的优缺点。 效率是内存使用率低,处理器使用率低和执行速度快。

移动设备比PC更简单,然后应用程序需要更高效。服务器接收许多连接,它们需要非常高效。许多移动设备使用Android和Java应用程序,许多服务器使用PHP。

Java和解释语言(如Java Script,Python和PHP)能否比C和C ++更有效?

JIT(及时)优势:

  • 它可以更好地进行优化,因为它知道某些变量的价值以及使用或更改的位置。
  • 它了解处理器并可以使用特定于处理器的指令进行优化。
  • 将函数转换为内联函数更容易。
  • 它可以删除已知的条件测试并删除不会运行的块。

Java缺点:

  • 当应用程序第一次运行时,应用程序将非常慢,因为字节码将被解释,JIT编译器将进行许多分析以找到良好的优化。应用程序无法使用最大的硬件电源。如果一个应用程序是一个游戏或一个实时应用程序,如果它是第一个成功运行并且没有延迟,但它使用最大的硬件功率,那么下次运行应用程序时,它将不会使用由于优化而产生的硬件功率最大值。问题是应用程序无法设计为在优化后使用最大的硬件功率,因为​​它在第一次运行时会太慢,并且不会继续运行。
  • Java检查数组索引是否不在边界内,并检查指针是否为空。它会为生成的代码添加几个内部“if”。
  • 所有对象都使用垃圾收集器,包括非常容易手动删除的对象。
  • 所有对象实例都是使用动态内存分配创建的,包括可以轻松使用堆栈的对象。如果循环迭代开始创建类的实例并结束删除创建的对象,则动态内存分配将是低效的。
  • 垃圾收集器需要在清理内存时停止应用程序,这对于游戏,GUI应用程序和实时应用程序来说是非常不受欢迎的。引用计数很慢,无法处理循环引用。多线程垃圾收集器速度较慢,需要更多地使用CPU。

5 个答案:

答案 0 :(得分:5)

  

Java和解释语言(如Java Script,Python和PHP)能否比C和C ++更有效?

比最好的C和C ++程序更有效率。有很多C和C ++程序远不如此高效,如果你有任何好处,用(现代)Java代码击败它们是非常实用的。 我也听说过目前最好的Javascript引擎,但我从来没有详细研究它们。

使用Python和PHP(以及许多其他语言),它有点不同。这些语言是用C 编写的,所以很明显它们不能比C更高效(按照构造)。然而,在它们中编写高效代码(即,使用实际上是一个编写得非常好的C库)比从头开始编写代码要容易得多。特别是,它减少了每个程序的缺陷数量。这是实践中非常重要的指标;如果允许错误,任何人都可以生成快速代码。

总的来说,我建议不要担心获得最大效率。你违反了收益递减法则。相反,使用合理的整体算法(或者,正如我的朋友曾经对我说的那样,“照顾大O()并让恒定因素照顾自己”)并关注程序是否足够好的问题在实践中。一旦它出现,停止摆弄并运送它!

答案 1 :(得分:3)

让我们挑选你声称的缺点:

  
      
  • 当应用程序第一次运行时,应用程序将非常慢,因为字节码将被解释,JIT编译器将进行许多分析以找到良好的优化。应用程序无法使用最大的硬件电源。
  •   

JIT编译是一个实现问题。并非所有平台都这样做。实际上,Android平台可以修改为1)提前完成编译,或者2)缓存JIT生成的本机代码,以便在下次运行应用程序时提供更快的启动。

有趣的是,各种Java供应商已经在不同时间尝试过这些策略,但实证证据表明,简单的JIT是最好的策略。

  
      
  • Java检查数组索引是否不在边界内,并检查指针是否为空。它会为生成的代码添加几个内部“if”。
  •   

JIT编译器可以优化许多这些测试。其余的,间接费用往往相对较小;例如百分之几的差异......不是2的因素。

请注意,检查的替代方法是典型应用程序错误将导致Android平台崩溃的风险。当然,如果应用程序可以摧毁内存,垃圾收集就会出现问题。

  
      
  • 所有对象都使用垃圾收集器,包括非常容易手动删除的对象。
  •   

另一方面,很容易忘记删除对象,删除对象两次,删除后再使用它们等等。这些错误都会导致很难找到的错误。

  
      
  • 所有对象实例都是使用动态内存分配创建的,包括可以轻松使用堆栈的对象。如果循环迭代开始创建类的实例并结束删除创建的对象,则动态内存分配将是低效的。
  •   

Java动态内存分配和对象创建是快速的。例如,比C ++更快。

  
      
  • 垃圾收集器需要在清理内存时停止应用程序,这对于游戏,GUI应用程序和实时应用程序来说是非常不受欢迎的。
  •   

然后使用并发/低暂停垃圾收集器。另一种方法是实现你的应用程序不会产生大量垃圾......并且很少触发垃圾收集。

  
      
  • 引用计数很慢,无法处理循环引用。
  •   

没有合适的Java GC使用引用计数。 (另一方面,很多C / C ++手动内存管理方案都有。例如,C ++中所谓的智能指针方案。)

  
      
  • 多线程垃圾收集器速度较慢,需要更多地使用CPU。
  •   

你实际上是指我认为的并发收集。是的确如此,但这是您为交互式游戏/实时应用程序所需的额外响应所付出的代价。

答案 2 :(得分:2)

你所描述的“有效”我称之为“理想”。一个需要很少内存,很少CPU时间并且运行速度快的应用程序,另一种方式,是一个既好又快又便宜的应用程序。不管它是否有用或有趣。

如果需要所有三个目标,我认为合理的唯一比较是产生共同结果的应用程序之间。在这种情况下,鉴于一个具有统一能力的程序员竞争团队,任何一项实施都不可能在所有三项计划中优于其他计划。

那就是说,你的问题遗漏了移动市场的一个关键标准:应用程序开发速度。与后端优化相比,移动应用程序从积极的用户体验中获益更多。如果没有这种限制,你提出的效率问题在我看来似乎比实际问题更为重要。

但是对于实际的问题:像Java这样的语言能否产生比静态编译到目标机器指令集的代码更高效的代码?可能不是。它可以有效还是高效?绝对。如果我们认为具有固定的,严重受限的资源的执行平台不经常变化,那将是另一回事。

答案 3 :(得分:1)

在任何语言中,快速执行的方法是尽可能少地执行工作,并尽可能少地进行垃圾收集。

这听起来像是一种空洞的普遍性,但无论语言如何,它在实践中意味着什么

  1. 对于数据结构设计,请尽可能简单。远离花哨的收集课程,充满了钟声和口哨声。特别是远离通知,以保持数据的一致性。如果您的数据已标准化,则永远不会出现不一致。如果你无法将其标准化,那么最好容忍暂时的不一致,而不是试图通过通知来保持紧张。

  2. 性能问题蔓延,即使是最好的代码。你应该尽量不做它们,但你仍然会制造它们。最重要的是知道如何找到它们,一旦制作,并将其删除。 Here's a blow-by-blow example.如果这样做,你会发现你需要一个更好的大O算法,然后把它放进去。在没有确定需要的情况下放入一个是缓慢的一个方法。

  3. 没有任何语言可以挽救程序免于未移除的性能问题。语言及其编译器,JITter等就像一匹赛马。想要一匹好马,这很好,但如果骑师不是那么苗条,那就太浪费了。 你的计划是骑师,你的工作就是参加减肥计划。

答案 4 :(得分:1)

我将在书中Masterminds of Programming粘贴一个由James Gosling自己给出的有趣答案。

  

嗯,我听说过这么说   实际上你有两个编译器   Java世界。你有编译器   到Java字节码,然后你有   你的JIT,基本上重新编译   一切都是特别的。所有的   你可怕的优化是在   JIT

     

詹姆斯:完全正确。这些天我们都是   打败真正优秀的C和C ++   编译器几乎总是。当你   转到动态编译器,你得到   编译器的两个优点   在最后一刻跑。一   你知道什么是芯片组吗?   你正在奔跑。这么多次   人们正在编译一块C   代码,他们必须编译它才能运行   关于通用x86的种类   建筑。几乎没有   你得到的二进制文件特别好   调整了他们中的任何一个。你下载了   Mozilla的最新版本,它会   几乎任何英特尔都可以运行   架构CPU。有很多   一个Linux二进制文件这很通用,   它是用GCC编译的   不是一个非常好的C编译器。

     

当HotSpot运行时,它确切地知道   你在运行什么芯片组。它   确切地知道缓存是如何工作的。它   确切知道内存层次结构   作品。它确切地知道所有的   管道互锁在CPU中工作。   它知道什么指令集   这个芯片有扩展。它   精确地优化什么机器   轮到你了。然后是另一半   是它实际上看到了   应用程序正在运行。它能够   有统计数据知道哪些   事情很重要。它能够   C编译器可以内联的东西   永远不会做。得到的东西   在Java世界中内联很漂亮   惊人。然后你抓住那个   存储管理的工作方式   现代垃圾收集者。有了   现代垃圾收集器,存储   分配非常快。

Masterminds of Programming