对于吃货出身又需要保持体重的我,出门一定要带男票,因为这样就可以把见到的好吃的都买给他吃,就当是自己吃了[汗]。偶尔做梦还是会梦到自己一个角落里偷吃东西,听到有脚步声,抬起头,大哭起来:“我饿了。” 然后就这样一直哭醒。
作为程序媛,对于程序的爱好,必须高于对吃的痴迷。虽然这对于吃货而言很难,但相比减肥都不是难事,因为我已经是一名腻害的程序媛了。
我在做项目的时候,将自己所有涉及到的项目编译和运行JAVA版本都升级到了1.7。告诉其他人说:我们要好好利用JVM升级带来的免费性能福利。有人跟我说:其实服务器上是1.7啊,只是jenkins部署的时候用的1.6而已。我叹了口气,这个问题,要我详细讲的话,我可以讲上两天,以下是精简的部分。
大家都知道,JAVA官网下载下来的包安装后里面有两个文件夹,一个是JDK(Java Development Kit),这是Java语言的软件开发工具包(SDK)。另一个是JRE(Java Runtime Environment),就是JAVA的运行时环境。如果JDK的文件夹是jdk1.7.XXX,那么相应的JRE的文件夹就是jre7。所以大家提到java版本的时候,有人习惯说java1.7,但还有人习惯说java7。
JDK的文件夹里包含了JRE。所以正常服务器上只安装JDK。Windows™里会看到JRE文件夹里大多数的文件扩展名是DLL(Dynamic Link Library),这是动态链接库文件。在Windows™中,许多应用程序并不是一个完整的可执行文件,而是被分割成一些相对独立的DLL。一个应用程序可以使用多个DLL,一个DLL也可能被不同的应用程序使用。而Linux®里的JRE文件夹下因为是完整的可执行文件,所以文件数会少一些。而且Linux®一切皆文件,所以是没有扩展名的。
看了上面的两段,累了吧。我累的时候喜欢坐在路边长椅上一边喝酸奶(健康不长胖,耶!)一边看着如花的姑娘们从眼前走过。丰满的姑娘好似茶花,圆润可爱;浓妆的姑娘好似曼陀罗,美丽却有毒,让人想靠近又不敢靠太近;还有一种会让人联想到芍药的姑娘,秀丽的气质里藏不住更美的内在……
言归正传啦,jenkins部署的时候一般包含两个步骤:一个是构建(build),还有一个是部署(deploy)。部署就是扔到实际运行服务器上。构建是先将所需要的资源按照指定的链接下载到本地,然后用JDK编译。那么用低版本的JDK进行编译有什么影响呢?
因为线上是能运行的,我们就刨去兼容问题不谈,只谈性能。
JAVA的编译期包括前端编译(将.java文件变成.class文件,.class文件就是字节码文件啦);提前编译(AOT ,ahead of time compiler,直接将.java文件编译成本地机器代码);运行期编译(JIT, just in time compiler,把字节码转变成机器码)。而jenkins部署的时候只做了前端编译。
我们常用的前端编译器:部署的时候用sun的javac,BTW(by the way),javac是纯java写的,有兴趣的童鞋可以研究一下源码。开发的时候,由于笔者本人是eclipse的铁粉儿,用的是 eclipse JDT中的增量式编译器(ECJ, Eclipse Compiler for Java)。其实javac这类编译器对代码的运行效率击鼓没有任何优化(在JDK1.3之后,javac的-O优化参数就不再有意义了)。虚拟机设计团队把对性能的优化集中到了即时编译中,这样可以让那些不是javac产生的class文件也同样能享受到编译器优化带来的好处。
好吧,人家说的是对的,jenkins用1.6编译对性能没有多大影响。毕竟我大乐视人才济济,不是随便一句话就可以忽悠人家的。但是javac做了许多针对编码过程的优化措施来改善程序员的编码风格和提高编码效率。相当多新生的java语法特性,都是靠编译器的语法糖(Syntactic Sugar,对功能没有影响,更方便程序员使用的语法)来实现,而不是依赖虚拟机的底层改进来支持,可以说,java中即时编译在运行期的优化过程对于程序运行来说更重要,而前端编译器在编译期的优化过程对于程序编码来说关系更加密切。
JAVA最初是通过解释器进行解释执行的,当虚拟机发现某人方法或代码块的运行特别频繁,就会把这些代码认定为“热点代码”而将它们编译成本地机器码,并进行各种层次的优化,完成这个任务的编译器就是即时编译器。
即时编译器也有不同的版本,比较常用的是HotSpot虚拟器的C1(clientcompiler用来获取更高的编译速度),C2(servercompiler获取更好的编译质量)编译器。它是解释器和编译器并存,保留解释器的原因是,加快启动时间,立即执行,当运行环境中内存资源限制较大时,解释器可以节约内存,解释器还可以作为激进优化的编译器的“逃生门”(称为逆优化Deoptimization),而编译器能把越来越多的代码编译成遍地代码后,获取更高的执行效率。HotSpot会根据自身版本和宿主机器的性能自动选择C1还是C2,用户也可以使用-client或者-server来自行决定。用户还可以使用-Xint强制虚拟机使用解释模式,也可以使用-Xcomp强制编译模式。
JDK设计团队几乎把代码的所有优化措施都集中在了即时编译器上,所以一般来说即时编译器产生的本地代码会比Javac产生的字节码更优秀。常用的优化技术有:公共子表达式消除,数组边界检查消除,方法内联,逃逸分析。有兴趣的童鞋可以自己研究一下,面试时很加分[胜利].
最开始还提到了提前编译。提前编译与即时编译的基本思想是相同的。在程序执行前生成Java方法的本地代码,以便在程序运行时直接使用本地代码。目的在于避免即时编译器的运行时性能消耗或内存消耗,或者避免解释程序的早期性能开销。
有人喜欢拿JAVA和C/C++对比。早期的Java运行时所提供的性能级别远低于C和C++。但是即时编译的出现使得现代的JIT编译器可以产生于C或C++静态编译相当的应用程序性能。但是,JAVA在输出本地代码上还是有一些劣势。首先,因为即时编译器运行占用的是用户程序的运行时间,具有很大的时间压力,优化手段也严重受制于编译成本。其次,Java语言是动态的类型安全语言,需要由虚拟机来确保程序不会违反语言的寓意或者访问非结构化内存。第三,Java语言中虽然没有virtual关键字,但是使用虚方法(多态方法)的频率却远远大于C和C++语言。这意味着运行时对方法接受者进行多态选择的频率要远远大于C和C++语言,这加大的优化难度。第四,Java语言是可以动态扩展的语言,运行时加载新的类可能改变程序类型的继承关系,编译器不得不时刻注意并随着类型的变化而在运行时撤销或重新进行一些优化。第五,Java语言中对象的内存分配都是在堆上进行的,只有方法中的局部变量才能在栈上分配。但是Java语言的这些性能上的劣势都是为了换取开发效率上的优势,动态安全,动态扩展,垃圾回收这些特性都为Java语言的开发效率做出了很大的贡献。
小知识:聪明的你已经注意到了文章中Windows™和Linux®的角标。™是trade mark(商业标记)的缩写。表示此商标正在受理注册中,还没有取得商标证。®表示商标已经注册并取得了商标证。
建议:大家在用英文简写的时候把简写表示的意思都解释一下,因为一个简写有很多意思,读者们需要用很长的反射弧才能想明白
关于作者
晓静,20岁时毕业于东北大学计算机系。在毕业后的第一家公司由于出众的语言天赋,在1年的时间里从零开始学日语并以超高分通过了国际日语一级考试,担当两年日语翻译的工作。后就职于人人网,转型做互联网开发。中国科学院心理学研究生。有近百个技术发明专利,创业公司合伙人。有日本东京,美国硅谷技术支持经验。目前任美团点评技术专家(欢迎关注静儿的个人技术公众号:编程一生 ),心法文章可参考我的《自动化管理之新人培养》
技术交流可关注我的github: https://github.com/xiexiaojing
共同学习,写下你的评论
评论加载中...
作者其他优质文章