http://www.ukhec.ac.uk/events/javahec/pozo.pdf
作者:美国国家标准科技研究院
12项测试中,java获胜7项。
java:C=7:5
==============
结论:大多数情况下,java更快
http://www.idiom.com/~zilla/Computer/javaCbenchmark.html
作者:美国南加州大学计算机图形与Immersive技术实验室J.P.Lewis and Ulrich Neumann:
Java领先
=========
结论:大多数测试中java获胜
http://www.shudo.net/jit/perf/
FFT , SOR,Monte Carlo, Sparse matmult,LU 这5个Benchmark的结果:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Java/Sun JDK 1.4.2 Server VM = VS C/VC++ =2:3
VC 获胜
综合比分324:329=98.5% java性能是C++的98.5
在此,C++用SSE2编译。
由于在客户端VC++(windows)不能用SSE2编译,所有的java平台上只要打开-server选项就可以以此速度运行。
===========================
结论:在大多数的客户端程序上,C++程序跑不过java
java速度是GCC的122%
Java/Sun JDK 1.4.2 Server VM VS C/gcc (-O2) =3:2
java 获胜
综合比分=324:266=122%
Java性能是C/GCC的122%
所以在linux上,java更快
===================
结论:在非windows上的大多数情况下,java更快
http://www.javaworld.com/javaworld/jw-02-1998/jw-02-jperf_p.html
中:
java和C++在以下方面打成平手
Integer division
Dead code
Dead code with Integer division
Floating-point division
Static method
Member method
Virtual member method
但java在以下方面的速度是C++的约3倍
Virtual member method with down cast and Run-Time Type Identification (RTTI)
=====================
结论:综合来说,java更快
http://www.kano.net/javabench/data
===============
结论:14项Benchmark中,Java获胜9项,C++5项
java以9:5战胜C++,而且其中很多项是以大比分领先:
Methord Call:近20倍
Object creation:4倍
Hash: 2倍半
word count:1倍半
Fibonacci:1倍半
:
http://www.tommti-systems.de/go.html?http://www.tommti-systems.de/main-Dateien/reviews/languages/benchmarks.html
以下由于第3个表格中java打开了优化选项,所以只有第三个项目中代表了java的真正速度
java:C++=9:5大比分
其中前两个表格中都用了java 1.5 alpha测试版,并且前两表格没打开-server参数。所以我们只看第三个表格(打开了-server优化参数)中的结果:
各种语言在各个方面中夺取的第一名:
java 1.4.2_03(得到9项第一):
double math
trig Math
IO
exception
hashmap
hashmaps
list
nested loop
string concat
C++ (MS 或 Intel,。得到5项第一。 MS compiler options: /Op /Oy /O2 /Oi /Og /Ot /EHsc /arch:SSE
# Intel(R) compiler options: /Op /Oy /Qpc80 /fast /G6 /Oi /Og /Ot /EHsc /arch:SSE)
int32 math
long 64 math
array
heapsort
matrix multiply
值得一提的是:
以上测试中,java在exception上的处理速度竟是C++的约18倍
====================
结论:java在打开-server选项后,取得了大多数测试的冠军
http://cpp.student.utwente.nl/benchmark/
结果:
14个Benchmark中
Java-server SUN JDK1.4.2以6比8负于Inter C++8.0
Java-server SUN JDK1.4.2以8比6战胜GCC-i686
Java-server SUN JDK1.4.2以7比7战胜GCC-i686
=================
结论:基本战平
但是在此测试中,作者说他“故意”限制了JVM的内存使用量,说这是为了和C++公平。这其实是很不公平的。
java打开-server的目的就是为了“用空间换时间”,在内存中将bytecode编译成更大但是更快的本地码,作者却限制内存使用,
就如同飞机与汽车比速度时给飞机和汽车同样数量的汽油一样,或者在限制飞机的飞行高度为5米以下一样(飞机在燃料不足或低空的情况下是不可能以最快的速度飞行了)
看似公平,实则极不公平,飞机就是靠大量的燃料来加速,不给燃料还比什么呢?
如果不限制内存使用量的话,相信java会更快
再来一个证据:
IBM’s research JVM (参考[38] )has demonstrated that Java can
outperform C and FORTRAN compilers even for numerical calculations.
IBM研究的JVM已经证明了Java即使在数学运算中性能也超过C和Fortran。
(参考[38]:作者:S. M. José Moreira, Manish Gupta,
论文: "A Comparison of Three Approaches to
Language, Compiler, and Library Support for Multidimensional Arrays in
Java,"
presented at Joint ACM Java Grande - ISCOPE 2001 Conference,
Stanford University, 2001.
2001年ISCOPE大会,2001年在斯坦福大学召开 )
http://aspen.ucs.indiana.edu/gce/C536javacog/c536featuresOfCoG.pdf
我们看到对于足够大的数据数组中,Java RMI性能优于HPC++、Nexus RMI/HPC++和Nexus RMI。。。。。
更惊人的是:在使用JIT后,Java RMI性能优于HPC++
We see that Java RMI outperforms HPC++, Nexus RMI/HPC++, and Nexus RMI
for suciently large data arrays. Since data is pipelined in Java RMI, but not in Nexus RMI, this result
is reasonable. What is more surprising is that when the JIT is enabled, Java RMI is able to outperform
HPC++.
作者:
Fabian Breg, Shridhar Diwan, Juan Villacis,
Jayashree Balasubramanian, Esra Akman, Dennis Gannon
Indiana大学计算机科学系
Department of Computer Science
Indiana University
http://www.cs.ucsb.edu/conferences/java98/papers/rmi.pdf
另一个证据:
题目:
DEVELOPMENT OF A JAVA-BASED METEOROLOGICAL WORKSTATION BY GERMAN
METEOROLOGICAL SERVICE AND PARTNERS
德国气象服务与伙伴用Java开发气象工作站
German Weather Service DWD and German
Military Geophysical Service decided two years ago
to develop a new meteorological workstation from
scratch
德国气象服务局(DWD)与德国军事地球物理服务局决定开发一套全新的气象工作站
.....
The performance issue was evaluated before in a pilot project and is
analysed during all phases of development. It
turned out that this is not a problem, however many
things must be considered in the design and during
coding. Even numerical calculatuions are now in
pure Java since we found they can outperform C++
when carefully optimized.
性能问题在一个导航项目开发之前进行了评估。结果是:性能不是问题,但在编码过程中要考虑很多事情。
我们发现:在优化过后,纯Java甚至连数学计算的性能也超过了C++
http://ams.confex.com/ams/pdfpapers/52402.pdf
再一个证据:
题目:<<Is Java ready for computational science?>>
<<Java为科学计算做好准备了吗?>>
出处:Computer Science Dept., University of Karlsruhe, Germany
德国Karlsruhe大学计算机科学系
http://www.ubka.uni-karlsruhe.de/vvv/1998/informatik/27/27.pdf.gz
IBM的Java编译器以3:2战胜Fortran 90
Fortran90:Java的结果(单位为秒)
20:14
40:30
440:444
1080:1089
11790:11690
输了的两项是以不到!%的差距输的
而赢了的三项中有两项是以33%以上的差距获胜的
文章中的结论:
Conclusion:
Java will become a major language for computational science.
Java将成为科学计算中的主要语言
还有一个证据:
<<Real Java for Real Time>>
用在实时领域中的真正的java
The best JIT compilers today
generate code that come close to, or in some cases even
outperform, C++ code.
当今最好的JIT编译器可以产生性能接近甚至超过C++的代码
http://www.lucas.lth.se/publications/pub2002/nilsson_realjavaforrealtime.pdf
三位作者:
Anders Nilsson、Torbjorn Ekman、Klas Nilsson
瑞典Lund大学计算机科学系
Dept. of Computer Science
Lund University
SWEDEN
One More证据:
<<Java server benchmarks>>
其中对比了Servlet与C/C++写的CGI的各方面的性能
结论:
Conclusion:
Java servlets outperform CGI
Java Servlet的性能超过CGI
http://www.research.ibm.com/journal/sj/391/baylor.html
Servlet与CGI的性能对比:
结论:Servlet性能大大超过用C写的CGI:
基准所取得的结果是:
管理 — 为 64 个节点 SP 配置开发和成功地实现了 Java 和 WebSphere 安装和有效的管理过程。
稳定性 — 大规模 WebSphere 配置的稳定性通过烤机测试证明,在这种测试中,64 个节点的 WebSphere 系统,连续 48 小时运行,硬件或软件没有故障或错误。收集的连续 12 小时以上的性能数据表明稳定的吞吐量和 CPU 利用率。
可扩展性 — 在多达 64 个节点的配置中,在低量和高量工作负载的情况下,通过显示几乎线性的吞吐量(页面浏览量/秒/节点数)证明可扩展性。
性能 — Java 和 WebSphere 技术可以 300% 完成规定的目标。当前的 C/CGI 技术,在 CPU 利用率为 37% 时,大约提供 1.3 次页面浏览/秒/节点;CPU 利用率为 30-40% 时,IBM 的 Java 和 WebSphere 技术大约提供 3.8 次页面浏览/秒/节点。CPU 利用率更高时(> 90%),使用 64 个节点的配置能够在平均响应时间小于 2 秒的情况下提供 564 次页面浏览/秒/节点。假设是类似 2000 年 2 月 Schwab 所安装的配置,这种级别的性将能在“市场风暴”(在此期间有大量的请求)中支持 100,000 次并发的最终用户会话。
图表见原原文:
http://www-900.ibm.com/developerWorks/cn/wsdd/hvws/Schwab2001.shtml
评测报告:.NET的性能仍然远远落后于Java
.NET上同样代码的运行速度并不随运行次数的增加而提高,说明.NET CLR只是简单地进行了JIT编译。而在Hotspot Server上,不仅开始时的性能就有优势,而且速度还会不断提高
可以看到,随着运行次数的增加,Sun Hotspot Server不断将领先优势拉大,最后几乎比.NET 1.1快了一倍。.NET CLR始终无法将运行时间降低到8秒以下,而Sun Hotspot Server在多次运行之后则可以保持在5秒以下。因此,Cameron认为,对于大运算量的应用程序,Hotspot JVM比Microsoft CLR要快得多。
http://www.5d.cn/bbs/archivecontent.asp?id=792254
结论:Java与Fortran一样快
Java Pro 2002-2003中文精华合集[第1辑]
《Java够快吗》
http://act.it.sohu.com/book/chapter.php?id=51&volume=1&chapter=9
http://act.it.sohu.com/book/serialize.php?id=51
参考文献
J.E. Moreira, et. al.,"The Ninja Project,"
Communications of the ACM, 44(10), 102, (2001).
<<ACM通讯>>
Z. Budimli, K. Kennedy, and J. Piper,
"The Cost of Being Object-Oriented: A Preliminary Study,"
<<科学计算>>
Scientific Computing, 7(2), 87, 1999.
Zoran Budimli网站的: www.cs.rice.edu/~zoran
这是SUN对比J2EE与.net的Benchmark报告:
J2EE是.net速度的200%
Web Services Performance
http://java.sun.com/performance/reference/whitepapers/WS_Test-1_0.pdf
可能第三方的结论会更客观:
2004年11月4日最新数据!!!!!!
http://www.shudo.net/jit/perf/
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
FFT , SOR,Monte Carlo, Sparse matmult,LU 这5个Benchmark的结果:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
C#.net根本不值一提,0:5 输给java。
在综合比分上,java VS C#=324:240
java大比分领先
至于mono就差的更远了
java以324:163完胜mono
Swing GUI图形界面的性能:
在菜单、树形、文本框、table等几项上,jdk1.4.0比jdk1.3.1快40%到90%
Reflection的性能上,jdk1.4.0比jdk1.3.1快20倍!快20倍以上的还包括远程窗口调用
其它各项上,jdk1.4.0几乎都比jdk1.3.1快50%以上,“Java慢”也只是“历史”了
其它各方面的性能提升在此
http://java.sun.com/j2se/1.4/performance.guide.html
SUN的数据说明,JDK1.4.2在各方面的性能是jdk1.4.1的50%到380%
可以看到,几乎每次java的版本提高都带来性能的极大提升。所以如果你在某处看到一个benchmark说C++比java快,那就很可能是用的老版本的java。
但是C++近年似乎没什么速度的提升
http://java.sun.com/j2se/1.4.2/1.4.2_whitepaper.html
C++的速度是由C++编译器在程序员开发时编译出来的机器语言的优化程度决定的。
Java的速度是由Java的JIT和HotSpot编译器将java bytecode在运行时“即时”编译成针对本地CPU的优化的本地代码决定的。
比速度的实际就是在比:看C++编译器和java编译器谁能产生更优化的机器代码。
很明显,C++的编译器不如java的JIT和HotSpot编译器,因为JIT和HotSpot编译器能针对CPU指令集进行人优化、能在运行时根据使用频率对method进行内联和优化。而C++的静态编译器永远也做不到这些
两者有着本质的不同,但是有些Qpper用一辈子也无法理解这其中的差别,
他们只能抱着一个可怜的、没有任何根据的“C++比Java快”进棺材
某些Qpper们认为:根本就不存在Java,只存在C++,Java只是一个C++的程序。
但他们却在有意回避一个事实:
用C++编译器编译出来的本地代码才是C++程序
用Fortran编译器编译出来的本地代码才是Fortran程序
用Delphi编译器编译出来的本地代码才是Delphi程序
用C#编译器编译出来的本地代码才是C#程序
用VB.net编译器编译出来的本地代码才是VB.net程序
用Java编译器编译出来的本地代码才是Java程序
如果照某些Qpper的观点,那么世界上只有机器语言一种语言,因为其它语言归根结底都是机器语言程序,岂不荒唐?
而Java的本地代码是用Java的JIT和HotSpot编译器在程序运行时编译出来的,根本不是C++编译器编译出来的,所以java程序根本不是一个C++程序!
JIT和HotSpot编译器可以根据程序运行的CPU进行指令集进行优化,C++编译器可以吗?
JIT和HotSpot编译器可以根据程序运行时的profile对本地代码进行inline等优化,C++编译器可以吗?
JIT和HotSpot编译器可以根据程序运行时根据程序运行的情况进行更准确的分支预测,C++编译器可以吗?
大家可以去jre1.5.0的安装路径中去看看:
其中的jar文件共有50.5M,由于jar文件是压缩文件,并且bytecode的代码要比native code精简的多(前面已经说过了:一个构造方法在bytecode中只要一个指令,构造方法在C++的编译器却要11个指令。Java 一個 method call 只要一個machine code,但用 x86 相對需要 4 個),所以这50.5M的java程序完成的工作大约相当于200M以上的本地代码完成的工作。
而其中的.exe和.dll共有7.7M,本地代码在java运行环境中占的比例连5%都不到。
而这仅有的5%的“C++编译器产生的本地代码”(比如AWT.dll)在java程序运行时还要被JIT和HotSpot编译器重新编译成新的指令序列(例如:符合SSE2的指令集的指令),并根据运行时的profile 来内联到其它java编译器编译出来的native code中成为全新的NativeCode序列
所以C++编译器编译出来java本地库的机器代码序列在java运行的时候根本看不到,这些机器代码也被Java的JIT和HotSpot编译器重新编译并更改执行序列,这些“C++编译器编译出来的机器代码”已经变成了“Java编译器编译出来的机器代码”。最终,在CPU中执行的所有机器语言序列全部是由Java编译器产生的,与C++编译器没有一点关系
以下是Java比C++快的理论依据:
关于java如此快的原因:
有些东西确实只有JIT能做到:
比如中提到:
http://www.idiom.com/~zilla/Computer/javaCbenchmark.html
The compiler knows what processor it is running on, and can generate code specifically for that processor。
JIT编译器知道什么处理器正在运行,可以产生对应此处理器的优化代码。在这一点上,C++的静态编译器肯定做不到。
# The compiler knows what processor it is running on, and can generate code specifically for that processor. It knows whether (for example) the processor is a PIII or P4, if SSE2 is present, and how big the caches are. A pre-compiler on the other hand has to target the least-common-denominator processor, at least in the case of commercial software.
JIT编译器知道什么处理器正在运行,可以产生对应此处理器的优化代码。它知道是否处理器是P3或P4,是否SSE2存在,cache有多大。一个C++之类的预先编译器只能针对所有处理器的指令集的“最小公分母”进行编译,至少在商业软件中是这样的。
# Because the compiler knows which classes are actually loaded and being called, it knows which methods can be de-virtualized and inlined. (Remarkably, modern java compilers also know how to "uncompile" inlined calls in the case where an overriding method is loaded after the JIT compilation happens.)
因为JIT编译器知道哪些类被实际装载和调用,它知道哪些方法可以被“消虚拟化(de-virtualized)”
和内联(值得一提的是:当代java编译器还能知道在被覆盖的方法被装载的情况下,在JIT编译后进行“逆编译”内联的方法调用)
# A dynamic compiler may also get the branch prediction hints right more often than a static compiler.
动态编译器可以进行更多的分支预测
重点强调:
***
Java速度如此快的原因在此(来源:IBM东京研究院)
http://www.is.titech.ac.jp/ppl2004/proceedings/ishizaki_slides.pdf
***
其中有6大步优化,
每个大步中又有若干小步的优化,
共几十步的优化。
自己看吧,英语比较简单,只是内函比较难理解,
如果自己看不明白,别人解释也一样听不懂。
“很难相信Java居然能和C++一样快,甚至还能更快一些。”
据我自己的实践,这种说法确实成立。然而,我也发现许多关于速度的怀疑都来自一些早期的实现方式。由于这些方式并非特别有效,所以没有一个模型可供参考,不能解释Java速度快的原因。
我之所以想到速度,部分原因是由于C++模型。C++将自己的主要精力放在编译期间“静态”发生的所有事情上,所以程序的运行期版本非常短小和快速。C++也直接建立在C模型的基础上(主要为了向后兼容),但有时仅仅由于它在C中能按特定的方式工作,所以也是C++中最方便的一种方法。最重要的一种情况是C和C++对内存的管理方式,它是某些人觉得Java速度肯定慢的重要依据:在Java中,所有对象都必须在内存“堆”里创建。
而在C++中,对象是在堆栈中创建的。这样可达到更快的速度,因为当我们进入一个特定的作用域时,堆栈指针会向下移动一个单位,为那个作用域内创建的、以堆栈为基础的所有对象分配存储空间。而当我们离开作用域的时候(调用完毕所有局部构建器后),堆栈指针会向上移动一个单位。然而,在C++里创建 “内存堆”(Heap)对象通常会慢得多,因为它建立在C的内存堆基础上。这种内存堆实际是一个大的内存池,要求必须进行再循环(再生)。在C++里调用 delete以后,释放的内存会在堆里留下一个洞,所以再调用new的时候,存储分配机制必须进行某种形式的搜索,使对象的存储与堆内任何现成的洞相配,否则就会很快用光堆的存储空间。之所以内存堆的分配会在C++里对性能造成如此重大的性能影响,对可用内存的搜索正是一个重要的原因。所以创建基于堆栈的对象要快得多。
同样地,由于C++如此多的工作都在编译期间进行,所以必须考虑这方面的因素。但在Java的某些地方,事情的发生却要显得“动态”得多,它会改变模型。创建对象的时候,垃圾收集器的使用对于提高对象创建的速度产生了显著的影响。从表面上看,这种说法似乎有些奇怪——存储空间的释放会对存储空间的分配造成影响,但它正是JVM采取的重要手段之一,这意味着在Java中为堆对象分配存储空间几乎能达到与C++中在堆栈里创建存储空间一样快的速度。
可将C++的堆(以及更慢的Java堆)想象成一个庭院,每个对象都拥有自己的一块地皮。在以后的某个时间,这种“不动产”会被抛弃,而且必须再生。但在某些JVM里,Java堆的工作方式却是颇有不同的。它更象一条传送带:每次分配了一个新对象后,都会朝前移动。这意味着对象存储空间的分配可以达到非常快的速度。“堆指针”简单地向前移至处女地,所以它与C++的堆栈分配方式几乎是完全相同的(当然,在数据记录上会多花一些开销,但要比搜索存储空间快多了)。
现在,大家可能注意到了堆事实并非一条传送带。如按那种方式对待它,最终就要求进行大量的页交换(这对性能的发挥会产生巨大干扰),这样终究会用光内存,出现内存分页错误。所以这儿必须采取一个技巧,那就是著名的“垃圾收集器”。它在收集“垃圾”的同时,也负责压缩堆里的所有对象,将“堆指针”移至尽可能靠近传送带开头的地方,远离发生(内存)分页错误的地点。垃圾收集器会重新安排所有东西,使其成为一个高速、无限自由的堆模型,同时游刃有余地分配存储空间。
为真正掌握它的工作原理,我们首先需要理解不同垃圾收集器(GC)采取的工作方案。一种简单、但速度较慢的GC技术是引用计数。这意味着每个对象都包含了一个引用计数器。每当一个句柄同一个对象连接起来时,引用计数器就会增值。每当一个句柄超出自己的作用域,或者设为null时,引用计数就会减值。这样一来,只要程序处于运行状态,就需要连续进行引用计数管理——尽管这种管理本身的开销比较少。垃圾收集器会在整个对象列表中移动巡视,一旦它发现其中一个引用计数成为0,就释放它占据的存储空间。但这样做也有一个缺点:若对象相互之间进行循环引用,那么即使引用计数不是0,仍有可能属于应收掉的“垃圾”。为了找出这种自引用的组,要求垃圾收集器进行大量额外的工作。引用计数属于垃圾收集的一种类型,但它看起来并不适合在所有JVM方案中采用。
在速度更快的方案里,垃圾收集并不建立在引用计数的基础上。相反,它们基于这样一个原理:所有非死锁的对象最终都肯定能回溯至一个句柄,该句柄要么存在于堆栈中,要么存在于静态存储空间。这个回溯链可能经历了几层对象。所以,如果从堆栈和静态存储区域开始,并经历所有句柄,就能找出所有活动的对象。对于自己找到的每个句柄,都必须跟踪到它指向的那个对象,然后跟随那个对象中的所有句柄,“跟踪追击”到它们指向的对象……等等,直到遍历了从堆栈或静态存储区域中的句柄发起的整个链接网路为止。中途移经的每个对象都必须仍处于活动状态。注意对于那些特殊的自引用组,并不会出现前述的问题。由于它们根本找不到,所以会自动当作垃圾处理。
在这里阐述的方法中,JVM采用一种“自适应”的垃圾收集方案。对于它找到的那些活动对象,具体采取的操作取决于当前正在使用的是什么变体。其中一个变体是“停止和复制”。这意味着由于一些不久之后就会非常明显的原因,程序首先会停止运行(并非一种后台收集方案)。随后,已找到的每个活动对象都会从一个内存堆复制到另一个,留下所有的垃圾。除此以外,随着对象复制到新堆,它们会一个接一个地聚焦在一起。这样可使新堆显得更加紧凑(并使新的存储区域可以简单地抽离末尾,就象前面讲述的那样)。
当然,将一个对象从一处挪到另一处时,指向那个对象的所有句柄(引用)都必须改变。对于那些通过跟踪内存堆的对象而获得的句柄,以及那些静态存储区域,都可以立即改变。但在“遍历”过程中,还有可能遇到指向这个对象的其他句柄。一旦发现这个问题,就当即进行修正(可想象一个散列表将老地址映射成新地址)。
有两方面的问题使复制收集器显得效率低下。第一个问题是我们拥有两个堆,所有内存都在这两个独立的堆内来回移动,要求付出的管理量是实际需要的两倍。为解决这个问题,有些JVM根据需要分配内存堆,并将一个堆简单地复制到另一个。
第二个问题是复制。随着程序变得越来越“健壮”,它几乎不产生或产生很少的垃圾。尽管如此,一个副本收集器仍会将所有内存从一处复制到另一处,这显得非常浪费。为避免这个问题,有些JVM能侦测是否没有产生新的垃圾,并随即改换另一种方案(这便是“自适应”的缘由)。另一种方案叫作“标记和清除”, Sun公司的JVM一直采用的都是这种方案。对于常规性的应用,标记和清除显得非常慢,但一旦知道自己不产生垃圾,或者只产生很少的垃圾,它的速度就会非常快。
标记和清除采用相同的逻辑:从堆栈和静态存储区域开始,并跟踪所有句柄,寻找活动对象。然而,每次发现一个活动对象的时候,就会设置一个标记,为那个对象作上“记号”。但此时尚不收集那个对象。只有在标记过程结束,清除过程才正式开始。在清除过程中,死锁的对象会被释放然而,不会进行任何形式的复制,所以假若收集器决定压缩一个断续的内存堆,它通过移动周围的对象来实现。
“停止和复制”向我们表明这种类型的垃圾收集并不是在后台进行的;相反,一旦发生垃圾收集,程序就会停止运行。在Sun公司的文档库中,可发现许多地方都将垃圾收集定义成一种低优先级的后台进程,但它只是一种理论上的实验,实际根本不能工作。在实际应用中,Sun的垃圾收集器会在内存减少时运行。除此以外,“标记和清除”也要求程序停止运行。
正如早先指出的那样,在这里介绍的JVM中,内存是按大块分配的。若分配一个大块头对象,它会获得自己的内存块。严格的“停止和复制”要求在释放旧堆之前,将每个活动的对象从源堆复制到一个新堆,此时会涉及大量的内存转换工作。通过内存块,垃圾收集器通常可利用死块复制对象,就象它进行收集时那样。每个块都有一个生成计数,用于跟踪它是否依然“存活”。通常,只有自上次垃圾收集以来创建的块才会得到压缩;对于其他所有块,如果已从其他某些地方进行了引用,那么生成计数都会溢出。这是许多短期的、临时的对象经常遇到的情况。会周期性地进行一次完整清除工作——大块头的对象仍未复制(只是让它们的生成计数溢出),而那些包含了小对象的块会进行复制和压缩。JVM会监视垃圾收集器的效率,如果由于所有对象都属于长期对象,造成垃圾收集成为浪费时间的一个过程,就会切换到“标记和清除”方案。类似地,JVM会跟踪监视成功的“标记与清除”工作,若内存堆变得越来越“散乱”,就会换回“停止和复制”方案。“自定义”的说法就是从这种行为来的,我们将其最后总结为:“根据情况,自动转换停止和复制/标记和清除这两种模式”。
JVM还采用了其他许多加速方案。其中一个特别重要的涉及装载器以及JIT编译器。若必须装载一个类(通常是我们首次想创建那个类的一个对象时),会找到.class文件,并将那个类的字节码送入内存。此时,一个方法是用JIT编译所有代码,但这样做有两方面的缺点:它会花更多的时间,若与程序的运行时间综合考虑,编译时间还有可能更长;而且它增大了执行文件的长度(字节码比扩展过的JIT代码精简得多),这有可能造成内存页交换,从而显著放慢一个程序的执行速度。另一种替代办法是:除非确有必要,否则不经JIT编译。这样一来,那些根本不会执行的代码就可能永远得不到JIT的编译。
由于JVM对浏览器来说是外置的,大家可能希望在使用浏览器的时候从一些JVM的速度提高中获得好处。但非常不幸,JVM目前不能与不同的浏览器进行沟通。为发挥一种特定JVM的潜力,要么使用内建了那种JVM的浏览器,要么只有运行独立的Java应用程序。
http://pub.chinans-ls.com/~jx/java/chapter/appe.htm
下面的文章也说明了java编译器比C++静态编译器的优势:
浅谈JAVA VM 发展(作者:黄敬群)
节选:
c++ 的 virtaul method calling:
不算 argument 4 個指令
c 的一般 call:
不算 argument 2 個指令
java 的 virtual call:
不算 argument 2 個指令.
c++ 的 constructor:
11 個指令
C++ destructor:
8 個指令
java 的 constructor:
2 個指令
由此可發現,對動態配置物件的操作而言,Java 一個 method call 只要一個
machine code,但用 x86 相對需要 4 個,這是 Java 在指令集層面直接支援所致。
Joakim Dahlstedt 的來頭可不小,是 JRockit VM 的主要設計者,現任 BEA System
裡頭 Java Runtime Group 的技術長,這篇文章並非老王賣瓜,相反的,Joakim 要
我們明瞭,評斷 Java VM "benchmark" (效能評比) 的方式需要調整,主要是基於以
下幾項:
1.一般的 benchmark 比較僅僅是 micro-benchmark level,不能推廣到 system-
level。
2.軟體產業開發方式發生了很大的變化,大量使用 class library、framework、
Application server,乃至 Web services。援引舊的 benchmark 僅能針對其中
個別 software stack,卻不能進行 system-level 的全面分析,如此的衡量本
身就有問題。
3.Java VM 本身的 Dynamic Optimization,可依據最真實的 profiling 數據調整
元件,使其針對效能進行重組。
4.最後,新的 CPU 架構,像是 Intel EPIC 可能更適合於 Dynamic Optimization
,而非傳統 static compiler。在 EPIC 的架構下,compiler 對效能的影響相
當大,compiler 有責任選擇平行處理的指令集,而非像傳統 Pentium 引入自動
的 out-of-order 亂序執行支援,這意味著,軟體支配了 EPIC 的效能,這對
static compiler 是不利的,因為僅能從 bytecode 取得固定的統計與符號,卻
未能對真實 profiling 作規劃。
Joakim 的結論給予我們很好的啟發:
"In conclusion, ..., but I hope I've convinced you that using runtime
systems like Sun Microsystems' HotSpot or BEA WebLogic JRockit Java
is not slow or inefficient, and that the performance of a large-scale
system built with Java may be superior to that of the same system
built using C."
IBM 的 JDK 曾經一度是最佳性能的 Java VM,但 Sun JDK 1. 4的性能已與 IBM JDK
1.3 相當,其 server 版採用更積極的 JIT 和 GC (Garbage Collection) 技術,尤
其是針對 SMP 的應用提供最適合該硬體架構與多執行緒處理的 GC。
在另一方面,IBM 將內部的 Jalapeno JVM 研究計畫 [6] 的成果以 Open Source 授
權釋出的 JikesRVM 計畫 [7],提供一個測試許多 JIT 與 GC 等技術與演算法的參
考實作平台。IBM Rearch 將 JikesRVM 視作 JIT 方面的一個 research infrastru-
cture,在網站上羅列了相當豐富的論文可參考 。筆者參考了 JikesRVM 的研究方向
,認為 JIT Compiler 發展趨勢可列出以下:
1. 類似於 Hotspot [8] 整合 base interpreter、profiling,以及 adaptive
compiler 三種技術的途徑。
2. 動態 profiling 技術,從簡單的 counter-based algorithm 進化到
performance monitoring event。
3. 應用靜態 compiler 中更 aggressive 的編譯技術 (對 JIT Compiler 而言,
編譯時間已是次要問題),產生最佳化原生碼 (native code)。
4. 應用 inter-procedural 分析,例如 escape analysis 可以 remove synchro-
nization 和實施 stack allocation。
5. 參考 Runtime 所獲得的資訊 (主要是 metadata 和 dynamic allocation),產
生最佳化原生碼。
[6] http://www.research.ibm.com/jalapeno/
[7] http://www-124.ibm.com/developerworks/oss/jikesrvm/
[8] http://java.sun.com/products/hotspot/
接著,我們來看看 Sun 的 Hotspot 技術。提到 Hotspot 不能不提 Henry Massalin
這位先驅,Henry 的博士論文在 Java Glossary 的 HotSpot 的解釋 [9] 中被譽為
"the mother of all self-modifying code",同時,HotSpot 也是 "builds heavily
on work done in a PhD thesis by Henry Massalin"。
[9] http://mindprod.com/jgloss/hotspot.html
Java 最初的實作是透過直譯器 (interpreter),但這並非意味 Java 一定被解譯執
行的。早期的 Java VM 的確是逐一指令的解譯,因此效能極不理想,於是引入了
JIT 等關鍵技術,而 HotSpot 可說下個世代的 JIT。據 Sun 官方文獻指出,使用
HotSpot 的 Java VM 在 Runtime 時期已經很難分辨 Java bytecode 是否被 JVM 解
釋執行了,因為 HotSpot 實際上是把 Java 的bytecode 編譯成原生碼執行了。
實際上,在 HotSpot 設計中,有兩個技術是相當重要的,也就是所謂 dynamic
compilation 和 profiling。HotSpot 對 bytecode 的編譯,並非在程式執行前預先
編譯的 (這種傳統的方式相對而言稱為 static compilation),相反的,是在程式執
行過程中,以 HotSpot 內建的編譯器做動態的編譯,早期的 JIT(Just In Time) 其
實也是如此的概念,不過沒有 HotSpot 來得全面性。
那麼,HotSpot 是如何動態編譯 Java bytecode 呢﹖HotSpot 採用一個高度彈性的
設計,內部維護了 Profile Monitor,專門監視程式執行中,判斷程式片段中使用的
頻率高寡,並依據對性能影響的程度,交付於若干演算法處理。HotSpot 對於那些對
程式執行時效率影響較大的程式碼片段,特稱為 hot spot (特別以小寫書寫,避免
與 HotSpot 混淆),HotSpot 會這些片段動態編譯成原生碼,同時,會依據之前
profiling 的結果,對這些原生碼進行最佳化,從而提高執行效率。相反的,如果執
行頻率較低的程式碼片段,HotSpot 就沒必要花時間做動態編譯,只需要以直譯器執
行即可。
整體而論,在 HotSpot 下,Java bytecode 是以解譯的方式被載入到 JavaVM 中,
但是 HotSpot 立刻會依據某些程式碼片段執行的狀態,獲知其中對效能影響最大的
部分,透過動態編譯與最佳化處理,建立原生碼,於是,接下來的執行過程中就可獲
得相當程度的效能提昇。我們可以得知,HotSpot 對 bytecode 的執行有以下三種處
理方式:
- 直譯 (不動態編譯)
- 部分動態編譯
- 依據 profiling 結果做動態編譯與最佳化處理
至於程式哪部分只做直譯、部分動態編譯,以及哪部分做何種最佳化處理,則全程由
Profile Monitor 決定。
採用 dynamic compilation 會比傳統的 static compilation 帶來什麼優點呢?撇
開跨平台需求不論,dynamic compilation 在許多方面優越於傳統的途徑,特別是
profiling 的能力。過去 static compilation 很難精準的預知程式執行中究竟何處
才是最需要最佳化處理的部分,僅能透過內建的 template 來建構 micro-level 的
最佳化程式碼。相反的,dynamic compilation 可獲悉最真實的 profiling 表現,
依據需求動態調整,這在日後處理器逐漸軟體化的發展趨勢而言,更顯得重要,因為
過去硬體的工作逐漸移轉到軟體來做,compiler optimization 的責任就格外沈重了
,像是前述 Intel EPIC 架構。
另一個典型的例子,稱為 method inlining。無論是在 C++ 或是 Java 中,呼叫
(invoke) method 是很消耗系統資源的,系統必須維護堆疊操作,以符合預期的
calling convention。於是,有人提出把原本需要做 method invocation 的處理,
改用 inline 的方式,「嵌入」到原本呼叫的地方,如此一來,就只是單純的循序執
行,也不會有堆疊操作。但是,method inlining 的方式對 C++ 與 Java 一類的物
件導向語言來說,編譯器很難有很好的實作方式,因為需要兼顧物件導向的特徵,尤
其是維持 dynamic binding 性質。static compiler 其實可以把 C++/Java 中屬性
為 private 與 static 的 method 做 method inlinng,但是一旦有 overridden 或
dynamic binding 時,static compiler 無法得知實際上的執行狀況,就會保守的不
予實施 inlining。這個難題,恰好可被 HotSpot 一類 dynamic compilation 的設
計迎刃而解,因為 Profiling Monitor 對 method invocation 的狀況可以掌握,當
然可精準的得知 Runtime 中,RTTI (Run-Time Type Identification) 可協助
HotSpot 處理 method inlining,因此,在 server-side 應用這種重複進行某項目
執行時,可獲得頗大的效能提昇。
Sun 的文獻也指出,在某些狀況下,Java 的應用程式甚至可比傳統的 C 程式還快。
以目前發展而言,JIT Compiler 的成本主要在於 profiling 與 dynamic compila-
tion 兩項。理想而言,這兩項成本應該視為 constantant time,於是許多研究論文
相繼提出,以作為效能改進。特製為 JIT Compiler 使用、精度不需很高的 Java
Runtime profiling 可參考〈A Portable Samplling-Based Profiler for Java
Virtual Machines〉[10],該論文提出採用 sampling 的方式來做近似分析。而至於
Dynamic compilation 的 overhead 可以用漸進式最佳化的方式來減少,在 Sun 的
HotSpot 白皮書已有詳盡的介紹。
全文
http://dev.csdn.net/develop/article/54/54057.shtm
本文地址:http://com.8s8s.com/it/it23441.htm