Java的性能 (一)

程序的性能是个经常被提及的话题,不论是比较算法还是比较编程语言,还是系统, 程序的性能都是避不开的话题。


抛开具体的程序语言,性能是和具体的执行环境紧密相关的,CPU负载,内存使用, IO负载都可以影响性能,这也是我们在测试一段程序性能的时候,每次返回的值都稍微不一样的原因。


不同人对性能的理解也有偏差,例如在金融交易系统中,性能的指标往往是一个很小的操作,比如订单处理需要几个毫秒,还是多少个微妙。 在更多的系统中,性能的指标往往是一个很大的操作(包括很多小操作)需要几十,几百毫秒,甚至是几秒几分钟。 在第一种情况中,其实我们关注的更多是延时,在后一种情况,更多的是关注吞吐量。


作为Java程序员,性能是个经常被提及的问题,不少人说Java是在虚拟机里面解释执行的,当然比C/C++慢啦,有人会举以前做过Java系统的例子来表明Java有多么的慢。 网络上也有不少的语言的性能比较测试,不少结果表明Java和C/C++性能相当。 面对这些质疑和别人的测试结果,我们可能还真的会回去写一段Java程序和C++程序跑一下来实际比较一下,可能发现Java确实慢很多, 也可能发现差不多。


我也有过很多类似的质疑和困惑,有时候还会担忧自己是不是选择了一个有致命瓶颈的技术。在网络上搜索了几圈之后,焦点都指向了Java虚拟机本身。  


Java程序员都应该知道我们写的".java" 结尾的代码文件,经过javac编译成很多".class" 文件,里面的内容叫java字节码。 这些.class文件可以用jvm的启动程序“java”来运行。 

 

//D:/HelloWorld.java
public class HelloWorld {
   public static void main (String []args) {
       int number = Integer.parseInt("100");
       System.out.println("value is " + number);
   }
}

D:\>javac HelloWorld.java

生成了HelloWorld.class,我们可以通过javap来查看class文件


D:\>javap -c HelloWorld
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 100
       2: invokestatic  #3                  // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
       5: istore_1
       6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: new           #5                  // class java/lang/StringBuilder
      12: dup
      13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      16: ldc           #7                  // String value is
      18: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: iload_1
      22: invokevirtual #9                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      25: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      28: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      31: return
}


Java性能是否好,往往取决于我们的jvm怎么来处理字节码文件。 一个相同的程序,用java 1.5 跑可能会比java1.8 慢上30%。 java的每次大版本更新通常都有一些性能的优化,我们只需要用上比较新的版本就可以获得免费的性能提升。


在java1.3以前,jvm都是以解释器的方式来运行程序,那时候的java性能可能要比C/C++慢上10倍。 1999年java1.31 包含了一个叫做hotspot的虚拟机实现,极大地提高了性能。 简单说就是hotspot可以监测我们程序中那些非常hot或者说是被调用了很多次的方法,把这些方法编译成高效的原生指令,在再次执行这些方法时可以直接调用原生指令。 这是一种全新的混合执行模式, 不常用到的方法以低效的解释的方式执行, 常用的方法以高效的原生指令来执行。 JVM会持续检测编译后方法的执行效率,并尝试以不同的优化方法重新编译来提高性能。


有人要问,那为什么不都编译执行呢,这样的性能是不是更高呢? 如果真的都用编译的方式来执行,我们会损失2个东西,一个是启动速度,因为需要在每次启动的时候都将所有的class文件编译, 第二个是无法动态优化性能,因为从原生的指令太低级,以至于丢失了上下文,基本没什么优化空间。


通过jvm执行机制,我们了解到了java的性能在jvm里面就是动态的。 回到我们的程序上来,我们就可以知道要测试java程序的真实性能就需要确保那些调用最多的方法都在jvm里面得到了编译和足够的动态优化, 这个过程叫热身(warmup)。











你可能感兴趣的:(java,性能)